ListSerializer模塊

ListSerializer模塊

自定義反序列化字段

# 一些只參與反序列化的字段,可是不是與數據庫關聯的
# 在序列化類中規定,並在校驗字段時從校驗的參數字典中剔除
class PublishModelSerializer(serializers.ModelSerializer):
    # 自定義不入庫的 反序列化 字段
    re_name = serializers.CharField(write_only=True)
    class Meta:
        model = models.Publish
        fields = ('name', 're_name', 'address')
    def validate(self, attrs):
        name = attrs.get('name')
        re_name = attrs.pop('re_name')  # 剔除
        if name != re_name:
            raise serializers.ValidationError({'re_name': '確認名字有誤'})
        return attrs

模型類中自定義序列化深度

# model類中自定義插拔的外鍵序列化字段,能夠採用外鍵關聯表的序列化類來完成深度查詢
class Book(BaseModel):
    # ...
    @property
    def publish_detail(self):
        from . import serializers
        data = serializers.PublishModelSerializer(self.publish).data
        return data

接口操做總結

# 單查羣查、單刪羣刪、單增羣增、單改羣改

路由層:api/url.py

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

模型層:api/models.py

# 修改部分:取消book類 name 與 publish 聯合惟一,
from django.db import models
from utils.model import BaseModel
class Book(BaseModel):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    img = models.ImageField(upload_to='img', default='img/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', null=True,
                                     related_name='books',
                                     db_constraint=False,
                                     )

    @property
    def publish_name(self):
        return self.publish.name

    @property
    def authors_info(self):
        author_list = []
        for author in self.authors.all():
            author_list.append({
                'name': author.name,
                'age': author.age,
                'mobile': author.detail.mobile
            })
        return author_list

    @property
    def publish_bac(self):
        from . import serializers
        data = serializers.PublishModelSerializer(self.publish).data
        return data

    class Meta:
        db_table = 'old_boy_book'
        verbose_name = '書籍'
        verbose_name_plural = verbose_name
        # 聯合惟一
        # unique_together = ('name', 'publish')

    def __str__(self):
        return self.name


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

    class Meta:
        db_table = 'old_boy_publish'
        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 = 'old_boy_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 = 'old_boy_author_detail'
        verbose_name = '做者詳情'
        verbose_name_plural = verbose_name

    def __str__(self):
        return '%s的詳情' % self.author.name

modelserializer實現羣增長和修改序列化層:api/serializers.py

# 羣增與羣改反序列化實現
# 1)ModelSerializer默認不經過羣改功能,須要在Meta中設置 list_serializer_class
# 2)自定義ListSerializer子類,重寫update方法,將子類綁定給 list_serializer_class
# 3)重寫update方法中經過 表明要更新的對象們instance 及 提供的更新數據們validated_data
#       獲得 更新後的對象們instance 返回
class BookV3ListSerializer(serializers.ListSerializer):
    def update(self, instance, validated_data):
        '''
        :param instance: [book_obj1, ..., book_obj2]
        :param validated_data: [{更新數據的字段}, ..., {更新數據的字段}]
        :return: [book_new_obj1, ..., book_new_obj2]
        '''
        for index, obj in enumerate(instance):  # type: int, models.Book
            # 單個對象數據更新 - 一個個屬性更新值
            for attr, value in validated_data[index].items():
                # 對象有更新數據字典的key對應的屬性,才完成該屬性的值更新
                if hasattr(obj, attr):
                    setattr(obj, attr, value)
            # 信息同步到數據庫
            obj.save()
        return instance

class BookV3ModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = ('name', 'price', 'publish', 'authors', 'img', 'publish_name', 'authors_info')
        list_serializer_class = BookV3ListSerializer
        extra_kwargs = {
            'publish': {
                'required': True,
                'write_only': True
            },
            'authors': {
                'required': True,
                'write_only': True
            },
            'img': {
                'read_only': True
            }
        }
    def validate_name(self, value):
        if 'sb' in value:
            raise serializers.ValidationError('書名有敏感詞彙')
        return value
    def validate(self, attrs):
        name = attrs.get('name')
        publish = attrs.get('publish')
        if models.Book.objects.filter(name=name, publish=publish):
            raise serializers.ValidationError({'book': '書籍以存在'})
        return attrs

