django_restfreamwork 2安装配置和序列化组件

Posted on 2020-08-21 19:02  王石头py  阅读(174)  评论(0)    收藏  举报

DRF框架准备

django restframework 框架

mysql

新建库

mysql> create database restframework charset utf8mb4;
Query OK, 1 row affected (0.00 sec)

配置xadmin

安装

pip3 install https://codeload.github.com/sshwsfc/xadmin/zip/django2 -i https://pypi.tuna.tsinghua.edu.cn/simple

注册xadmin

settings.py

INSTALLED_APPS = [
    ...
    'xadmin',
    'crispy_forms',
    'reversion',
    ...
]

# 修改使用中文界面
LANGUAGE_CODE = 'zh-Hans'
#LANGUAGE_CODE = 'en-us'
# 修改时区
TIME_ZONE = 'Asia/Shanghai'
#TIME_ZONE = 'UTC'

xadmin数据库迁移

python3 manage.py makemigrations
python3 manage.py migrate

新增xadmin路由

新增总路由

# from django.contrib import admin
# from django.urls import path,include
# 
# from django.urls import re_path
# from django.conf import settings
# from django.views.static import serve

import xadmin
xadmin.autodiscover()
# version模块自动注册需要版本控制的 Model
from xadmin.plugins import xversion
xversion.register_models()


#用xadmin,就不用admin要注释,
urlpatterns = [
#     #path('admin/', admin.site.urls),
#     re_path(r'media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}),
#     path('', include("home.urls")),
   path(r'xadmin/', xadmin.site.urls),
]

创建xadmin超级用户

$ python3 manage.py createsuperuser
用户名 (leave blank to use 'wangjunxiang'): root
电子邮件地址: root@root.com
Password: 123123
Password (again): 123123
密码长度太短。密码必须包含至少 8 个字符。
这个密码太常见了。
密码只包含数字。
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

配置xadminx.py

新建apps/自定义项目app名/adminx.py

  • 注意:配置文件一定叫 adminx.py
import xadmin
from xadmin import views

class BaseSetting(object):
    """xadmin的基本配置"""
    enable_themes = True  # 开启主题切换功能
    use_bootswatch = True

xadmin.site.register(views.BaseAdminView, BaseSetting)

class GlobalSettings(object):
    """xadmin的全局配置"""
    site_title = "luffy"  # 设置站点标题
    site_footer = "luffy"  # 设置站点的页脚
    menu_style = "accordion"  # 设置菜单折叠

xadmin.site.register(views.CommAdminView, GlobalSettings)

# 导入表
from .models import 表1,表2,表3
class 表1_ModelAdmin(object):
    list_display=["表字段1","表字段2","表字段3"]
xadmin.site.register(表1, 表1_ModelAdmin)

class 表2_ModelAdmin(object):
    list_display=["表字段1",]
xadmin.site.register(表2, 表2_ModelAdmin)

class 表3_ModelAdmin(object):
    list_display=["表字段1",]
xadmin.site.register(表3, 表3_ModelAdmin)

设置访问白名单

settings.py中设置为 '*'

ALLOWED_HOSTS = ['*']

数据库录入数据

访问 127.0.0.1:8000/xadmin

INSERT INTO `book` VALUES (1, '无限恐怖', 1, '2020-08-20', 1);
INSERT INTO `book` VALUES (2, '盘龙', 2, '2020-08-20', 2);
INSERT INTO `book` VALUES (3, '射雕英雄传', 3, '2020-08-20', 3);
INSERT INTO `book` VALUES (4, '星辰变与无限空间', 3, '2020-08-20', 3);
INSERT INTO `book` VALUES (5, 'asdadsasdasd', 3, '2020-08-20', 2);
INSERT INTO `book` VALUES (6, 'django', 1, '2020-08-20', 1);

