django-models
npx skills add https://github.com/kjnez/claude-code-django --skill django-models
Agent 安装分布
Skill 文档
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 queries
Benefits
- 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