視圖層:api/views.py

class BookV3APIView(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 book_obj:
                return APIResponse(1, 'pk error')
            book_ser = serializers.BookV3ModelSerializer(book_obj)
            return APIResponse(0, 'ok', results=book_ser.data)
        book_query = models.Book.objects.filter(is_delete=False).all().order_by('-id')
        book_ser = serializers.BookV3ModelSerializer(book_query, many=True)
        return APIResponse(0, 'ok', results=book_ser.data)
    
    
    # 單增羣增
    def post(self, request, *args, **kwargs):
        # 單增 /books/  data  {}
        # 羣增 /books/  data  [{}, ..., {}]
        request_data = request.data
        if isinstance(request_data, dict):
            data = [request_data, ]
        elif isinstance(request_data, list):
            data = request_data
        else:
            return APIResponse(1, '數據格式有誤')
        book_ser = serializers.BookV3ModelSerializer(data=data, many=True)
        if book_ser.is_valid():
            book_obj_list = book_ser.save()
            return APIResponse(0, 'ok',
                results=serializers.BookV3ModelSerializer(book_obj_list, many=True).data
            )
        else:
            return APIResponse(1, '添加失敗', results=book_ser.errors)

        
    """
    1)先肯定要更新的對象 主鍵們 與 數據們
    2)經過校驗數據庫剔除 已刪除的對象 與 不存在的對象
        主鍵們 => 剔除過程 => 能夠修改的對象們
        數據們 => 剔除過程 => 能夠修改的對象們對應的數據們
    3)反序列化及校驗過程
        經過 => save() 完成更新
        失敗 => ser_obj.errors 返回
    """
    # 單改羣改
    def patch(self, request, *args, **kwargs):
        # 單改 /books/(pk)/  data  {"name": "new_name", ...}
        # 羣改 /books/  data  [{"pk": 1, "name": "new_name", ...}, ...,{"pk": n, "name": "new_name", ...}]
        # 結果:
        # pks = [1, ..., n] => book_query => instance
        # data = [{"name": "new_name", ...}, ..., {"name": "new_name", ...}] => data

        # 數據預處理
        pk = kwargs.get('pk')
        request_data = request.data
        if pk:
            if not isinstance(request_data, dict):
                return APIResponse(1, '單改數據有誤')
            pks = [pk, ]
            data = [request_data, ]
        elif isinstance(request_data, list):
            try:
                pks = []
                for dic in request_data:
                    pks.append(dic.pop('pk'))
                data = request_data
            except:
                return APIResponse(1, '羣改數據有誤')
        else:
            return APIResponse(1, '數據格式有誤')

        # 將 已刪除的書籍 與 不存在的書籍 剔除 (提供修改的數據相對應也剔除)
        book_query = []
        filter_data = []
        for index, pk in enumerate(pks):
            book_obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
            if book_obj:
                book_query.append(book_obj)
                filter_data.append(data[index])
        # 反序列化完成數據的修改
        book_ser = serializers.BookV3ModelSerializer(instance=book_query, data=filter_data, many=True, partial=True)
        if book_ser.is_valid():
            book_obj_list = book_ser.save()
            return APIResponse(1, 'ok',
                results=serializers.BookV3ModelSerializer(book_obj_list, many=True).data
            )
        else:
            return APIResponse(1, '更新失敗', results=book_ser.errors)

    # 單刪羣刪  
    def delete(self, request, *args, **kwargs):
        # 單刪 /books/(pk)/
        # 羣刪 /books/  數據包攜帶 pks => request.data
        pk = kwargs.get('pk')
        if pk:
            pks = [pk]
        else:
            pks = request.data.get('pks')
        if not pks:
            return APIResponse(1, '刪除失敗')
        book_query = models.Book.objects.filter(is_delete=False, pk__in=pks)
        if not book_query.update(is_delete=True):  # 操做的記錄大於0就記錄刪除成功
            return APIResponse(1, '刪除失敗')
        return APIResponse(0, '刪除成功')
相關文章
相關標籤/搜索