BEGIN;
INSERT INTO `book_author` VALUES (1, 1, 1);
INSERT INTO `book_author` VALUES (2, 2, 3);
INSERT INTO `book_author` VALUES (3, 3, 2);
INSERT INTO `book_author` VALUES (4, 4, 1);
INSERT INTO `book_author` VALUES (5, 4, 3);
INSERT INTO `book_author` VALUES (6, 5, 1);
INSERT INTO `book_author` VALUES (7, 5, 2);
INSERT INTO `book_author` VALUES (8, 5, 3);
INSERT INTO `book_author` VALUES (9, 6, 4);
COMMIT;


BEGIN;
INSERT INTO `publisher` VALUES (1, '集英社');
INSERT INTO `publisher` VALUES (2, '华中出版社');
INSERT INTO `publisher` VALUES (3, '清华大学出版社');
COMMIT;

配置DRF框架

安装DRF

pip3 install djangorestframework==3.11.0

注册DRF

settings.py

INSTALLED_APPS = [
    ....
  	....
  	#导入DRF框架
    'rest_framework',
]

DRF【序列化组件】

序列化:get()前端发起请求,后端给前端返回数据

反序列化:post()前端请求后端并带着数据,后端进行字段校验后,更新数据,返回前端

通用的模型/总路由

总路由做路由分发

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', include("SerDemo.urls")),
]

models表结构

from django.db import models

# Create your models here.

__all__ = ["Book", "Publisher", "Author"]


class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="图书名称")
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = models.IntegerField(choices=CHOICES, verbose_name="图书的类别")
    pub_time = models.DateField(verbose_name="图书的出版日期")

    publisher = models.ForeignKey(to="Publisher", on_delete=None)
    author = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.title

    class Meta:
        db_table = "book"
        verbose_name_plural = "图书表"



class Publisher(models.Model):
    title = models.CharField(max_length=32, verbose_name="出版社的名称")

    def __str__(self):
        return self.title

    class Meta:
        db_table = "publisher"
        verbose_name_plural = "出版社表"


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者的姓名")

    def __str__(self):
        return self.name

    class Meta:
        db_table = "author"
        verbose_name_plural = "作者表"

迁移数据库

python3 manage.py makemigrations
python3 manage.py migrate

继承【Serializer】序列化器实例

1,查看多条数据的接口

设置子路由接口

urls.py

from django.urls import path, include
from .views import BookView

urlpatterns = [
    path('list', BookView.as_view()),
]

继承【APIView】的视图

views.py

from .models import Book, Publisher						#引入表
from rest_framework.views import APIView			#引入DRF框架
from rest_framework.response import Response	#引入DRF返回
from .serializers import BookSerializer				#引入序列化器



class BookView(APIView):

    def get(self, request):
      	"""序列化实例"""
        book_list = Book.objects.all()
        ret = BookSerializer(book_list, many=True)
        return Response(ret.data)


    def post(self, request):
      	"""
      		反序列化实例
      		当序列化器需要保存数据到数据库,需要在序列化器中重写 create方法
      	"""
        print(request.data)
        serializer = BookSerializer(data=request.data)  #传反序列化的数据
        if serializer.is_valid():                       #序列化器对前端传来的数据校验
            serializer.save()                           #验证通过,保存到数据库
            return Response(serializer.validated_data)
        else:
            return Response(serializer.errors)

新建序列化器

新建 serializers.py文件

from rest_framework import serializers
from .models import Book

class PublisherSerializer(serializers.Serializer):
  	"""序列化器嵌套"""
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)

class AuthorSerializer(serializers.Serializer):
  	"""序列化器嵌套"""
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)

