# DRF serializers小结

DRF serializers小结


django settings文件查找顺序

​ django查询变量的顺序是先从用户的settings里面查找,然后在global_settings中查找,如果用户desettings文件中找到了,则不会继续查找global_settings中的配置信息。

django原生的serializer

from django.db import models

# Create your models here.


class Courses(models.Model):
    title = models.CharField(max_length=32)
    description = models.CharField(max_length=128)
class CoursesView(View):
    def get(self, request):
        courses = list()

        for item in Courses.objects.all():
            course = {
                "title": item.title,
                "description": item.description
            }

            courses.append(course)

        return HttpResponse(json.dumps(courses, ensure_ascii=False))

​ 通过上面的方式,我们定义了符合规范的返回数据,加上符合规范的url,我们完成了手动完成了REST开发,但是效率不高。

from django.core.serializers import serialize

class StudentView(APIView):
    def get(self, request):
        origin_Student = Student.objects.all()
        serialized_students = serialize("json", origin_students)
        return HttpResponse(serialized_students)

​ 使用方法非常简单,导入模块之后,价格需要的格式和queryset传给serialize进行序列化,然后返回序列化后的数据。

​ 如果你的项目仅仅只需要序列化一部分数据,不需要用到诸如认证、权限等其他功能,可以使用django原生的serializer,否则建议使用DRF。

解析器组件

from django.http import JsonResponse
from rest_framework.view import APIView
from rest_framework.parsers import JSONParser, FormParser
# Create your views here.


class LoginView(APIView):
    def get(self, request):
        return render(request, 'parserver/logoin.html')
    def post(self, request):
        # request 是被drf封装的新的对象,是基于django的request
        # request.data是一个property,用于对数据进行校验
        # request.data最后会找到self.parser_classes中的解析器来实现对数据进行解析
        print(requet.data)  # {'username':'Gavin', 'password':123}
        return JsonResponse({'status_code':200, 'code':'ok'})

​ 使用步骤如下:

  1. from rest_framework.views import APIView
  2. 继承APIView
  3. 直接使用request.data就可以获取Json数据

​ 如果只需要解析Json数据,不允许其他类型的数据请求,可以这样做:

  1. from rest_framework.views import JsonParse
  2. 给视图类定义一个parser_classes变量,值为列表类型[JsonParser]
  3. 如果parser_classes = [] ,那就不处理任何数据类型的请求了

序列化组件的使用

from django.db import models
# create your models here.


class Publich(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_lenght=32)
    age = models.IntegerField()
    
    def __str__(self):
        return self.name
    
class Author(models.Model):    
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    def __str__(self):
        return self.name  

    
 class Book(models.Model):
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')
通过序列化组件进行接口设计,首先设计url,以GET和POST为例
from django.ruls import re_path
from serializers import views
urlpatterns = [
    re_path(r'book/', views.BookView.as_view())
]
新建一个名为app_serializers.py的模块,将所有的序列化的使用集中在这个模块里,对程序 进行解耦:
# -*- coding: utf-8 -*-
from rest_framework import serializers

from .models import Book


class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=128)
    publish_date = serializers.DateTimeField()
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
    publish = serializers.CharField(max_length=32)
    authors = serializers.CharField(max_length=32)
接着,使用序列化组件,开始写试图类
from rest_framework.views import APIView
from rest_framework.response import Response

# 当前app中的模块
from .models import Book
# 导入序列化器
from .app_serializer import BookSerializer

class BookView(APIView):
    def get(self, request):
        origin_books = Book.objects.all()
        serialized_books = BookSerializer(origin_books, many=True)
        return Response(serialized_books.data)
