Django:DjangoRestFramework drf 开发4

8. 序列化的使用

序列化和反序列化在项目中的使用

1. 搭建Django项目

1. 创建Django项目
搭建Django项目
2. 创建子应用

(venv-djdrf) jason93@92 djangodrf % python manage.py startapp booktest

2. 安装和配置

1. 安装第三方包

(venv-djdrf) jason93@92 djangodrf % pip install django
(venv-djdrf) jason93@92 djangodrf % pip install djangorestframework
(venv-djdrf) jason93@92 djangodrf % pip install pymysql

2. 配置

  • settings.py
# 配置子应用
INSTALLED_APPS = [
	......
    'rest_framework',
    'booktest.apps.BooktestConfig',
]

# 注释csrf中间件
MIDDLEWARE = [
	......
    # 'django.middleware.csrf.CsrfViewMiddleware',
    ......
]

# 配置mysql数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_drf',
        'USER': 'root',
        'PASSWORD': '12345678',
        'HOST': 'localhost',
        'PORT': 3306
    }
}
  • 项目__init__.py
import pymysql

pymysql.install_as_MySQLdb()
  • 项目urls.py
from django.urls import path, include

urlpatterns = [
	......
    path('', include('booktest.urls')),  # Django2版本后路由是path匹配
]
  • 终端启动mysql,创建名为 django_drf 的数据库:
jason93@92 ~ % mysql -u root -p12345678  # 进入mysql数据库
......
mysql> create database django_drf charset=utf8;  # 创建数据库,指明编码格式为utf-8
Query OK, 1 row affected, 1 warning (0.01 sec)  # 创建成功
  • 创建所需文件
    • booktest子应用中分别创建 urls.py、serializers.py

3. 项目开发

1. 编写模型类

  • models.py
from django.db import models


class Book(models.Model):
    """ 图书模型类 """
    book_name = models.CharField(max_length=64, unique=True, verbose_name='书名')
    author = models.CharField(max_length=32, verbose_name='作者')
    type = models.CharField(max_length=32, verbose_name='分类')
    read_count = models.IntegerField(default=0, verbose_name='阅读量')
    comment_count = models.IntegerField(default=0, verbose_name='评论量')
    published_date = models.DateField(verbose_name='出版日期')
    date_joined = models.DateField(auto_now_add=True, verbose_name='新增日期')
    is_deleted = models.IntegerField(default=0, verbose_name='是否删除')  # 此处做的是逻辑删除,该字段也可以不设,直接物理删除

    class Meta:
        db_table = 'books'
        verbose_name = '图书表'
        verbose_name_plural = verbose_name
        ordering = ['-date_joined']

    def __str__(self):
        return self.book_name


class Hero(models.Model):
    """ 英雄模型类 """
    name = models.CharField(max_length=32, verbose_name='英雄名')
    gender = models.CharField(max_length=6, choices=(('male', '男'), ('female', '女')), default='male', verbose_name='性别')
    age = models.IntegerField(verbose_name='年龄')
    skill = models.CharField(max_length=1024, verbose_name='技能')
    related_book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='hero', verbose_name='关联图书')  # 外键
    date_joined = models.DateField(auto_now_add=True, verbose_name='新增日期')
    is_deleted = models.IntegerField(default=0, verbose_name='是否删除')

    class Meta:
        db_table = 'heros'
        verbose_name = '英雄表'
        verbose_name_plural = verbose_name
        ordering = ['-date_joined']

    def __str__(self):
        return self.name

2. 迁移模型类

(venv-djdrf) jason93@92 djangodrf % python manage.py makemigrations
Migrations for 'booktest':
  booktest/migrations/0001_initial.py
    - Create model Book
    - Create model Hero
(venv-djdrf) jason93@92 djangodrf % python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, booktest, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying booktest.0001_initial... OK
  Applying sessions.0001_initial... OK

3. 设计序列化器

根据模型类设计序列化器

  • serializers.py