class BookSerializer(serializers.Serializer):
    """
        read_only=True 前端对后端发起请求,后端返回数据给前端用,这个字段就定义为 read_only
        write_only=True 前端提交字段修改数据库字段的值用到,让前端提交字段为 w_字段名,而不是数据库原先的字段,需要和前端说好
    """
    id = serializers.IntegerField(required=False)
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True)
    w_category = serializers.ChoiceField(choices=CHOICES, write_only=True)
    pub_time = serializers.DateField()

    # 同理,凡是前端读取的字段都用read_only,凡是前端修改数据的都是write_only
    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)
    author = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    def create(self, validated_data):
        """
            当序列化器对字段校验成功后,保存新的数据,必须要重写create方法,views视图中 serializer.save() 才能不报错
            validated_data:前端传来的数据都在这个里面
        """
        """
        模拟前端发送json数据
        {
            "title": "Alex的使用教程",
            "w_category": 1,
            "pub_time": "2018-10-09",
            "publisher_id": 1,
            "author_list": [1, 2]
        }
        """
        book = Book.objects.create(
            title=validated_data["title"],
            category=validated_data["w_category"],
            pub_time=validated_data["pub_time"],
            publisher_id=validated_data["publisher_id"],
        )
        book.author.add(*validated_data["author_list"]) #.add对author表添加多条记录
        return book

验证

查询所有数据【get请求】

当get请求 http://0.0.0.0:8000/books/list

查看到所有的数据

新增一条数据【post请求】

当给 http://0.0.0.0:8000/books/list 发送post请求的 json 数据,提交成功

json数据如下

{
        "title": "寸芒",
        "w_category": 1,
        "pub_time": "2018-10-09",
        "publisher_id": 2,
        "author_list": [2, 3]
}

2,查看单条数据的接口

设置子路由接口

urls.py 新增路由

查看单条数据需要携带id过来

# from django.urls import path, include
#from .views import BookView
from .views import BookEditView

urlpatterns = [
#     path('list', BookView.as_view()),
    path('retrieve/<int:id>', BookEditView.as_view()),
]

继承【APIView】的视图

views.py 新增一个视图类

from .models import Book
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import BookSerializer

class BookEditView(APIView):
    def get(self, request, id):
        """查看单条数据,前端需要传id进来,get要接收id"""
        book_obj = Book.objects.filter(id=id).first()
        ret = BookSerializer(book_obj)
        return Response(ret.data)

    def put(self, request, id):
        """
            当序列化器修改某一条数据,需要重新写序列化器中 的update方法
        """
        """
            前端put请求携带如下数据修改这条记录
        {
            "title":"神雕侠侣",
        }
        """
        book_obj = Book.objects.filter(id=id).first()
        #第一个参数传需要更新的对象,第二个参数传需要更新的数据,第三个参数partial=True允许进行部分更新
        serializer = BookSerializer(book_obj, data=request.data, partial=True)
        if serializer.is_valid():       #对字段进行验证
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors)

    def delete(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        book_obj.delete()
        return Response("")

修改序列化器函数

serializers.py文件

from rest_framework import serializers
from .models import Book

class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)

class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)

def my_validate(value):
    """
        自定义钩子方法
        例如,传的值不能含有「敏感信息」这4个字符串
    """
    if "敏感信息" in value.lower():
        raise serializers.ValidationError("不能含有敏感信息")   #如果有,抛出异常
    else:
        return value