定义好model和url之后可使用序列化组价的步骤:
  1. 导入序列化组件:from rest_framework import serializers

  2. 定义序列化类:继承serializerrs.Serializer(建议单独创建一个专用模块来存放所有的序列化类)class BookSerializer(serializers.Serializer):pass

  3. 定义需要返回的字段:(字段类型可以与model中的类型不一致,参数也可以调整),字段名称必须与model中的一致。

  4. 在GET接口或几种,获取QuerySet

  5. 开始序列化:将QuerySet作为第一个参数传给序列化器类,many默认为False,如果返回的数据是一个列表嵌套字典的多个对象那个集合,需要改为many=True

  6. 返回:将序列化对象的data属性返回即可

    rest_framework.response的Response对象,它是DRF重新封装的相应对象。该对象在返回响应数据的时候,会判断客户端类型,如果是浏览器,它会以web页面的形式返回,如果是POSTMAN这一类的工具,就直接返回Json类型数据。

    此外,序列化类中的字段名也可以与model中的字段不一致,但是需要使用source参数来告诉组件原来的字段名,一般我们写成一样的就好了。

    class BookSerializer(serializers.Serializer):
        BookTitle = serializers.CharField(max_length=128, source="title")
        publishDate = serializers.DateTimeField()
        price = serializers.DecimalField(max_digits=5, decimal_places=2)
        # source也可以用于ForeignKey字段
        publish = serializers.CharField(max_length=32, source="publish.name")
        authors = serializers.CharField(max_length=32)
    

    那么多对多字段如何处理?如果将source参数定义为"authors.all",那么取出来的结果将是一个QuerySet,这样的数据对前端并不是很友好,所以我们可以使用序列化器嵌套,或者这样:

    class BookSerializer(serializers.Serializer):
        title = serializers.CharField(max_length=32)
        price = serializers.DecimalField(max_digits=5, decimal_places=2)
        publishDate = serializers.DateField()
        publish = serializers.CharField()
        publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name')
        publish_email = serializers.CharField(max_length=32, read_only=True, source='publish.email')
        # authors = serializers.CharField(max_length=32, source='authors.all')
        authors_list = serializers.SerializerMethodField()
    
        def get_authors_list(self, authors_obj):
            authors = list()
            for author in authors_obj.authors.all():
                authors.append(author.name)
    
            return authors
    

    注意,get_必须与字段名称一致,否则会报错。

    # -*- coding: utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    # 当前app中的模块
    from .models import Book
    from .app_serializer import BookSerializer
    
    # Create your views here.
    
    
    class BookView(APIView):
        def get(self, request):
            origin_books = Book.objects.all()
            serialized_books = BookSerializer(origin_books, many=True)
    
            return Response(serialized_books.data)
    
        def post(self, request):
            verified_data = BookSerializer(data=request.data)
    
            if verified_data.is_valid():
                book = verified_data.save()
                # 可写字段通过序列化添加成功之后需要手动添加只读字段
                authors = Author.objects.filter(nid__in=request.data['authors'])
                book.authors.add(*authors)
    
                return Response(verified_data.data)
            else:
                return Response(verified_data.errors)
    

    POST接口的实现步骤:
    1. url定义:需要为post新增url,根据规范,url定位资源,http请求方式定义用户行为

    2. 定义post方法:在视图类中定义post方法

    3. 开始序列化:通过我们定义的序列化类,创建一个序列化对象,传入参数data=request.data(application/json)数据

    4. 校验数据:通过实例对象的is_valid()方法,对请求数据的合法性进行校验

    5. 保存数据:调用save()方法,将数据插入数据库

    6. 插入数据到多对多关系表:如果有多对多字段,手动插入数据到多对多关系表

    7. 返回:将插入的对象返回

      因为多对多字关系字段是沃恩自定义的,而且必须这样定义,返回的数据才有意义,而用户插入数据的时候,serrializers.Serializer没有实现create,我们必须手动插入数据,像这样:

      # 第二步, 创建一个序列化类,字段类型不一定要跟models的字段一致
    class BookSerializer(serializers.Serializer):
      # nid = serializers.CharField(max_length=32)
        title = serializers.CharField(max_length=128)
        price = serializers.DecimalField(max_digits=5, decimal_places=2)
        publish = serializers.CharField()
        # 外键字段, 显示__str__方法的返回值
        publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name')
        publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city')
        # authors = serializers.CharField(max_length=32) # book_obj.authors.all()
      
        # 多对多字段需要自己手动获取数据,SerializerMethodField()
        authors_list = serializers.SerializerMethodField()
      
        def get_authors_list(self, book_obj):
            author_list = list()
      
            for author in book_obj.authors.all():
                author_list.append(author.name)
      
            return author_list
      
         def create(self, validated_data):
            # {'title': 'Python666', 'price': Decimal('66.00'), 'publish': '2'}
             validated_data['publish_id'] = validated_data.pop('publish')
             book = Book.objects.create(**validated_data)
             return book
      
          def update(self, instance, validated_data):
              # 更新数据会调用该方法
              instance.title = validated_data.get('title', instance.title)
              instance.publishDate = validated_data.get('publishDate', instance.publishDate)
              instance.price = validated_data.get('price', instance.price)
              instance.publish_id = validated_data.get('publish', instance.publish.nid)
      
              instance.save()
      
              return instance
  这样就比较复杂,那么我们**如何让序列化类自动插入数据**?如果字段很多,那么写序列化类也会成为一种负担,有没有更加简单的方式呢?

  ~~~python
  class BookSerializer(serializers.ModelSerializer):
      class Meta:
          model = Book
  
          fields = ('title',
                    'price',
                    'publish',
                    'authors',
                    'author_list',
                    'publish_name',
                    'publish_city'
                    )
          extra_kwargs = {
              'publish': {'write_only': True},
              'authors': {'write_only': True}
          }
  
      publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name')
      publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city')
  
      author_list = serializers.SerializerMethodField()
  
      def get_author_list(self, book_obj):
          # 拿到queryset开始循环 [{}, {}, {}, {}]
          authors = list()
  
          for author in book_obj.authors.all():
              authors.append(author.name)
  
          return authors
  ~~~

  ##### 我们继承了ModelSerializer而不是Serializer,**ModelSerializer对Serializer进行了扩展,添加了以下功能:**

  - **基于model自动创建一些字段**

  - **会自动生成一些验证,比如unique_together验证**

  - **包含了简单的默认的create()和updata()**

  ~~~python
  class BookSerializer(serializers.ModelSerializer):
      class Meta:
          model = Book
          fields = ('title', 'price',)
          # fields = "__all__"
  ~~~
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342