最佳方法查询 Django Rest Framework 中的数据库是什么?

huangapple go评论81阅读模式
英文:

What is the best method to query db in django rest framework

问题

我有models.py

  1. class Category(MPTTModel):
  2. # 几个字段
  3. class Brand(models.Model):
  4. # 几个字段
  5. class Attribute(models.Model):
  6. # 几个字段
  7. class AttributeValue(models.Model):
  8. attributes = models.ForeignKey(Attribute, # 其他条件)
  9. # 几个字段
  10. class Product(models.Model):
  11. category = models.ForeignKey(Category, # 其他条件)
  12. brand = models.ForeignKey(Brand, # 其他条件)
  13. attributes = models.ManyToManyField(Attribute, # 其他条件)
  14. # 其他字段
  15. class ProductImages(models.Model):
  16. product = models.ForeignKey(Product, # 其他条件)

views.py,我有

  1. class ProductAPIView(generics.GenericAPIView):
  2. serializer_class = ProductSerializer
  3. queryset = Product.objects.all()

serializers.py

  1. class ProductSerializer(serializers.ModelSerializer):
  2. brand = serializers.SlugRelatedField(queryset=Brand.objects.all())
  3. category = serializers.SlugRelatedField(queryset=Category.objects.all())
  4. attributes = AttributeSerializer(many=True, read_only=True)
  5. product_images = ProductImageSerializer(many=True, read_only=True)

我得到的JSON响应如下

  1. {
  2. "brand": ...,
  3. "category": ...,
  4. "attributes": [
  5. { "attribute_values": [...] },
  6. { "attribute_values": [...] }
  7. ],
  8. "product_images": [{...}, {...}]
  9. }

我了解到select_relatedprefetch_related可以优化外键字段和多对多字段的数据库查询。

我将查询集更改为queryset = Product.objects.select_related('category', 'brand').all()

如何在ProductAPIView中更改查询集以包括attributesproduct_imagesattribute_values字段,并提高性能?

英文:

I have models.py

  1. class Category(MPTTModel):
  2. # few fields
  3. class Brand(models.Model):
  4. # few fields
  5. class Attribute(models.Model):
  6. # few fields
  7. class AttributeValue(models.Model):
  8. attributes = models.ForeignKey(Attribute, # other conditions)
  9. # few fields
  10. class Product(models.Model):
  11. category = models.ForeignKey(Category, # other conditions)
  12. brand = models.ForeignKey(Brand, # other conditions)
  13. attributes = models.ManyToManyField(Attribute, # other conditions)
  14. # few other fields
  15. class ProductImages(models.Model):
  16. product = models.ForeignKey(Product, # other conditions)

In views.py, I have

  1. class ProductAPIView(generics.GenericAPIView):
  2. serializer_class = ProductSerializer
  3. queryset = Product.objects.all()

serializers.py

  1. class ProductSerializer(serializers.ModelSerializer):
  2. brand = serializers.SlugRelatedField(queryset = Brand.objects.all())
  3. category = serializers.SlugRelatedField(queryset = Category.objects.all())
  4. attributes = AttributeSerializer(many = True, read_only = True)
  5. product_images = ProductImageSerializer(many = True, read_only = True)

I'm getting my json response like this

  1. {
  2. "brand": ...,
  3. "category": ...,
  4. "attributes": [
  5. { "attribute_values": [...] },
  6. { "attribute_values": [...] }
  7. ],
  8. "product_images": [{...}, {...}]
  9. }

I came across select_related and prefetch_related which will optimise the db queries on foreign key fields and many-to-many fields.

I changed the queryset to queryset = Product.objects.select_related('category', 'brand').all()

How can i change the queryset in ProductAPIView to include the attributes, product_images and attribute_values fields also and improve the performance here ?

答案1

得分: 1

以下是您要翻译的部分:

"for the first part of your question you can take a look at Prefetch and this question.

for debugging purposes I always use this decorator:

  1. from django.db import connection, reset_queries
  2. import time
  3. import functools
  4. def query_debugger(func):
  5. @functools.wraps(func)
  6. def inner_func(*args, **kwargs):
  7. reset_queries()
  8. start_queries = len(connection.queries)
  9. start = time.perf_counter()
  10. result = func(*args, **kwargs)
  11. end = time.perf_counter()
  12. end_queries = len(connection.queries)
  13. print(f"Function : {func.__name__}")
  14. print(connection.queries)
  15. print(f"Number of Queries : {end_queries - start_queries}")
  16. print(f"Finished in : {(end - start):.2f}s")
  17. return result
  18. return inner_func

And use it like this:

  1. @query_debugger
  2. def get(self, request, *args, **kwargs):
  3. pass
  4. ```"
  5. <details>
  6. <summary>英文:</summary>
  7. for the first part of your question you can take a look at [Prefetch](https://docs.djangoproject.com/en/4.2/ref/models/querysets/#django.db.models.Prefetch)
  8. and this [question](https://stackoverflow.com/questions/54569384/django-chaining-prefetch-related-and-select-related?answertab=scoredesc#tab-top).
  9. for debugging purposes I always use this decorator:

from django.db import connection, reset_queries
import time
import functools

def query_debugger(func):

  1. @functools.wraps(func)
  2. def inner_func(*args, **kwargs):
  3. reset_queries()
  4. start_queries = len(connection.queries)
  5. start = time.perf_counter()
  6. result = func(*args, **kwargs)
  7. end = time.perf_counter()
  8. end_queries = len(connection.queries)
  9. print(f&quot;Function : {func.__name__}&quot;)
  10. print(connection.queries)
  11. print(f&quot;Number of Queries : {end_queries - start_queries}&quot;)
  12. print(f&quot;Finished in : {(end - start):.2f}s&quot;)
  13. return result
  14. return inner_func
  1. And use it like this:

@query_debugger
def get(self, request, *args, **kwargs):
pass

  1. </details>
  2. # 答案2
  3. **得分**: 1
  4. 你可以链式使用 `select_related` 和 `prefetch_related`:
  5. ```python
  6. class ProductAPIView(generics.GenericAPIView):
  7. serializer_class = ProductSerializer
  8. queryset = Product.objects.select_related('category', 'brand').prefetch_related("attributes")

或者覆盖 get_queryset 函数:

  1. class ProductAPIView(generics.GenericAPIView):
  2. serializer_class = ProductSerializer
  3. def get_queryset(self):
  4. qs = super().get_queryset()
  5. return qs.select_related('category', 'brand').prefetch_related("attributes")

你可以使用 Prefetch 类来指定在 prefetch_related() 中使用的查询集,并以这种方式与 select_related() 结合使用:

英文:

you can chain select_realted & prefetch_related

  1. class ProductAPIView(generics.GenericAPIView):
  2. serializer_class = ProductSerializer
  3. queryset = Product.objects.select_related(&#39;category&#39;, &#39;brand&#39;).prefetch_related(&quot;attributes&quot;)

or override get_queryset function

  1. class ProductAPIView(generics.GenericAPIView):
  2. serializer_class = ProductSerializer
  3. def get_queryset(self):
  4. qs = super().get_queryset()
  5. return qs.select_related(&#39;category&#39;, &#39;brand&#39;).prefetch_related(&quot;attributes&quot;)

You can use the Prefetch class to specify the queryset that is used in prefetch_related() and this way combine it with select_related():

huangapple
  • 本文由 发表于 2023年5月13日 18:45:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76242311.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定