from rest_framework import serializers
from .models import Book, Hero


class BookSerializer(serializers.Serializer):
    """ 图书序列化器 """
    id = serializers.IntegerField(read_only=True, label='图书编号')
    book_name = serializers.CharField(min_length=2, max_length=64, allow_blank=False, help_text='请输入图书名',
                                      error_messages={"min_length": "图书名不能少于2个字", "max_length": "图书名不能多于64个字"})
    author = serializers.CharField(max_length=32)
    type = serializers.CharField(max_length=32)
    read_count = serializers.IntegerField(default=0, required=False)
    comment_count = serializers.IntegerField(default=0, required=False)
    published_date = serializers.DateField()
    date_joined = serializers.DateField(read_only=True)
    is_deleted = serializers.IntegerField(default=0)
    hero = serializers.StringRelatedField(read_only=True, many=True)  # 英雄模型类中定义外键时指定related_name,book序列化器可引用

    def validate_book_name(self, attr):
        """ 验证图书名是否唯一 """
        # 查询数据库是否由该图书
        if Book.objects.filter(book_name=attr).first():
            # 存在则抛出异常信息
            raise serializers.ValidationError("该图书已存在")
        # 不存在则把attr返回
        return attr

    def validate(self, attrs):
        """ 验证阅读量是否比评论量多 """
        # 分别从attrs(字典)中获取阅读量和评论量
        read_count = attrs.get('read_count')
        comment_count = attrs.get('comment_count')
        # 由于阅读量和评论量都不是必需参数,所以判断用户是否把阅读量和评论量都上传
        if all([read_count, comment_count]):
            # 如果阅读量小于评论量则抛出异常信息,否则返回attrs
            if read_count < comment_count:
                raise serializers.ValidationError("阅读量不能低于评论量")
        return attrs

    def create(self, validated_data):
        """ 创建 """
        # 使用orm创建图书并返回
        return Book.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """ 修改 """
        # instance 是被修改都图书对象,validated_data是接收到的需要修改的数据字典
        instance.book_name = validated_data.get('book_name')
        instance.author = validated_data.get('author')
        instance.type = validated_data.get('type')
        instance.read_count = validated_data.get('read_count')
        instance.comment_count = validated_data.get('comment_count')
        instance.published_date = validated_data.get('published_date')
        instance.is_deleted = validated_data.get('is_deleted')
        # 修改完成后保存
        instance.save()
        # 把保存后的图书对象返回
        return instance


class HeroSerializer(serializers.Serializer):
    """ 英雄序列化器 """
    id = serializers.IntegerField(read_only=True, label='英雄编号')
    name = serializers.CharField(max_length=32)
    gender = serializers.CharField(max_length=6, default='male')
    age = serializers.IntegerField()
    skill = serializers.CharField(max_length=1024)
    related_book_id = serializers.IntegerField(required=True)  # 外键
    date_joined = serializers.DateField(read_only=True)
    is_deleted = serializers.IntegerField(default=0)

    def create(self, validated_data):
        """ 创建 """
        return Hero.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """ 修改 """
        instance.name = validated_data.get('name')
        instance.gender = validated_data.get('gender')
        instance.age = validated_data.get('age')
        instance.skill = validated_data.get('skill')
        instance.related_book_id = validated_data.get('related_book_id')
        instance.is_deleted = validated_data.get('is_deleted')
        instance.save()
        return instance

4. 编写视图函数

  • views.py

    • 先看一下视图函数的大致结构:
from django.views import View
from django.http import JsonResponse
from .models import Book, Hero
from .serializers import BookSerializer, HeroSerializer
import json


class BooksView(View):
    """ 图书类视图 """

    def get(self, request):
        """ 获取所有图书信息 """
        # 接收
        # 验证
        # 处理
        # 响应
        pass

    def post(self, request):
        """ 新增图书 """
        # 接收
        # 验证
        # 处理
        # 响应
        pass


