我有一个Product
模型,正在使用该模型来检索所有产品和单个产品。查询单个记录时,我需要返回所有列,但查询全部时,只返回一些列。
# models.py
class Product(models.Model):
product_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
description = models.CharField(max_length=1000)
price = models.DecimalField(max_digits=10, decimal_places=2)
discounted_price = models.DecimalField(max_digits=10, decimal_places=2)
image = models.CharField(max_length=150, blank=True, null=True)
image_2 = models.CharField(max_length=150, blank=True, null=True)
thumbnail = models.CharField(max_length=150, blank=True, null=True)
display = models.SmallIntegerField()
class Meta:
managed = False
db_table = 'product'
# serializers.py
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('product_id', 'name', 'description', 'price', 'discounted_price', 'image', 'thumbnail')
# products.py
import logging
from django.db.models import F
from django.contrib.auth.models import AnonymousUser
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.filters import SearchFilter
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from api import errors
from api.models import Category, Product, Review, ProductCategory
from api.serializers import ProductSerializer, ReviewSerializer
logger = logging.getLogger(__name__)
class ProductSetPagination(PageNumberPagination):
page_size = 20
page_query_description = 'Inform the page. Starting with 1. Default: 1'
page_size_query_param = 'limit'
page_size_query_description = 'Limit per page, Default: 20.'
max_page_size = 200
class ProductViewSet(viewsets.ReadOnlyModelViewSet):
"""
list: Return a list of products
retrieve: Return a product by ID.
"""
queryset = Product.objects.all().order_by('product_id')
serializer_class = ProductSerializer
pagination_class = ProductSetPagination
filter_backends = (SearchFilter,)
search_fields = ('name', 'description')
@action(methods=['GET'], detail=False, url_path='search', url_name='Search products')
def search(self, request, *args, **kwargs):
"""
Search products
"""
return super().list(request, *args, **kwargs)
def get_products_by_category(self, request, category_id):
"""
Get a list of Products by Categories
"""
# Filter queryset to find all products in category
response = ProductCategory.objects.filter(category_id=category_id).values(
'product_id', name=F('product__name'), description=F('product__description'), price=F('product__price'), discounted_price=F('product__discounted_price'), thumbnail=F('product__thumbnail'))
# Return response
if response.exists():
return Response(response, 200)
else:
return Response(response, 204)
def get_products_by_department(self, request, department_id):
"""
Get a list of Products of Departments
"""
categories = Category.objects.filter(department_id=department_id).values('category_id')
for item in categories:
category_id = item['category_id']
products = ProductCategory.objects.filter(category_id=category_id).values(
'product_id', name=F('product__name'), description=F('product__description'),
price=F('product__price'), discounted_price=F('product__discounted_price'), thumbnail=F('product__thumbnail'))
# Return response
if products.exists():
return Response(products, 200)
else:
return Response(products, 204)
def render_product_review_row(self, row):
row["review"] = row.pop("tmp_review")
row["rating"] = row.pop("tmp_rating")
row["created_on"] = row.pop("tmp_created_on")
return row
@action(methods=['GET'], detail=True, url_path='<int:product_id>/reviews', url_name='List reviews')
def reviews(self, request, *args, **kwargs):
"""
Return a list of reviews
"""
product_id = int(kwargs['product_id'])
# Filter all reviews for product
queryset = Review.objects.filter(product_id=product_id)
queryset = queryset.annotate(
original_review=F('review'),
original_rating=F('rating'),
original_created_on=F('created_on'),
)
queryset = queryset.values(
name=F('customer__name'),
tmp_review=F('review'),
tmp_rating=F('rating'),
tmp_created_on=F('created_on'),
)
if queryset.exists():
status_code = 200
else:
status_code = 204
response = [self.render_product_review_row(row) for row in queryset]
return Response(response, status_code)
@swagger_auto_schema(method='POST', request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'review': openapi.Schema(type=openapi.TYPE_STRING, description='Review Text of Product', required=['true']),
'rating': openapi.Schema(type=openapi.TYPE_INTEGER, description='Rating of Product', required=['true']),
}
))
列出所有产品的回复应为:
{
"paginationMeta": {
"currentPage": integer,
"currentPageSize": integer,
"totalPages": integer,
"totalRecords": integer
},
"rows": [ {
"product_id": integer,
"name": string,
"description": string,
"price": string,
"discounted_price": string,
"thumbnail": string
}
}
单个产品的响应应为:
params:
{
"description_length": integer //Limit of the description, default: 200
}
{
"product_id": integer,
"name": string,
"description": string,
"price": string,
"discounted_price": string,
"image": string,
"image_2": string,
"thumbnail": string
}
我应该覆盖这些方法并为此使用自定义序列化程序,还是有一种更简单/更好的方法来获得上述结果?
答案 0 :(得分:1)
您可以编写2个Serializer
,一个用于获取列表,一个用于检索1个项目。
我假设ProductSerializer
用于检索,而ProductListSerializer
用于列表。
class ProductListSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('product_id', 'name',)
# only show 2 field when get all item
您需要覆盖get_serializer_class
。我认为,我们可以创建如下的mixin类:
class MultiSerializerViewSetMixin(object):
def get_serializer_class(self):
try:
return self.serializer_action_classes[self.action]
except (KeyError, AttributeError):
return super(MultiSerializerViewSetMixin, self).get_serializer_class()
并与您的ProductViewSet
class ProductViewSet(MultiSerializerViewSetMixin, viewsets.ReadOnlyModelViewSet):
queryset = Product.objects.all().order_by('product_id')
serializer_class = ProductSerializer
serializer_action_classes = {
'list': ProductListSerializer,
'retrieve': ProductSerializer,
}