Name: Ivaylo Donchev
Age: 24
Experience with Django: 4 years
Full stack developer at HackSoft
FE
Django
Database
sql queries
HTTP request
HTTP response
The main job of the list / detail view is to forward data from DB to the client with as minimum job from the application as possible.
class Demo0Api(APIView):
class ArticleSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=255)
def get(self, request):
"""
Get first 200 articles
"""
queryset = list(Article.objects.all()[:200])
serializer = self.ArticleSerializer(queryset, many=True)
return Response(data=serializer.data)
class Foo(Model):
name = CharField(max_length=255) ###########################
# #
# Foo Foo Foo #
class Bar(Model): # \ | / #
name = CharField(max_length=255) # \ | / #
# \ | / #
foo = ForeignKey( # \ | / #
Foo, # Bar #
related_name='bars', # #
on_delete=CASCADE ###########################
)
# Case 1
for bar in Bar.objects.all():
print(bar.foo.name)
# Case 2
for foo in Foo.objects.all():
for bar in foo.bars.all():
print(bar.name)
class Foo(Model):
name = CharField(max_length=255) ###########################
# #
# Foo Foo Foo #
class Bar(Model): # \ | / #
name = CharField(max_length=255) # \ | / #
# \ | / #
foo = ForeignKey( # \ | / #
Foo, # Bar #
related_name='bars', # #
on_delete=CASCADE ###########################
)
# Case 1
for bar in Bar.objects.select_related('foo'):
print(bar.foo.name)
# Case 2
for foo in Foo.objects.prefetch_related('bars'):
for bar in foo.bars.all():
print(bar.name)
Relations ? Use select_related and prefetch_related
class Invoice(Model):
date = DateField()
description = TextField()
@property
def total_price(self):
price = 0
for item in self.items.all():
price += item.price
return price
class InvoiceItem(Model):
invoice = ForeignKey(
Invoice,
on_delete=CASCADE,
related_name='items',
)
quantity = IntegerField()
unit_price = DecimalField(max_digits=10, decimal_places=2)
tax = DecimalField(
max_digits=10,
decimal_places=2,
default=Decimal('0.2')
)
@property
def price(self):
return self.quantity * self.unit_price * (1 + self.tax)
class Demo2Api(APIView):
class InvoiceSerializer(Serializer):
id = IntegerField()
total_price = DecimalField(max_digits=10, decimal_places=2)
def get(self, request):
queryset = Invoice.objects.prefetch_related('items')
serializer = self.InvoiceSerializer(
queryset,
many=True
)
return Response(data=serializer.data)
~1.5 seconds to load (~700 Invoices)
class Demo2Api(APIView):
class InvoiceSerializer(serializers.Serializer):
id = serializers.IntegerField()
total_price = serializers.DecimalField(max_digits=10, decimal_places=2)
def get(self, request):
price_expression = ExpressionWrapper(
expression=F('quantity') * F('unit_price') * (F('tax') + Value(1)),
output_field=DecimalField()
)
queryset = Invoice.objects \
.annotate(
_total_price=Coalesce(
Subquery(
queryset=InvoiceItem.objects
.annotate(_price=price_expression)
.filter(invoice=OuterRef('id'))
.values_list('invoice__id')
.values_list(Sum('_price'))[:1],
output_field=DecimalField()
),
0.0
)
)
serializer = self.InvoiceSerializer(
queryset,
many=True
)
return Response(data=serializer.data)
~0.2 seconds to load (~700 Invoices)
.filter() & .exclude()
.annotate()
.only() & .defer()
QuerySet
sql.Query
Q(age__gt=20)
Q(name_startswith='I')
BaseUser.objects \
.filter(age__gt=20) \
.filter(name__startswith='I')
QuerySet is an immutable math tree
User.objects.all() != User.objects.all()
.update() == UPDATE ... WHERE ...
.delete() == DELETE .... WHERE ...
.create() == INSERT ... VALUES ...
? == SELECT ... FROM ...
.all() - model instance
.values() - dictionary
.values_list() - tuple
.values_list(..., named=True) - named tuple
Objects count
Data structure
orm object
dictionary
tuple
named tuple*
def attach_is_expired_dynamically(invoices):
expiration_days = 2 # 2 days
now = datetime.now().date()
for invoice in invoices:
is_expired = bool(invoice.date + timedelta(days=expiration_days) > now)
invoice.is_expired = is_expired
return invoices
class InvoiceItemQuerySet(QuerySet):
def map(self, func):
class MyIterableClass(self._iterable_class):
def __iter__(self):
for obj in super(self.__class__, self).__iter__():
yield func(obj)
qs = self._clone()
qs._iterable_class = MyIterableClass
return qs
class InvoiceItem(Model):
objects = InvoiceItemQuerySet.as_manager()
invoice = ForeignKey(
Invoice,
on_delete=CASCADE,
related_name='items',
)
quantity = IntegerField()
unit_price = DecimalField(max_digits=10, decimal_places=2)
tax = DecimalField(
max_digits=10,
decimal_places=2,
default=Decimal('0.2')
)
@property
def price(self):
return self.quantity * self.unit_price * (1 + self.tax)
(source sample)
Check our Django Styleguide :)