python / intermediate
Snippet
Django Q Objects for Complex Database Queries
Q objects enable complex database queries by combining conditions with AND, OR, and NOT operations. They are essential for building dynamic filters where conditions change based on user input. The annotate() method adds computed fields to queries, while select_related() prevents N+1 queries by eagerly loading related objects. Combining Q objects with annotation allows sophisticated filtering based on aggregated data.
snippet.py
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from django.db.models import Q, Count, Avgfrom datetime import datetime, timedeltaclass ArticleManager(models.Manager):def search_articles(self, query, filters=None):base_query = self.filter(Q(title__icontains=query) |Q(content__icontains=query) |Q(tags__name__icontains=query))if filters:query_filter = Q()if filters.get('published'):query_filter &= Q(is_published=True)if filters.get('date_range'):start, end = filters['date_range']query_filter &= Q(published_at__range=(start, end))if filters.get('authors'):query_filter &= Q(author__in=filters['authors'])if filters.get('excluded_categories'):query_filter &= ~Q(category__in=filters['excluded_categories'])base_query = base_query.filter(query_filter)return base_query.annotate(comment_count=Count('comments'),avg_rating=Avg('ratings__value')).select_related('author', 'category').order_by('-published_at')def get_trending(self, days=7):cutoff = datetime.now() - timedelta(days=days)return self.filter(published_at__gte=cutoff).annotate(engagement=Count('views') + Count('comments') * 3).order_by('-engagement')[:10]
django
Breakdown
1
Q(title__icontains=query) | Q(content__icontains=query)
Pipe operator creates OR condition, underscore icontains is case-insensitive contains
2
query_filter &= Q(category__in=filters['excluded_categories'])
Ampersand equals combines conditions with AND
3
query_filter &= ~Q(category__in=filters['excluded_categories'])
Tilde creates NOT condition, excluding categories from results
4
Count('comments') * 3
Multiply counts to weight engagement factors differently
5
.order_by('-engagement')[:10]
Slice at database level with [:10] for efficiency, minus for descending