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

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

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

问题

我有models.py

class Category(MPTTModel):
    # 几个字段

class Brand(models.Model):
    # 几个字段

class Attribute(models.Model):
   # 几个字段

class AttributeValue(models.Model):
   attributes = models.ForeignKey(Attribute, # 其他条件)
   # 几个字段

class Product(models.Model):
   category = models.ForeignKey(Category, # 其他条件)
   brand = models.ForeignKey(Brand, # 其他条件)
   attributes = models.ManyToManyField(Attribute, # 其他条件)
   # 其他字段

class ProductImages(models.Model):
   product = models.ForeignKey(Product, # 其他条件)

views.py,我有

class ProductAPIView(generics.GenericAPIView):

    serializer_class = ProductSerializer
    queryset = Product.objects.all()

serializers.py

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

我得到的JSON响应如下

{
    "brand": ...,
    "category": ...,
    "attributes": [
        { "attribute_values": [...] },
        { "attribute_values": [...] }
    ],
    "product_images": [{...}, {...}]
}

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

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

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

英文:

I have models.py

class Category(MPTTModel):
    # few fields

class Brand(models.Model):
    # few fields

class Attribute(models.Model):
   # few fields

class AttributeValue(models.Model):
   attributes = models.ForeignKey(Attribute, # other conditions)
   # few fields

class Product(models.Model):
   category = models.ForeignKey(Category, # other conditions)
   brand = models.ForeignKey(Brand, # other conditions)
   attributes = models.ManyToManyField(Attribute, # other conditions)
   # few other fields

class ProductImages(models.Model):
   product = models.ForeignKey(Product, # other conditions)

In views.py, I have

class ProductAPIView(generics.GenericAPIView):

    serializer_class = ProductSerializer
    queryset = Product.objects.all()

serializers.py

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

I'm getting my json response like this

{
    "brand": ...,
    "category": ...,
    "attributes": [
        { "attribute_values": [...] },
        { "attribute_values": [...] }
    ],
    "product_images": [{...}, {...}]
}

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:

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


def query_debugger(func):

    @functools.wraps(func)
    def inner_func(*args, **kwargs):

        reset_queries()
        
        start_queries = len(connection.queries)

        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()

        end_queries = len(connection.queries)

        print(f"Function : {func.__name__}")
        print(connection.queries)
        print(f"Number of Queries : {end_queries - start_queries}")
        print(f"Finished in : {(end - start):.2f}s")
        return result

    return inner_func

And use it like this:

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

<details>
<summary>英文:</summary>

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)
and this [question](https://stackoverflow.com/questions/54569384/django-chaining-prefetch-related-and-select-related?answertab=scoredesc#tab-top).

for debugging purposes I always use this decorator:

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

def query_debugger(func):

@functools.wraps(func)
def inner_func(*args, **kwargs):

    reset_queries()
    
    start_queries = len(connection.queries)

    start = time.perf_counter()
    result = func(*args, **kwargs)
    end = time.perf_counter()

    end_queries = len(connection.queries)

    print(f&quot;Function : {func.__name__}&quot;)
    print(connection.queries)
    print(f&quot;Number of Queries : {end_queries - start_queries}&quot;)
    print(f&quot;Finished in : {(end - start):.2f}s&quot;)
    return result

return inner_func
And use it like this:

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



</details>



# 答案2
**得分**: 1

你可以链式使用 `select_related` 和 `prefetch_related`:

```python
class ProductAPIView(generics.GenericAPIView):

    serializer_class = ProductSerializer
    queryset = Product.objects.select_related('category', 'brand').prefetch_related("attributes")

或者覆盖 get_queryset 函数:

class ProductAPIView(generics.GenericAPIView):

    serializer_class = ProductSerializer
    
    def get_queryset(self):
        qs = super().get_queryset()
        return qs.select_related('category', 'brand').prefetch_related("attributes")

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

英文:

you can chain select_realted & prefetch_related

class ProductAPIView(generics.GenericAPIView):

    serializer_class = ProductSerializer
    queryset = Product.objects.select_related(&#39;category&#39;, &#39;brand&#39;).prefetch_related(&quot;attributes&quot;)

or override get_queryset function

class ProductAPIView(generics.GenericAPIView):

    serializer_class = ProductSerializer
    
    def get_queryset(self):
        qs =  super().get_queryset()
        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:

确定