class BookSerializer(serializers.Serializer):
    """
        read_only=True 前端对后端发起请求,后端返回数据给前端用,这个字段就定义为 read_only
        write_only=True 前端提交字段修改数据库字段的值用到,让前端提交字段为 w_字段名,而不是数据库原先的字段,需要和前端说好
    """
    id = serializers.IntegerField(required=False)
    title = serializers.CharField(max_length=32, validators=[my_validate])  #validators=[] 列表中传入自定义钩子方法
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True)
    w_category = serializers.ChoiceField(choices=CHOICES, write_only=True)
    pub_time = serializers.DateField()

    # 同理,凡是前端读取的字段都用read_only,凡是前端修改数据的都是write_only
    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)
    author = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    def create(self, validated_data):
        """
            当序列化器对字段校验成功后,保存新的数据,必须要重写create方法,views视图中 serializer.save() 才能不报错
            validated_data:前端传来的数据都在这个里面
        """
        """
        模拟前端发送json数据
        {
            "title": "寸芒",
            "w_category": 1,
            "pub_time": "2011-01-09",
            "publisher_id": 2,
            "author_list": [2, 3]
        }
        """
        book = Book.objects.create(
            title=validated_data["title"],
            category=validated_data["w_category"],
            pub_time=validated_data["pub_time"],
            publisher_id=validated_data["publisher_id"],
        )
        book.author.add(*validated_data["author_list"]) #.add对author表添加多条记录
        return book

    def update(self, instance, validated_data):
        """
            instance 就是视图传过来的第一个参数:需要更新的模型对象,比如传过来的book_obj,里面有title,category字段
        """
        instance.title = validated_data.get("title", instance.title) #如果第一个参数title取不到会报错,就给个第二个值不会报错了
        instance.category = validated_data.get("category", instance.category)
        instance.pub_time = validated_data.get("pub_time", instance.pub_time)
        instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)   #还能修改foreignkey
        if validated_data.get("author_list"):
            """用来修改manytomany的字段"""
            # instance就是book_obj,author就是book表中manytomany的字段
            # ORM中manytomany新增是.add(),修改是.set()
            instance.author.set(validated_data["author_list"])
        instance.save()
        return instance

    def validate_title(self, value):
        """
            局部钩子函数:对title字段数据进行校验
            例如:title字段中必须含有python
        """
        if "python" not in value.lower():
            #如果验证失败,则抛出异常
            raise serializers.ValidationError("标题必须含有python")
        return value

    def validate(self, attrs):
        """
            全局钩子函数: 对多个字段进行校验
            例如,当w_category是1(python),publisher是1的时候,验证才通过
            attrs 是字典 {"字段名","值"}
        """
        if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:
            return attrs
        else:
            raise serializers.ValidationError("分类以及标题不符合要求")

序列化器的验证功能

局部钩子函数【权重低】

与自定义钩子函数相比,先进行自定义钩子函数的校验,权重比较低

在序列化器中通过定义 def validate_想要验证的字段名(self, value) 的函数,校验单个定义的字段,来自定义验证方法

class BookSerializer(serializers.Serializer):
    .....

    def create(self, validated_data):
        .....

    def update(self, instance, validated_data):
        ......

        
        
    def validate_title(self, value):
        """
            局部钩子函数:对title字段数据进行校验
            例如:title字段中必须含有python
        """
        if "python" not in value.lower():
            #如果验证失败,则抛出异常
            raise serializers.ValidationError("标题必须含有python")
        return value
全局钩子函数

直接在序列化器中定义 def validate(self, attrs) 函数,用来校验多个字段

class BookSerializer(serializers.Serializer):
    .....

    def create(self, validated_data):
        .....

    def update(self, instance, validated_data):
        .....

    def validate_title(self, value):
        .....

        
    def validate(self, attrs):
        """
            全局钩子函数: 对多个字段进行校验
            例如,当w_category是1(python),publisher是1的时候,验证才通过
            attrs 是字典 {"字段名","值"}
        """
        if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:
            return attrs
        else:
            raise serializers.ValidationError("分类以及标题不符合要求")
自定义钩子函数【权重高】

与局部钩子函数相比,先进行自定义钩子函数的校验,权重高

需要在类外面单独定义个函数 def 自定义函数名(value) ,里面写验证逻辑

某个字段调用这个自定义钩子函数,需要在参数后面加参数 alidators=[] 列表中传入自定义钩子方法

def my_validate(value):
    """
        自定义钩子方法
        例如,传的值不能含有「敏感信息」这4个字符串
    """
    if "敏感信息" in value.lower():
        raise serializers.ValidationError("不能含有敏感信息")   #如果有,抛出异常
    else:
        return value


class BookSerializer(serializers.Serializer):
    .....
    title = serializers.CharField(max_length=32, validators=[my_validate])  #validators=[] 列表中传入自定义钩子方法,代表对这个字段进行校验
    .....

    def create(self, validated_data):
        .....

    def update(self, instance, validated_data):
        .....

    def validate_title(self, value):
        .....

    def validate(self, attrs):
        .....

测试

查看单条数据【get请求】

请求 http://0.0.0.0:8000/books/retrieve/1 接口

修改单条数据【put请求】

