Master complex database relationships, query optimization, and advanced ORM techniques
The Django ORM is one of the most powerful features of the framework, providing a Pythonic way to interact with databases. Advanced ORM techniques enable you to build scalable, high-performance applications that handle millions of records efficiently.
Why Advanced ORM Matters:
What You'll Build:
A complete content management system with advanced relationships, custom managers, query optimization, and complex business logic - demonstrating enterprise-level database design.
Performance-Critical Features:
Django supports sophisticated database relationships for complex data modeling:
Foreign Key Relationships:
1class Author(models.Model):
2 name = models.CharField(max_length=100)
3 email = models.EmailField(unique=True)
4
5class Book(models.Model):
6 title = models.CharField(max_length=200)
7 author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
8 # on_delete options: CASCADE, PROTECT, SET_NULL, SET_DEFAULTMany-to-Many Relationships:
1class Tag(models.Model):
2 name = models.CharField(max_length=50)
3
4class Article(models.Model):
5 title = models.CharField(max_length=200)
6 tags = models.ManyToManyField(Tag, through='ArticleTag')
7
8class ArticleTag(models.Model):
9 article = models.ForeignKey(Article, on_delete=models.CASCADE)
10 tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
11 created_at = models.DateTimeField(auto_now_add=True)
12 # Custom through model allows additional fieldsOne-to-One Relationships:
1class UserProfile(models.Model):
2 user = models.OneToOneField(User, on_delete=models.CASCADE)
3 bio = models.TextField()
4 avatar = models.ImageField(upload_to='avatars/')Self-Referencing Relationships:
1class Category(models.Model):
2 name = models.CharField(max_length=100)
3 parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)Efficient querying is crucial for application performance:
N+1 Query Problem:
1# BAD: N+1 queries (1 + N additional queries)
2books = Book.objects.all()
3for book in books:
4 print(book.author.name) # Additional query for each book
5
6# GOOD: Single query with join
7books = Book.objects.select_related('author')
8for book in books:
9 print(book.author.name) # No additional queriesselect_related() for Foreign Keys:
1# Fetch books with their authors in a single query
2books = Book.objects.select_related('author', 'publisher')prefetch_related() for Reverse Relations:
1# Fetch authors with their books efficiently
2authors = Author.objects.prefetch_related('books')
3for author in authors:
4 for book in author.books.all(): # No additional queries
5 print(book.title)Complex Prefetching:
1from django.db.models import Prefetch
2
3# Custom prefetch with filtering
4authors = Author.objects.prefetch_related(
5 Prefetch('books', queryset=Book.objects.filter(published=True))
6)Aggregation and Annotation:
1from django.db.models import Count, Avg, Sum
2
3# Annotate authors with book counts
4authors = Author.objects.annotate(
5 book_count=Count('books'),
6 avg_rating=Avg('books__rating')
7)Custom managers encapsulate common queries and business logic:
Custom Manager:
1class PublishedManager(models.Manager):
2 def get_queryset(self):
3 return super().get_queryset().filter(status='published')
4
5class BookManager(models.Manager):
6 def by_author(self, author_name):
7 return self.filter(author__name__icontains=author_name)
8
9 def bestsellers(self):
10 return self.filter(sales__gte=10000).order_by('-sales')
11
12class Book(models.Model):
13 title = models.CharField(max_length=200)
14 status = models.CharField(max_length=20, default='draft')
15 sales = models.PositiveIntegerField(default=0)
16
17 objects = models.Manager() # Default manager
18 published = PublishedManager() # Custom manager
19 book_manager = BookManager() # Custom manager with methodsCustom QuerySet:
1class BookQuerySet(models.QuerySet):
2 def published(self):
3 return self.filter(status='published')
4
5 def by_category(self, category):
6 return self.filter(category=category)
7
8 def bestsellers(self):
9 return self.filter(sales__gte=10000)
10
11class BookManager(models.Manager):
12 def get_queryset(self):
13 return BookQuerySet(self.model, using=self._db)
14
15 def published(self):
16 return self.get_queryset().published()
17
18class Book(models.Model):
19 # ... fields ...
20 objects = BookManager()
21
22# Usage: Book.objects.published().by_category('fiction').bestsellers()Manager Inheritance:
1class TimestampedManager(models.Manager):
2 def recent(self, days=7):
3 from django.utils import timezone
4 cutoff = timezone.now() - timezone.timedelta(days=days)
5 return self.filter(created_at__gte=cutoff)
6
7class ArticleManager(TimestampedManager):
8 def featured(self):
9 return self.filter(is_featured=True)Build a sophisticated CMS with complex relationships, custom managers, query optimization, and advanced ORM features. Demonstrates enterprise-level database design patterns.
1# š ADVANCED CONTENT MANAGEMENT SYSTEM
2# Enterprise-level Django ORM with complex relationships and optimization
3
4# models.py - Advanced Model Design
5from django.db import models
6from django.contrib.auth.models import User
7from django.utils import timezone
8from django.core.validators import MinValueValidator, MaxValueValidator
9from django.db.models import Q, Count, Avg, Sum, F, Case, When, Value
10from django.db.models.functions import Coalesce
11
12# Custom QuerySets for reusable query logic
13class CategoryQuerySet(models.QuerySet):
14 def active(self):
15 return self.filter(is_active=True)
16
17 def with_article_count(self):
18 return self.annotate(
19 article_count=Count('articles', filter=Q(articles__status='published'))
20 )
21
22 def top_level(self):
23 return self.filter(parent__isnull=True)
24
25 def by_popularity(self):
26 return self.annotate(
27 total_views=Sum('articles__view_count')
28 ).order_by('-total_views')
29
30class ArticleQuerySet(models.QuerySet):
31 def published(self):
32 return self.filter(
33 status='published',
34 published_at__lte=timezone.now()
35 )
36
37 def by_author(self, author):
38 return self.filter(author=author)
39
40 def in_category(self, category):
41 # Include subcategories using tree traversal
42 return self.filter(
43 Q(category=category) | Q(category__parent=category)
44 )
45
46 def popular(self, threshold=1000):
47 return self.filter(view_count__gte=threshold)
48
49 def recent(self, days=30):
50 cutoff = timezone.now() - timezone.timedelta(days=days)
51 return self.filter(published_at__gte=cutoff)
52
53 def with_engagement_score(self):
54 """Calculate engagement score based on views, comments, and likes"""
55 return self.annotate(
56 engagement_score=F('view_count') + F('comment_count') * 5 + F('like_count') * 10
57 )
58
59 def featured_or_popular(self):
60 return self.filter(
61 Q(is_featured=True) | Q(view_count__gte=5000)
62 )
63
64# Custom Managers
65class CategoryManager(models.Manager):
66 def get_queryset(self):
67 return CategoryQuerySet(self.model, using=self._db)
68
69 def active(self):
70 return self.get_queryset().active()
71
72 def with_stats(self):
73 return self.get_queryset().active().with_article_count()
74
75class ArticleManager(models.Manager):
76 def get_queryset(self):
77 return ArticleQuerySet(self.model, using=self._db)
78
79 def published(self):
80 return self.get_queryset().published()
81
82 def for_homepage(self):
83 """Optimized query for homepage with all needed relations"""
84 return self.get_queryset().published().select_related(
85 'author', 'category'
86 ).prefetch_related(
87 'tags', 'comments__author'
88 ).with_engagement_score()[:10]
89
90class PublishedArticleManager(models.Manager):
91 """Manager that only returns published articles"""
92 def get_queryset(self):
93 return ArticleQuerySet(self.model, using=self._db).published()
94
95# Advanced Models with Complex Relationships
96class Category(models.Model):
97 """Hierarchical category system"""
98 name = models.CharField(max_length=100, unique=True)
99 slug = models.SlugField(max_length=100, unique=True)
100 description = models.TextField(blank=True)
101 parent = models.ForeignKey(
102 'self',
103 on_delete=models.CASCADE,
104 null=True,
105 blank=True,
106 related_name='children'
107 )
108 image = models.ImageField(upload_to='categories/', blank=True, null=True)
109 is_active = models.BooleanField(default=True)
110 sort_order = models.PositiveIntegerField(default=0)
111 created_at = models.DateTimeField(auto_now_add=True)
112
113 objects = CategoryManager()
114
115 class Meta:
116 verbose_name_plural = "Categories"
117 ordering = ['sort_order', 'name']
118 indexes = [
119 models.Index(fields=['parent', 'is_active']),
120 models.Index(fields=['sort_order']),
121 ]
122
123 def __str__(self):
124 return self.name
125
126 @property
127 def full_path(self):
128 """Get full category path (e.g., 'Technology > Programming > Python')"""
129 if self.parent:
130 return f"{self.parent.full_path} > {self.name}"
131 return self.name
132
133 def get_descendants(self):
134 """Get all descendant categories"""
135 descendants = []
136 for child in self.children.all():
137 descendants.append(child)
138 descendants.extend(child.get_descendants())
139 return descendants
140
141class Tag(models.Model):
142 """Article tags for better organization"""
143 name = models.CharField(max_length=50, unique=True)
144 slug = models.SlugField(max_length=50, unique=True)
145 description = models.TextField(blank=True)
146 color = models.CharField(max_length=7, default='#007bff') # Hex color
147 is_featured = models.BooleanField(default=False)
148 created_at = models.DateTimeField(auto_now_add=True)
149
150 class Meta:
151 ordering = ['name']
152
153 def __str__(self):
154 return self.name
155
156 @property
157 def article_count(self):
158 return self.articles.filter(status='published').count()
159
160class Author(models.Model):
161 """Extended author model with additional fields"""
162 user = models.OneToOneField(User, on_delete=models.CASCADE)
163 bio = models.TextField(blank=True)
164 avatar = models.ImageField(upload_to='authors/', blank=True, null=True)
165 website = models.URLField(blank=True)
166 social_twitter = models.CharField(max_length=50, blank=True)
167 social_linkedin = models.CharField(max_length=50, blank=True)
168 is_verified = models.BooleanField(default=False)
169 follower_count = models.PositiveIntegerField(default=0)
170 created_at = models.DateTimeField(auto_now_add=True)
171
172 class Meta:
173 ordering = ['user__first_name', 'user__last_name']
174
175 def __str__(self):
176 return self.user.get_full_name() or self.user.username
177
178 @property
179 def article_count(self):
180 return self.articles.filter(status='published').count()
181
182 @property
183 def total_views(self):
184 return self.articles.filter(status='published').aggregate(
185 total=Sum('view_count')
186 )['total'] or 0
187
188class Article(models.Model):
189 """Main article model with advanced features"""
190 STATUS_CHOICES = [
191 ('draft', 'Draft'),
192 ('review', 'Under Review'),
193 ('published', 'Published'),
194 ('archived', 'Archived'),
195 ]
196
197 PRIORITY_CHOICES = [
198 (1, 'Low'),
199 (2, 'Normal'),
200 (3, 'High'),
201 (4, 'Critical'),
202 ]
203
204 # Core fields
205 title = models.CharField(max_length=200)
206 slug = models.SlugField(max_length=200, unique=True)
207 author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='articles')
208 category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='articles')
209 tags = models.ManyToManyField(Tag, blank=True, related_name='articles')
210
211 # Content fields
212 excerpt = models.TextField(max_length=300, blank=True)
213 content = models.TextField()
214 featured_image = models.ImageField(upload_to='articles/', blank=True, null=True)
215
216 # Publishing fields
217 status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
218 priority = models.PositiveSmallIntegerField(choices=PRIORITY_CHOICES, default=2)
219 is_featured = models.BooleanField(default=False)
220 is_sticky = models.BooleanField(default=False)
221 published_at = models.DateTimeField(blank=True, null=True)
222
223 # SEO fields
224 meta_title = models.CharField(max_length=70, blank=True)
225 meta_description = models.CharField(max_length=160, blank=True)
226 meta_keywords = models.CharField(max_length=255, blank=True)
227
228 # Analytics fields
229 view_count = models.PositiveIntegerField(default=0)
230 like_count = models.PositiveIntegerField(default=0)
231 share_count = models.PositiveIntegerField(default=0)
232 comment_count = models.PositiveIntegerField(default=0)
233
234 # Timestamps
235 created_at = models.DateTimeField(auto_now_add=True)
236 updated_at = models.DateTimeField(auto_now=True)
237
238 # Custom managers
239 objects = ArticleManager()
240 published_objects = PublishedArticleManager()
241
242 class Meta:
243 ordering = ['-published_at', '-created_at']
244 indexes = [
245 models.Index(fields=['status', 'published_at']),
246 models.Index(fields=['category', 'status']),
247 models.Index(fields=['author', 'status']),
248 models.Index(fields=['is_featured', 'status']),
249 models.Index(fields=['view_count']),
250 ]
251 constraints = [
252 models.CheckConstraint(
253 check=Q(view_count__gte=0),
254 name='positive_view_count'
255 ),
256 models.CheckConstraint(
257 check=Q(priority__in=[1, 2, 3, 4]),
258 name='valid_priority'
259 ),
260 ]
261
262 def __str__(self):
263 return self.title
264
265 def save(self, *args, **kwargs):
266 # Auto-set published_at when status changes to published
267 if self.status == 'published' and not self.published_at:
268 self.published_at = timezone.now()
269 super().save(*args, **kwargs)
270
271 @property
272 def reading_time(self):
273 """Calculate estimated reading time (words per minute)"""
274 words = len(self.content.split())
275 return max(1, words // 200)
276
277 @property
278 def engagement_score(self):
279 """Calculate engagement score"""
280 return self.view_count + (self.comment_count * 5) + (self.like_count * 10)
281
282 def increment_view_count(self):
283 """Efficiently increment view count"""
284 Article.objects.filter(id=self.id).update(view_count=F('view_count') + 1)
285
286class Comment(models.Model):
287 """Article comments with threading support"""
288 article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comments')
289 author = models.ForeignKey(User, on_delete=models.CASCADE)
290 parent = models.ForeignKey(
291 'self',
292 on_delete=models.CASCADE,
293 null=True,
294 blank=True,
295 related_name='replies'
296 )
297 content = models.TextField()
298 is_approved = models.BooleanField(default=False)
299 is_deleted = models.BooleanField(default=False)
300 created_at = models.DateTimeField(auto_now_add=True)
301 updated_at = models.DateTimeField(auto_now=True)
302
303 class Meta:
304 ordering = ['created_at']
305 indexes = [
306 models.Index(fields=['article', 'is_approved']),
307 models.Index(fields=['parent']),
308 ]
309
310 def __str__(self):
311 return f'Comment by {self.author.username} on {self.article.title}'
312
313 @property
314 def is_reply(self):
315 return self.parent is not None
316
317# Advanced Query Examples and Analytics
318class ContentAnalytics:
319 """Advanced analytics using Django ORM"""
320
321 @staticmethod
322 def get_popular_categories(limit=10):
323 """Get categories by total article views"""
324 return Category.objects.active().annotate(
325 total_views=Sum('articles__view_count', filter=Q(articles__status='published')),
326 article_count=Count('articles', filter=Q(articles__status='published'))
327 ).filter(
328 article_count__gt=0
329 ).order_by('-total_views')[:limit]
330
331 @staticmethod
332 def get_trending_articles(days=7, limit=10):
333 """Get trending articles in the last N days"""
334 cutoff = timezone.now() - timezone.timedelta(days=days)
335 return Article.published_objects.filter(
336 published_at__gte=cutoff
337 ).order_by('-view_count')[:limit]
338
339 @staticmethod
340 def get_author_leaderboard(limit=10):
341 """Get top authors by total views and articles"""
342 return Author.objects.annotate(
343 total_views=Sum('articles__view_count', filter=Q(articles__status='published')),
344 published_articles=Count('articles', filter=Q(articles__status='published')),
345 avg_views=Avg('articles__view_count', filter=Q(articles__status='published'))
346 ).filter(
347 published_articles__gt=0
348 ).order_by('-total_views')[:limit]
349
350 @staticmethod
351 def get_engagement_stats():
352 """Get overall engagement statistics"""
353 return Article.published_objects.aggregate(
354 total_views=Sum('view_count'),
355 total_likes=Sum('like_count'),
356 total_comments=Sum('comment_count'),
357 avg_engagement=Avg(F('view_count') + F('like_count') * 10 + F('comment_count') * 5)
358 )
359
360# Demonstration: Query Optimization Examples
361print("š Advanced Content Management System")
362print("=" * 60)
363
364print("\nš Advanced Query Examples:")
365
366# Simulate database queries (would normally hit database)
367query_examples = [
368 "Category.objects.active().with_article_count()",
369 "Article.objects.published().in_category(tech_category)",
370 "Article.objects.for_homepage() # Optimized with prefetch",
371 "Article.objects.with_engagement_score().order_by('-engagement_score')",
372 "Author.objects.annotate(total_views=Sum('articles__view_count'))",
373]
374
375for i, query in enumerate(query_examples, 1):
376 print(f"{i}. {query}")
377
378print("\nš Performance Optimizations:")
379optimizations = [
380 "select_related() for foreign key relationships",
381 "prefetch_related() for reverse foreign keys and M2M",
382 "Database indexes on frequently queried fields",
383 "Custom managers for reusable query logic",
384 "Annotation for computed fields",
385 "F() expressions for database-level operations",
386 "Bulk operations for multiple record updates",
387 "Query analysis with django-debug-toolbar"
388]
389
390for i, opt in enumerate(optimizations, 1):
391 print(f"{i:2d}. {opt}")
392
393print("\nšļø Advanced Model Features:")
394features = [
395 "Hierarchical categories with parent-child relationships",
396 "Custom QuerySets with chainable methods",
397 "Multiple managers for different use cases",
398 "Database constraints for data integrity",
399 "Computed properties with caching",
400 "Signal handlers for automatic operations",
401 "Meta options for ordering and indexing",
402 "Through models for M2M relationships with extra fields"
403]
404
405for i, feature in enumerate(features, 1):
406 print(f"{i:2d}. {feature}")
407
408print("\nš Analytics Capabilities:")
409print("⢠Popular categories by total views")
410print("⢠Trending articles in timeframe")
411print("⢠Author leaderboard with statistics")
412print("⢠Engagement scoring and ranking")
413print("⢠Category hierarchy traversal")
414print("⢠Comment threading and moderation")
415print("⢠View count tracking and analytics")
416print("⢠SEO optimization fields")
417
418print("\nā
Enterprise Features:")
419print("⢠Complex relationship modeling")
420print("⢠Query optimization for performance")
421print("⢠Custom business logic in managers")
422print("⢠Database-level constraints")
423print("⢠Comprehensive indexing strategy")
424print("⢠Analytics and reporting")
425print("⢠Content workflow management")
426print("⢠Hierarchical data structures")š Advanced ORM Architecture Breakdown:
1. Custom QuerySets:
CategoryQuerySet: Reusable category filtering and annotation logicArticleQuerySet: Complex article queries with engagement scoringpublished().recent().popular() for readable queries2. Custom Managers:
CategoryManager: Category-specific business logicArticleManager: General article operationsPublishedArticleManager: Filtered manager for published content only3. Complex Relationships:
4. Query Optimization Techniques:
select_related(): Reduces queries for foreign key relationshipsprefetch_related(): Optimizes reverse foreign key and M2M queries5. Database Design:
6. Advanced Model Features:
7. Analytics and Reporting:
This implementation showcases enterprise-level Django ORM usage for high-performance applications!
Your progress is saved in this browser, on this device only.