class BookView(View):
    """ 图书类视图(带主键) """

    def get(self, request, pk):
        """ 根据主键pk获取一个图书的信息 """
        # 接收
        # 验证
        # 处理
        # 响应
        pass

    def put(self, request, pk):
        """ 根据主键pk修改一个图书的信息 """
        # 接收
        # 验证
        # 处理
        # 响应
        pass

    def delete(self, request, pk):
        """ 根据主键pk删除一个图书 """
        # 接收
        # 验证
        # 处理
        # 响应
        pass


class HerosView(View):
    """ 英雄类视图 """

    def get(self, request):
        """ 获取所有英雄信息 """
        # 接收
        # 验证
        # 处理
        # 响应
        pass

    def post(self, request):
        """ 新增英雄 """
        # 接收
        # 验证
        # 处理
        # 响应
        pass


class HeroView(View):
    """ 英雄类视图(带主键) """

    def get(self, request, pk):
        """ 根据主键获取英雄信息 """
        # 接收
        # 验证
        # 处理
        # 响应
        pass

    def put(self, request, pk):
        """ 根据主键修改英雄信息 """
        # 接收
        # 验证
        # 处理
        # 响应
        pass

    def delete(self, request, pk):
        """ 根据主键删除英雄信息 """
        # 接收
        # 验证
        # 处理
        # 响应
        pass
        

具体的实现如下:

from django.views import View
from django.http import JsonResponse
from .models import Book, Hero
from .serializers import BookSerializer, HeroSerializer
import json


class BooksView(View):
    """ 图书类视图 """

    def get(self, request):
        """ 获取所有图书信息 """
        # 接收
        # 验证
        # 处理
        book = BookSerializer(Book.objects.all(), many=True).data
        # 响应
        return JsonResponse(book, safe=False)

    def post(self, request):
        """ 新增图书 """
        # 接收
        params = json.loads(request.body.decode())
        # 验证:验证通过则保存并返回序列化后的数据;验证失败返回错误信息
        serializer = BookSerializer(data=params)
        if serializer.is_valid():
            # 处理:保存并创建book对象
            book = serializer.save()
            # 响应
            return JsonResponse(BookSerializer(book).data, status=201)
        else:
            return JsonResponse(serializer.errors)


class BookView(View):
    """ 图书类视图(带主键) """

    def get(self, request, pk):
        """ 根据主键pk获取一个图书的信息 """
        # 接收、验证
        try:
            book = Book.objects.get(pk=pk)
        except Exception as e:
            return JsonResponse({"error": str(e)})
        # 处理、响应
        return JsonResponse(BookSerializer(book).data)

    def put(self, request, pk):
        """ 根据主键pk修改一个图书的信息 """
        # 接收
        params = json.loads(request.body.decode())
        try:
            book_obj = Book.objects.get(pk=pk)
        except Exception as e:
            return JsonResponse({"error": str(e)})
        else:
            # 验证
            serializer = BookSerializer(book_obj, data=params)
            if serializer.is_valid():
                # 处理
                book = serializer.save()
                # 响应
                return JsonResponse(BookSerializer(book).data, status=201)
            else:
                return JsonResponse(serializer.errors)

    def delete(self, request, pk):
        """ 根据主键pk删除一个图书 """
        # 接收、验证、处理
        try:
            Book.objects.get(pk=pk).delete()
            # 响应
            return JsonResponse({}, status=204)
        except Exception as e:
            return JsonResponse({"error": str(e)})


class HerosView(View):
    """ 英雄类视图 """

    def get(self, request):
        """ 获取所有英雄信息 """
        # 接收
        # 验证
        # 处理
        hero = HeroSerializer(Hero.objects.all(), many=True).data
        # 响应
        return JsonResponse(hero, safe=False)

    def post(self, request):
        """ 新增英雄 """
        # 接收
        params = json.loads(request.body.decode())
        # 验证
        serializer = HeroSerializer(data=params)
        if serializer.is_valid():
            # 处理
            hero = serializer.save()
            # 响应
            return JsonResponse(HeroSerializer(hero).data, status=201)
        else:
            return JsonResponse(serializer.errors)