请求 http://0.0.0.0:8000/books/retrieve/1 接口

验证单条数据【put请求:序列化器中的局部钩子函数实现】

http://0.0.0.0:8000/books/retrieve/5 接口发起put请求,在钩子函数中会定义内容中是否含有python如果没有则抛出异常

带python的数据才会成功

继承【ModelSerializer】序列化器实例

之前继承 serializers.Serializer 需要写很多字段,很麻烦,为了解决繁琐的代码,不用写表的所有字段都定义了,继承 serializers.ModelSerializer 自动的解决

查看多条数据的接口

设置子路有接口

from django.urls import path, include
from .views import BookView

urlpatterns = [
    path('list', BookView.as_view()),
]

继承【APIView】的视图

from .models import Book
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import BookSerializer

class BookView(APIView):

    def get(self, request):
        book_list = Book.objects.all()
        ret = BookSerializer(book_list, many=True)
        return Response(ret.data)


    def post(self, request):
        """ 当序列化器需要保存数据到数据库,需要在序列化器中重写 create方法 """
        print(request.data)
        serializer = BookSerializer(data=request.data)  #传反序列化的数据
        if serializer.is_valid():                       #序列化器对前端传来的数据校验
            serializer.save()                           #验证通过,保存到数据库
            return Response(serializer.data)
        else:
            return Response(serializer.errors)

序列化器

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    """继承ModelSerializer,序列化器会与models进行结合"""

    class Meta:
        """先对序列化器的model进行配置"""
        model = Book
        fields = "__all__"  # 返回__all__所有字段,或者指定字段["id", "title", "pub_time"]
        # depth = 1       #代表外键嵌套层数为1层,有可能外键关联的表还会外键关联第三张表,这代表停到第一层就停止
        extra_kwargs = {
                    "category": {"write_only": True},   # category 字段不写的话,会返回出去,这个字段不想通过接口提供出去
                    "publisher": {"write_only": True},  # 就写在这里,这个字段作为反序列化post请求过来修改字段用
                    "author": {"write_only": True}
                }

    ############ SerializerMethodField 取出关联表想要的字段,不想要的可以不要 ,下面的变量名会带着值变成返回给前端 ##########
    category_display = serializers.SerializerMethodField(read_only=True)
    # 如果拿出想外键对应表中的字段,不是外键id,下面需要继续定义 def get_<publisher/字段名> 函数,返回想要关联表(外键/manytomany)的字段
    publisher_info = serializers.SerializerMethodField(read_only=True)
    # manytomany字段对应表中的值,下面需要继续定义 def get_<authors/字段名> 函数
    authors = serializers.SerializerMethodField(read_only=True)

    def get_category_display(self,obj):
        return obj.get_category_display()   # orm方法 get_字段_display ,获取chiose中文 ((1,张三),(2,李四))

    def get_publisher_info(self, obj):
        """
            函数以 get_字段命名
            obj是序列化的每个book对象,因为Class meta 中定义了model=book
        """
        publisher_obj = obj.publisher  # 根据publisher外键取 Publisher 表里的值
        return {
            "id": publisher_obj.id,
            "title": publisher_obj.title
        }

    def get_authors(self, obj):
        """
            函数以 get_字段命名
            obj是序列化的每个book对象,因为Class meta 中定义了model=book
        """
        authors_query_set = obj.author.all()
        return [{"id": author_obj.id, "name": author_obj.name} for author_obj in authors_query_set]

测试

查看所有数据【get请求】

当get请求 http://0.0.0.0:8000/books/list

新增一条数据【post请求】

对接口 http://0.0.0.0:8000/books/list post请求 携带数据

不需要重写create方法,自动新增数据

新增如下如下数据

{
        "title": "Alex的使用教程2",
        "category": 1,
        "pub_time": "2018-10-09",
        "publisher": 1,
        "author": [
            1,
            2
        ]
}

Serializer和ModelSerializer区别

Serializer 和 ModelSerializer的区别
https://www.cnblogs.com/xiugeng/p/11460855.html