django-models
Skill177 starsChanged 10 months agoMITView on GitHub
Django model design patterns emphasizing fat models/thin views, QuerySet optimization, and domain logic encapsulation. Use when designing models, optimizing queries, implementing business logic, or working with the ORM.
We split this skill into the rules it gives an agent
16 rules. About 0 are common, 6 are distinctive, and 10 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 18
Use TextChoices/IntegerChoices for status fields and enums
- Very specific: line 19
Add get_absolute_url() for canonical object URLs
- Distinctive: line 22
Add database indexes for frequently filtered/sorted fields
- Distinctive: line 23
Use abstract base models for shared fields (timestamps, soft deletes, etc.)
- Very specific: line 26
Use blank=True, default="" for optional text fields (avoid null)
- Very specific: line 27
Use null=True, blank=True for optional foreign keys
- Distinctive: line 36
Use properties for computed read-only values
- Distinctive: line 65
select_related(): Use for ForeignKey and OneToOneField (creates SQL JOIN)
- Distinctive: line 66
prefetch_related(): Use for ManyToManyField and reverse ForeignKeys (separate query + Python join)
- Very specific: line 72
Use .exists() instead of if queryset: or if len(queryset):
- Very specific: line 73
Use .count() instead of len(queryset.all())
- Very specific: line 79
Use F() expressions for database-level updates (views=F('views') + 1)
- Distinctive: line 106
Rule of thumb: If you control both the trigger and the reaction, don't use a signal.
- Very specific: line 111
Run makemigrations after model changes
- Very specific: line 113
Run migrate to apply migrations
- Very specific: line 117
Create with makemigrations --empty app_name.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Django Model Patterns
Core Philosophy: Fat Models, Thin Views
Business logic belongs in models and managers, not views. Views orchestrate workflows; models implement domain behavior. This principle creates testable, reusable code that stays maintainable as complexity grows.
Good: Model methods handle business rules, state transitions, validation Bad: Views contain if/else logic for domain rules, calculate derived values
Model Design
Structure Your Models Around Domain Concepts
- Use
TextChoices/IntegerChoicesfor status fields and enums - Add
get_absolute_url()for canonical object URLs - Include
__str__()for readable representations - Set proper
orderingin Meta for consistent default sorting - Add database indexes for frequently filtered/sorted fields
- Use abstract base models for shared fields (timestamps, soft deletes, etc.)
Field Selection Guidelines
- Use
blank=True, default=""for optional text fields (avoid null) - Use
null=True, blank=Truefor optional foreign keys - For unique optional fields, use
null=Trueto avoid collision issues - Leverage
JSONFieldfor flexible metadata (avoid creating many optional fields) - Set appropriate
max_lengthbased on actual data needs
Encapsulate Business Logic in Model Methods
- State transitions:
post.publish(),order.cancel() - Permission checks:
post.is_editable_by(user) - Complex calculations:
invoice.calculate_total() - Use properties for computed read-only values
- Specify
update_fieldswhen saving partial changes
QuerySet Patterns: The Power of Composition
Custom QuerySet classes are your secret weapon. They make queries reusable, chainable, and testable.
Pattern: QuerySet as Manager
Define a QuerySet subclass with domain-specific filter methods
Attach it to your model: objects = YourQuerySet.as_manager()
Chain methods for composable queriesBenefits
- Reusable query logic across views, tasks, management commands
- Chainable methods enable expressive, readable queries
- Easy to test in isolation
- Encapsulates query complexity away from views
Common QuerySet Methods
- Filtering by status/state
- Date range queries (recent, upcoming, expired)
- User-scoped queries (owned_by, visible_to)
- Combined lookups (published_and_recent)
Query Optimization: Avoid N+1 Queries
The Golden Rules
- select_related(): Use for ForeignKey and OneToOneField (creates SQL JOIN)
- prefetch_related(): Use for ManyToManyField and reverse ForeignKeys (separate query + Python join)
- only(): Load specific fields when you don't need the whole object
- defer(): Exclude heavy fields (TextField, JSONField) you won't use
- Prefetch() object: Customize prefetch with filters and select_related
Efficient Counting and Existence Checks
- Use
.exists()instead ofif queryset:orif len(queryset): - Use
.count()instead oflen(queryset.all()) - Both perform database-level operations without loading objects
Aggregation and Annotation
annotate(): Add computed fields to each object (Count, Sum, Avg, etc.)aggregate(): Compute values across entire queryset- Use
F()expressions for database-level updates (views=F('views') + 1) - Combine annotate with filter for "objects with at least N related items"
Managers vs QuerySets
Use QuerySets for chainable query logic. Use Managers for model-level operations that don't return querysets.
Manager: Think "factory methods" - User.objects.create_user()
QuerySet: Think "filters and transformations" - Post.objects.published().recent()
Most of the time, you want a custom QuerySet, not a custom Manager.
Signals: Use Sparingly
Signals create implicit coupling and make code harder to follow. Prefer explicit method calls.
When Signals Make Sense
- Audit logging (track all changes to a model)
- Cache invalidation (clear cache when model changes)
- Decoupling apps (third-party app needs to react to your models)
When to Avoid Signals
- Business logic that should be in model methods
- Logic tightly coupled to the calling code (just call the function directly)
- Complex workflows (use explicit service layer instead)
Rule of thumb: If you control both the trigger and the reaction, don't use a signal.
Migrations
Workflow
- Run
makemigrationsafter model changes - Review generated migration files before applying
- Run
migrateto apply migrations - Migrations should be reversible when possible
Data Migrations
Create with makemigrations --empty app_name. Use apps.get_model() to access models (not direct imports). Write both forward and reverse operations.
Use data migrations for: Populating new fields, transforming data, migrating between fields.
Anti-Patterns to Avoid
Query Anti-Patterns
- Iterating over objects and accessing relations without
select_related()/prefetch_related() - Using
if queryset:instead of.exists() - Using
len()to count instead of.count() - Loading entire objects when you only need specific fields
Design Anti-Patterns
- Business logic in views instead of models
- Views performing calculations that belong in model methods
- Overusing signals for synchronous operations
- Creating new models when JSONField would suffice
- Forgetting to add indexes for filtered/sorted fields
Integration
Works with:
- pytest-django-patterns: Factory-based model testing
- celery-patterns: Async operations on models (pass IDs, not instances)
- django-forms: ModelForm validation and saving
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- getsentry/sentry★ 46ksame name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository16 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- celery-patternsSkill
- code-qualitySkill
- django-extensionsSkill
- django-formsSkill
- django-templatesSkill
- docs-syncSkill
- htmx-patternsSkill
- onboardSkill
- pr-reviewSkill
- pr-summarySkill
- pytest-django-patternsSkill
- skill-creatorSkill
- systematic-debuggingSkill
- ticketSkill
- worktree-commit-mergeSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

