Intro
We saw in previous article how to use query params
. Now it's time to improve it.
Improvement I
Our overwritten get_queryset
method will create multiple INNER JOINS
in our SQL
query. We can prevent that by using prefetch_related
:
# articles/views.py
from rest_framework import generics
from src.articles.models import Article
from src.articles.serializer import ArticleSerializer
class ArticleListView(generics.ListCreateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
region_separator = ","
def get_queryset(self):
"""
Optionally restricts the returned articles to given regions,
by filtering against a `regions` query parameter in the URL.
"""
regions = self.request.query_params.get("regions", None)
if regions:
qs = Article.objects.prefetch_related("regions")
for region in regions.split(self.region_separator):
qs = qs.filter(regions__code=region)
return qs
# return default Article.objects.all()
return super().get_queryset()
Improvement II
Writing business logic or query decisions in Views
is usually considered anti-pattern for Django. Query methods should be in models
or better in models.Manager
;
# articles/models.py
from django.db import models
from src.articles.models import Article
from src.articles.serializer import ArticleSerializer
class ArticleManager(models.Manager):
def by_regions(self, regions, region_separator=","):
"""
Restricts the returned articles to given regions
"""
if regions:
qs = self.prefetch_related("regions")
for region in regions.split(region_separator):
qs = qs.filter(regions__code=region)
return qs
return self.all()
class Article(models.Model):
title = models.CharField(max_length=255)
content = models.TextField(blank=True)
regions = models.ManyToManyField(
"Region", related_name="articles", blank=True
)
objects = ArticleManager()
def __str__(self):
return self.title
...
# articles/views.py
from rest_framework import generics
from src.articles.models import Article
from src.articles.serializer import ArticleSerializer
class ArticleListView(generics.ListCreateAPIView):
serializer_class = ArticleSerializer
def get_queryset(self):
"""
Optionally restricts the returned articles to given regions,
by filtering against a `regions` query parameter in the URL.
"""
regions = self.request.query_params.get("regions", None)
return Article.objects.by_regions(regions=regions)
If you have more complex queris you can use third party packages.
All done!
Top comments (0)