Protocol-based SEO auditing framework for Django models with automatic rule discovery
Project description
Django SEO Audit
A production-ready Django package for comprehensive SEO auditing of models using Python protocols and automatic rule discovery.
Features
- Protocol-Based Architecture: Uses Python 3.12+ protocols for clean, loosely-coupled design
- Automatic Rule Discovery: Rules auto-register when defined - no manual registration needed
- Auto-Discovery Management Command: Automatically finds and audits any model using
SEOAuditableMixin - Comprehensive Rule Set: 18 built-in rules across 4 categories:
- Core SEO: Title length, meta descriptions, keywords, H1 tags
- Social Media: OpenGraph and Twitter Card optimization
- Content Quality: Content depth, introduction, resources
- Technical SEO: Canonical URLs, robots directives, structured data
- Django Model Mixin: Drop-in mixin with intelligent fallbacks for common field names
- Extensible: Easy to add custom rules for your specific SEO requirements
- Beautiful CLI Output: Color-coded results with emoji indicators and actionable suggestions
Installation
From PyPI (when published)
pip install django-seo-audit
From Source (Development)
# In your workspace directory
git clone https://github.com/directory-platform/django-seo-audit.git
cd django-seo-audit
pip install -e .
UV Workspace (Recommended for development)
Add to your workspace pyproject.toml:
[tool.uv.workspace]
members = [
"your-project",
"django-seo-audit",
]
Then run:
uv sync
Quick Start
1. Add to Django Settings
# settings.py
INSTALLED_APPS = [
# ...
"django_seo_audit",
]
2. Add Mixin to Your Models
# models.py
from django.db import models
from django_seo_audit import SEOAuditableMixin
class Article(SEOAuditableMixin, models.Model):
name = models.CharField(max_length=200)
seo_title = models.CharField(max_length=60, blank=True)
meta_description = models.TextField(max_length=160, blank=True)
focus_keyphrase = models.CharField(max_length=100, blank=True)
# ... other fields
The mixin provides intelligent fallbacks:
get_seo_title()→seo_titleornameget_meta_description()→meta_descriptionorshort_descriptionget_h1_tag()→h1_tagorseo_titleorname
3. Run SEO Audit
# List all auditable models
python manage.py seo_audit --list-models
# Audit a specific object
python manage.py seo_audit --model blog.Article --slug my-article
# Audit with verbose output and specific categories
python manage.py seo_audit --model blog.Article --slug my-article --verbose --category core_seo
4. Programmatic Usage
from django_seo_audit import SEOAuditor
# Audit any object
article = Article.objects.get(slug="my-article")
auditor = SEOAuditor()
result = auditor.audit_object(article)
# Check results
print(f"SEO Score: {result.overall_score}/10")
print(f"Grade: {result.get_health_grade()}")
print(f"Passing: {result.is_passing()}")
# Get issues by severity
critical_issues = result.get_critical_issues()
warnings = result.get_warnings()
successes = result.get_successes()
Protocols
Django SEO Audit uses Python protocols to define contracts for auditable objects. Your models don't need to inherit from anything - just implement the methods you need.
BasicSEOAuditable
Core SEO methods every model should implement:
from django_seo_audit import BasicSEOAuditable
def get_seo_title(self) -> str: ...
def get_meta_description(self) -> str: ...
def get_canonical_url(self) -> str: ...
def get_focus_keyphrase(self) -> str: ...
def get_secondary_keywords(self) -> str: ...
def get_h1_tag(self) -> str: ...
SocialMediaAuditable
For social sharing optimization:
from django_seo_audit import SocialMediaAuditable
def get_og_title(self) -> str: ...
def get_og_description(self) -> str: ...
def get_og_image_url(self) -> str | None: ...
def get_twitter_title(self) -> str: ...
def get_twitter_description(self) -> str: ...
def get_twitter_image_url(self) -> str | None: ...
ContentAuditable
For content quality assessment:
from django_seo_audit import ContentAuditable
def has_detailed_content(self) -> bool: ...
def get_content_word_count(self) -> int: ...
def get_content_sections_count(self) -> int: ...
def has_introduction_content(self) -> bool: ...
def get_resource_count(self) -> int: ...
TechnicalSEOAuditable
For technical SEO features:
from django_seo_audit import TechnicalSEOAuditable
def get_robots_directive(self) -> str: ...
def get_schema_type(self) -> str: ...
def get_schema_data(self) -> dict | None: ...
def get_breadcrumb_title(self) -> str: ...
def has_custom_canonical_url(self) -> bool: ...
Creating Custom Rules
Creating custom SEO rules is straightforward:
# rules/custom_rules.py
from django_seo_audit import SEORule, SEOResult, SEOStatus
from django_seo_audit.protocols import BasicSEOAuditable
class CustomKeywordDensityRule(SEORule):
"""Check keyword density in content."""
name = "Keyword Density"
description = "Ensures focus keyword appears with optimal density"
category = "custom"
weight = 3 # 1-5 importance scale
def check(self, obj: BasicSEOAuditable) -> SEOResult:
keyword = obj.get_focus_keyphrase()
# Your logic here
return SEOResult(
status=SEOStatus.GOOD,
message="Keyword density optimal",
score=10,
)
Rules are automatically discovered and registered when imported!
Management Command Reference
List Auditable Models
python manage.py seo_audit --list-models
Shows all models using SEOAuditableMixin with object counts.
Audit by Slug
python manage.py seo_audit --model app_label.ModelName --slug object-slug
Audit by Primary Key
python manage.py seo_audit --model app_label.ModelName --pk 42
Filter by Category
python manage.py seo_audit --model blog.Article --slug test \
--category core_seo \
--category social_media
Available categories: core_seo, social_media, content, technical_seo
Verbose Output
python manage.py seo_audit --model blog.Article --slug test --verbose
Shows detailed explanations and actionable suggestions for improvements.
Built-in Rules
Core SEO (5 rules)
- SEO Title Length: Optimal 50-60 characters
- Meta Description Length: Optimal 150-160 characters
- Focus Keyphrase: Presence in title and description
- H1 Tag Optimization: Proper H1 configuration
- Secondary Keywords: 3-7 keyword variety
Social Media (5 rules)
- OpenGraph Image: 1200x630px image configuration
- OpenGraph Title: Up to 95 characters
- OpenGraph Description: Up to 200 characters
- Twitter Card Image: Twitter-optimized imagery
- Twitter Card Title: Up to 70 characters
Content (4 rules)
- Detailed Content: Comprehensive content sections
- Introduction Content: Engaging introduction
- Supporting Resources: Videos, articles, tools
- Content Depth: 500+ words across sections
Technical SEO (4 rules)
- Canonical URL: Duplicate content prevention
- Robots Directive: Proper indexing configuration
- Structured Data: Schema.org markup
- Breadcrumb Optimization: Navigation clarity
Architecture
- Protocol-Based: Uses Python 3.12+
Protocolfor structural subtyping - Auto-Registration: Rules register via
__init_subclass__metaclass magic - Immutable Results:
SEOResultuses frozen dataclass for thread-safety - Graceful Degradation: Failed rules don't break the entire audit
- Zero Config: Works out of the box with sensible defaults
Requirements
- Python 3.12+
- Django 4.2+
Development
Setup
# Clone repository
git clone https://github.com/heysamtexas/django-seo-audit.git
cd django-seo-audit
# Install with dev dependencies
pip install -e ".[dev]"
# or with uv
uv sync --extra dev
Running Tests
The package includes a comprehensive test suite with 207 tests:
# Run all tests
make test
# or
PYTHONPATH=. uv run python tests/manage.py test
# Run with verbose output
make test-verbose
# Run specific test module
PYTHONPATH=. uv run python tests/manage.py test tests.test_rules
Code Quality
# Run linting
make lint
# or
ruff check src/ tests/
# Auto-format code
make format
# or
ruff format src/ tests/
# Type checking
make typecheck
# or
mypy src/
# Run all checks
make check
Example Models
See tests/example_app/models.py for living documentation:
- BlogPost: Full SEO implementation
- Product: Minimal implementation with fallbacks
- Page: Custom protocol without mixin
Releases and Publishing
This package uses GitHub Actions to automatically publish to PyPI when a new release is created.
Creating a Release
-
Update version in
pyproject.toml:[project] version = "0.1.1" # Bump version
-
Commit version bump:
git add pyproject.toml git commit -m "Bump version to 0.1.1" git push origin master
-
Create GitHub release:
# Using GitHub CLI gh release create v0.1.1 --title "Release v0.1.1" --notes "Release notes here" # Or use GitHub web interface # Navigate to: https://github.com/heysamtexas/django-seo-audit/releases/new
-
GitHub Actions will automatically:
- Verify version matches tag
- Run tests
- Build package
- Publish to PyPI
PyPI Trusted Publisher Setup (First Release Only)
Before creating your first release, configure PyPI Trusted Publisher:
-
Go to PyPI: https://pypi.org/manage/account/publishing/
-
Add a new pending publisher:
- PyPI Project Name:
django-seo-audit - Owner:
heysamtexas - Repository name:
django-seo-audit - Workflow name:
publish.yml - Environment name: (leave blank)
- PyPI Project Name:
-
Create first release - PyPI will create the project automatically
No API tokens needed! Trusted Publishers are more secure and maintenance-free.
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-rule) - Commit your changes (
git commit -m 'Add amazing SEO rule') - Push to the branch (
git push origin feature/amazing-rule) - Open a Pull Request
License
MIT License - see LICENSE file for details.
Credits
Built by the Directory Platform team as part of the directory ecosystem.
Links
- Documentation: [Coming soon]
- GitHub: https://github.com/heysamtexas/django-seo-audit
- Issues: https://github.com/heysamtexas/django-seo-audit/issues
- PyPI: https://pypi.org/project/django-seo-audit/ (after first release)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_seo_audit-0.2.0.tar.gz.
File metadata
- Download URL: django_seo_audit-0.2.0.tar.gz
- Upload date:
- Size: 51.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79f7bd377b7d7775eab51069e07cc9c72def8cb361e10465604d75d5d9ce3655
|
|
| MD5 |
298cae7bbcc179966dd8a6663ea6619b
|
|
| BLAKE2b-256 |
2963a9cb396fd5e2ecdc6f84a2bbeb36cbb04d327d2ab9e8d05bd7316d79fe31
|
Provenance
The following attestation bundles were made for django_seo_audit-0.2.0.tar.gz:
Publisher:
publish.yml on heysamtexas/django-seo-audit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_seo_audit-0.2.0.tar.gz -
Subject digest:
79f7bd377b7d7775eab51069e07cc9c72def8cb361e10465604d75d5d9ce3655 - Sigstore transparency entry: 601068697
- Sigstore integration time:
-
Permalink:
heysamtexas/django-seo-audit@ba9b5ca1067b761a2883a844520ef2becd302f4d -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/heysamtexas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ba9b5ca1067b761a2883a844520ef2becd302f4d -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_seo_audit-0.2.0-py3-none-any.whl.
File metadata
- Download URL: django_seo_audit-0.2.0-py3-none-any.whl
- Upload date:
- Size: 27.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d74fe1670ac678c2638cc4cc1cf31499d3d20962509bd6179ca56e8a5541b6e
|
|
| MD5 |
dbf89fe51e213f7bd10f28b0e39aeacd
|
|
| BLAKE2b-256 |
fea94710c183413bee225ffeba77aa5014b0fd57f5b92c6e49d20d96cb8a2855
|
Provenance
The following attestation bundles were made for django_seo_audit-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on heysamtexas/django-seo-audit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_seo_audit-0.2.0-py3-none-any.whl -
Subject digest:
5d74fe1670ac678c2638cc4cc1cf31499d3d20962509bd6179ca56e8a5541b6e - Sigstore transparency entry: 601068698
- Sigstore integration time:
-
Permalink:
heysamtexas/django-seo-audit@ba9b5ca1067b761a2883a844520ef2becd302f4d -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/heysamtexas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ba9b5ca1067b761a2883a844520ef2becd302f4d -
Trigger Event:
release
-
Statement type: