drf--视图家族

drf 视图家族

前期准备

总路由 urls.py

from django.contib import admin
from django.views import serve
from django.conf import settings

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api/', include('api.urls')),
    url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
]

基表:utils/models.py

from django.db import models

class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    create_time = models.DateTimeField(auto_now_add=True, null=True)
    
    class Meta:
        # 抽象表, 不会完成数据库迁移
        abstract = True

模型层 api/models.py

from django.db import models
from utils.models import BaseModel

class Book(BaseModel):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    img = models.ImageField(upload_to='icon', default='icon/default.png')
    publish = models.ForeignKey(to='Publish',
                                null=True,
                                related_name='books',
                                db_constraint=False,
                                on_delete=models.DO_NOTHING
                                )
    authors = models.ManyToManyField(to='Author',
                                     related_name='authors',
                                     db_constraint=False

    @property
    def publish_info(self):
        return {'name': self.publish.name, 'address': self.publish.address}
    
    @property
    def author_info(self):
        author_list = []
        
        for author in self.authors.all():
            detail = AuthorDetail.objects.filter(author_id=self.author.id)
            author_list.append({
                'name': author.name,
                'age': author.age,
                'mobile': '未知' if not detail else author.detail.mobile
            })
            
        return author_list
        

    class Meta:
        db_table = "books"
        verbose_name = '书籍'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Author(BaseModel):
    name = models.CharField(max_length=64)
    age = models.IntegerField()

    @property
    def mobile(self):
        return self.detail.mobile
    
    class Meta:
        db_table = 'author'
        verbose_name = '作者'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class AuthorDetail(BaseModel):
    mobile = models.CharField(max_length=11)
    author = models.OneToOneField(to='Author',
                                  null=True,
                                  related_name='detail',
                                  db_constraint=False,
                                  on_delete=models.CASCADE
                                  )
    class Meta:
        db_table = 'detail'
        verbose_name = '作者详情'
        verbose_name_plural = verbose_name

    def __str__(self):
        return f"{self.author.name}的详情"


class Publish(BaseModel):
    name = models.CharField(max_length=64)
    address = models.CharField(max_length=128)

    class Meta:
        db_table = 'publish'
        verbose_name = '出版社'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

序列化器 api/serializers.py

from rest_framework import serializers
from . import models

class BookListSerializer(serializers.ListSerializer):
    def update(self, instance, validated_data):
        for ind, obj in enumerate(instance):
            for attr, value in validated_data[ind].items():
                if hasattr(obj, attr):
                    set(obj, attr, value)   
            obj.save()
        return instance

class BookV2ModelSerializer(serializers.ModelSerializer):
    re_name = serializers.CharField(
        min_length=3, 
        required=True, 
        write_only=True,  # 只参与反序列化
        error_messages={
            'min_length': '太短了',
            'required': '不能为空'
        }
    )
    
    class Meta:
        model = models.Book
        fields = ('name', 're_name', 'price', 'img', 'publish', 'publish_info', 'authors', 'authors_info')
        list_serializer_class = BookListSerializer
         extra_kwargs = {
            'name':{
                'min_length': 3,
                'error_messages': {
                    'min_length': '太短了',
                    'required': '不能为空'
                }
            },
            # 有默认值的字段会默认required为False,在反序列化中如果不传值不会进行校验,但是如果传值就会进行校验
            'publish':{
                'required': True,
                'write_only': True,
                'error_messages':{
                    'required': '不能为空'
                }
            },
            'authors':{
                'required': True,
                'write_only': True,
                'error_messages':{
                    'required': '不能为空'
                }
            },
        }
            
        # 自定义校验规则
        # 局部钩子
        def validate_name(self, value):
            if 'sb' in value:
                raise serializers.ValidationError('书名包含敏感词汇')
            return value
        
        # 全局钩子
        def validate(self, attr):
            name = attr.get('name')
            re_name = attr.get('re_name')
            publish = attr.get('publish')
            if name != re_name:
                raise serializers.ValidationError(
                    {'re_name': '两次书名不一致'}
                )
                
            # 通过逻辑控制联合唯一
            if models.Book.objects.filter(name=name, publish=publish):
                raise serializers.ValidationError(
                    {'book': '书籍已存在'}
                )   
            return attr  

基本视图(views)

主要就是通过视图类APIView,里面的逻辑都需要自己去实现,需要自己写接口

子路由 api/urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^books/$', views.BookAPIView.as_view()),
    url(r'^books/(?P<pk>.*)/$', views.BookAPIView.as_view()),
]

视图层 api/views.py

from rest_framework import views
from . import models, serializers
from utils.response import APIResponse

class BookAPIView(views.APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        # 单取
        if pk:
            book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
            if not pk:
                return APIResponse(1, 'pk有误')
            book_ser = serializers.BookV2ModelSerializer(book_obj)
            return APIResponse(0, 'ok', results=book_ser.data)

        # 群取
        book_obj_list = models.Book.objects.filter(is_delete=False).all()
        if not book_obj_list:
            return APIResponse(1, '没有数据')
        book_ser = serializers.BookV2ModelSerializer(book_obj_list, many=True)
        return APIResponse(0, 'ok', results=book_ser.data)

    # 增加
   def post(self, request, *args, **kwargs):
        # 把单增也转换为群增
        request_data = request.data
        if isinstance(request_data, dict):
            data = [request_data, ]
        elif isinstance(request, list):
            data = request_data
        else:
            return APIView(1, '数据格式有误')
        
        book_ser = serializers.BookV2ModelSerializer(data=data, many=True)
        if book_ser.is_valid():
            book_obj_list = book_ser.save()
            results = serializers.BookV2ModelSerializer(book_obj_list, many=True).data
            return APIResponse(0, 'ok', results=results)
        else:
            return APIResponse(1, '添加失败', results=book_ser.errors)

视图工具类(mixins)

  1. RetrieveModelMixin:retrieve方法实现了获取一个对象
  2. ListModelMixin:list方法实现了获取多个对象
  3. CreateModelMixin:create方法实现了增加一个对象
  4. UpdateModelMixin:update方法实现了单整体更新,partial_udate实现了单局部更新
  5. DestroyModelMixin:destory方法实现了单独删除

一般结合generics工具视图使用

工具视图(generics)

GenericAPIView

GenericAPIView,是generics家族的基类,主要帮我们把qureysetserializer_class封装成了属性,提供了以下三种方法:

  • get_qureyset():获取多个对象
  • get_object():获取一个对象
  • get_serializer(*args, **kwargs):获取序列化后的对象

GenericAPIView的子类们

继承了mixins中多个功能类和GenericAPIView基类

类名 实现接口
CreateAPIView 单增(post)
ListAPIView 多取(get)
RetrieveAPIView 单取(get)
DestroyAPIView 单删(delete)
UpdateAPIView 单局部(patch)及整体(put)修改
ListCreateAPIView 多取(get)和多增(post)
RetrieveUpdateAPIView 单取(get),单局部(patch)及整体(put)修改
RetrieveDestroyAPIView 单取(get)和单删(delete)
RetrieveUpdateDestroyAPIView 单取(get),单局部(patch)及整体(put)修改和单删(delete)

子路由 api/urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
    # generics
    url(r'^v1/books/$', views.BookGenericAPIView.as_view()),
    url(r'^v1/books/(?P<pk>.*)/$', views.BookGenericAPIView.as_view()),
    
    # mixins + generics
    url(r'^v2/books/$', views.BookMixinsGenericAPIView.as_view()),
    url(r'^v2/books/(?P<pk>.*)/$', views.BookMixinsGenericAPIView.as_view()),
    
    # 系统整合mixins、generics
    url(r'^v3/books/$', views.BookRetrieveUpdateAPIView.as_view()),
    url(r'^v3/books/(?P<pk>.*)/$', views.BookRetrieveUpdateAPIView.as_view()),
]

视图层 api/views.py

from rest_framework import generics, mixins
from . import models, serializers
from utils.response import APIResponse

# v1 generics - 视图基类
class BookGenericAPIView(generics.GenericAPIView):
    queryset = models.Book.objects.filter(is_delete=False).order_by('-id')
    serializer_class = serializers.BookModelSerializer

    def get(self, request, *args, **kwargs):
        if 'pk' in kwargs:
            book_obj = self.get_object()
            book_ser = self.get_serializer(book_obj)
            return APIResponse(0, 'ok', results=book_ser.data)
        book_query = self.get_queryset()
        book_ser = self.get_serializer(book_query, many=True)
        return APIResponse(0, 'ok', results=book_ser.data)

    def post(self, request, *args, **kwargs):
        book_ser = self.get_serializer(data=request.data)
        book_ser.is_valid(raise_exception=True)
        book_obj = book_ser.save()
        return APIResponse(0, 'ok', results=self.get_serializer(book_obj).data)

# v2 mixins工具集 + generics视图基类
class BookMixinsGenericAPIView(mixins.RetrieveModelMixin, mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
    queryset = models.Book.objects.filter(is_delete=False).order_by('-id')
    serializer_class = serializers.BookModelSerializer
    def get(self, request, *args, **kwargs):
        if 'pk' in kwargs:
            response = self.retrieve(request, *args, **kwargs)
        else:
            response = self.list(request, *args, **kwargs)
        return APIResponse(0, 'ok', results=response.data)

    def post(self, request, *args, **kwargs):
        response = self.create(request, *args, **kwargs)
        return APIResponse(0, 'ok', results=response.data)

# v3 视图基类子类 - 工具视图类
class BookRetrieveUpdateAPIView(generics.RetrieveUpdateAPIView):
    queryset = models.Book.objects.filter(is_delete=False).order_by('-id')
    serializer_class = serializers.BookModelSerializer

视图集(viewsets)

ViewSetMixin:视图集工具,重写了as_view方法, 完成了请求方式到视图类中的方法的映射

例如:.as_view({'get': 'retrieve', 'delete': 'remove_obj'}), 表示当请求方式为get的时候,则会调用retrieve函数,当请求方式为delete的时候,则会调用remove_obj函数

GenericViewSet:与模型类有关的接口视图集 - 可以从mixins那继承功能,也可以自定义功能

ViewSet:与模型类无关或不是标准模型类接口 - 一般都是自定义功能

子路由 api/urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^v4/books/$', views.BookGenericViewSet.as_view({
        'get': 'list',
        'post': 'create'
    })),
    url(r'^v4/books/(?P<pk>.*)/$', views.BookGenericViewSet.as_view({
        'get': 'retrieve',
        'put': 'update',
        'patch': 'partial_update',
        'delete': 'remove_book'
    })),
]

视图层 api/views.py

from rest_framework import mixins, viewsets
from . import models, serializers
from utils.response import APIResponse

class BookGenericViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):
    queryset = models.Book.objects.filter(is_delete=False).order_by('-id')
    serializer_class = serializers.BookModelSerializer

    def remove_book(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        try:
            book_obj = models.Book.objects.get(is_delete=False, pk=pk)
            book_obj.is_delete = True
            book_obj.save()
            return APIResponse(0, '删除成功')
        except:
            return APIResponse(1, '删除失败')

猜你喜欢

转载自www.cnblogs.com/Hades123/p/11488667.html