class HeroView(View):
    """ 英雄类视图(带主键) """

    def get(self, request, pk):
        """ 根据主键获取英雄信息 """
        # 接收、验证
        try:
            hero = Hero.objects.get(pk=pk)
        except Exception as e:
            return JsonResponse({"error": str(e)})
        # 处理、响应
        return JsonResponse(HeroSerializer(hero).data)

    def put(self, request, pk):
	    """ 根据主键修改英雄信息 """
	    # 接收
	    params = json.loads(request.body.decode())
	    try:
	        hero_obj = Hero.objects.get(pk=pk)
	    except Exception as e:
	        return JsonResponse({"error": str(e)})
	    else:
	        # 验证
	        serializer = HeroSerializer(hero_obj, data=params)
	        if serializer.is_valid():
	            # 处理
	            hero = serializer.save()
	            # 响应
	            return JsonResponse(HeroSerializer(hero).data, status=201)
	        else:
	            return JsonResponse(serializer.errors)

    def delete(self, request, pk):
        """ 根据主键删除英雄信息 """
        # 接收、验证、处理
        try:
            Hero.objects.get(pk=pk).delete()
            # 响应
            return JsonResponse({}, status=204)
        except Exception as e:
            return JsonResponse({"error": str(e)})

5. 配置子路由

  • urls.py
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^books/$', views.BooksView.as_view()),
    url(r'^books/(?P<pk>\d+)/$', views.BookView.as_view()),
    url(r'^heros/$', views.HerosView.as_view()),
    url(r'^heros/(?P<pk>\d+)/$', views.HeroView.as_view()),
]

6. 测试项目

这里使用一款现阶段依旧很流行的测试软件:postman,可以发送GET、POST、PUT、DELETE等多种请求方式。

  • demo1:测试新增图书
    新增图书

  • demo2:测试查询图书(查询所有:127.0.0.1:8000/books/)
    测试查询图书1

  • demo3:测试查询图书(指定主键pk查询:127.0.0.1:8000/books/1/) 查询图书2

  • demo4:再新增一个查所有:

     {
         "book_name": "神雕侠侣",
         "author": "金庸",
         "type": "古装武侠剧",
         "read_count": 5,
         "published_date": "1986-05-02",
         "is_deleted": 0
     }
    

在这里插入图片描述

  • demo5:根据主键pk值修改
    修改
    可以看到状态是201,这时再查询pk=2的图书信息:
    查询
    修改成功

  • demo6:删除图书对象
    删除图书对象
    可以看到返回状态码是204,返回信息是个空字典,这也是我们在代码中设计的。
    这时再查看所有的图书:
    查询所有图书
    发现id=2的图书没有了,说明删除成功。

以下测试一些错误尝试:

  • demo1:创建已经存在的图书:
    在这里插入图片描述

  • demo2:查询刚才被删掉id=2的图书:
    查询不存在的图书
    我们把捕捉到的错误信息转换成字符串作为error的值。翻译为:图书匹配查询不存在

  • demo3:修改一个不存在的图书

    • 这里要说明一下,put方法中我们把 Book.objects.get(pk=pk) 作为异常处理的对象放到try内所以不存在的时候会报错,这儿不能使用filter()方法,因为即使查询不到也不会报错而是返回空。

    修改不存在的图书
    提示查询结果不存在,即id=2的图书不存在。

  • demo4:删除不存在的图书对象
    删除不存在的图书
    也是报查询结果不存在。

英雄类的测试

暂时就不展示了,有兴趣的Partner可以自己测试一下,而且还可以指定关联图书的ID获取关系属性值。视图函数中我设置返回的是str方法中返回的内容,这个比较直观。

如有不足,欢迎指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员老五

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值