DRF----------(三)

一.配置文件settings.py  (连接Mysql)

INSTALLED_APPS = [
    # ...
    'rest_framework',
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'dg_proj',
        'USER': 'root',
        'PASSWORD': 'root',
    }
}
"""
任何__init__文件
import pymysql
pymysql.install_as_MySQLdb()
"""

LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

二.路由分发

#
from django.conf.urls import url, include
from django.contrib import admin
from django.views.static 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}),
]

# 子(app中)
from django.conf.urls import url
from . import views
urlpatterns = [
    
]

   函数传参回顾  (位置参数 ------ 默认参数 ------    *   ------关键字参数 (有值/无值)  )

# * 前都是位置参数:无值位置必须赋值,有值位置可以不要赋值,必须在无值位置之后
# * 后都是关键字参数:无值关键字必须赋值,有值关键字可以不要赋值,都是指名道姓传参,所以顺序任意
# * 可以紧跟一个变量,用来接收所有未接收完的位置参数
def fn(a, b, c=0, *, d=0, x):
    print(a)
    print(b)
    print(c)
    print(d)
    print(x)

fn(10, 20, 30, x=30, d=100)

三.特殊注释

# TODO 今天做到这了,结合TODO控制台可以完成快速定位

# 假设a就是str类型,实际操作值不是0,而是不能明确标识的字符串
a = 0  # type: str
# 再书写 a. 就可以提示 str 方法

x, y = 0, 0  # type: str, dict

def z(a:str, b:dict):
    pass

def fn(a, b):
    """
    :param str a:
    :param dict b:
    :return:
    """

四.多表设计

"""
Book表
    name、price、img、authors、publish、is_delete、create_time

Publish表
    name、address、is_delete、create_time
    
Author表:
    name、age、is_delete、create_time

AuthorDetail表
    mobile, author、is_delete、create_time
    
    
BaseModel基表
    is_delete、create_time
上面四表继承基表,可以继承两个字段
"""

表关系:

注: 实际开发中要去除外键关联,用逻辑建立外键关系,但不影响orm操作

 

基表:utils/model.py

from django.db import models

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

多表关系:app/models.py

"""
多表关系总结
一对一:Author、AuthorDetail两表
1)关系字段放在AuthorDetail表中:作者删除详情也随即删除,详情删除作者仍保留
2)作者找详情用 外键related_name(detail),或者表名小写(authordetail);详情找作者用 外键字段(author)

3)db_constraint断开表关联,on_delete规定逻辑关联删除动作,models.CASCADE级联删除 author = models.OneToOneField(to='Author', null=True, related_name='detail', db_constraint=False, on_delete=models.CASCADE )
一对多:Book、Publish两表 1)关系字段放在Book表中(多的一方):出版社删除书外键不动,书删除没有任何影响 2)出版社找书用 外键related_name(books),或者通过表名小写_set(book_set.all());书找出版社 外键字段(publish) 3)db_constraint断开表关联,on_delete规定逻辑关联删除动作,models.DO_NOTHING关联无动作 publish = models.ForeignKey(to='Publish', null=True, related_name='books', db_constraint=False, on_delete=models.DO_NOTHING, ) 扩展:外键字段的其它关联方式 1)断关联,删除关联表记录,外键值置空 db_constraint=False, on_delete=models.SET_NULL, null=True, 2)断关联,删除关联表记录,外键值置默认值 db_constraint=False, on_delete=models.SET_DEFAULT, default=1,
多对多:Book、Author两表 1)关系字段放在任意一方都可以:出版社删除或书删除彼此不影响,但关系表一定级联删除 2)正向找 外键字段,反向找 外键字段related_name 3)db_constraint断开表关联,on_delete不存在(不设置,本质在第三张表中设置,且一定是级联) authors = models.ManyToManyField(to='Author', null=True, related_name='books', db_constraint=False, ) """
from django.db import models

# 多表
# 书表 出版社表 作者表
"""
Book表: name、price、img、authors、publish、is_delete、create_time
Publish表: name、address、is_delete、create_time
Author表: name、age、is_delete、create_time
AuthorDetail表: mobile, author、is_delete、create_time
"""

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,
        # on_delete=models.SET_NULL, null=True,
        # on_delete=models.SET_DEFAULT, default=1,
    )
    authors = models.ManyToManyField(to='Author', null=True,
        related_name='books',
        db_constraint=False,
    )
    class Meta:
        db_table = 'old_boy_book'
        verbose_name = '书籍'
        verbose_name_plural = verbose_name
    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()
    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)
    # related_name 反向查询的 字段  eg:author_obj.detail.mobile
    # 一对一外键:放在拆出来的表,断关联,设置级联
    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
测试代码:script/t_dg.py
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dg_proj.settings")
import django
django.setup()

from api import models

# 一对一
# author = models.Author.objects.first()  # type: models.Author
# print(author.name)
# print(author.detail.mobile)
# author.delete()

# author_detail = models.AuthorDetail.objects.first()  # type: models.AuthorDetail
# print(author_detail.mobile)
# print(author_detail.author.name)
# author_detail.delete()


# 一对多
# publish = models.Publish.objects.filter(pk=2).first()  # type: models.Publish
# print(publish.name)
# print(publish.books.first().name)
# publish.delete()

# publish = models.Publish.objects.filter(is_delete=False).first()  # type: models.Publish
# if publish:
#     print(publish.name)
#     print(publish.books.first().name)
#     publish.is_delete = True
#     publish.save()

# book = models.Book.objects.first()  # type: models.Book
# print(book.name)
# print(book.publish.name)
# book.delete()


# 多对多
# book = models.Book.objects.first()  # type: models.Book
# print(book.name)
# print(book.authors.first().name)
# book.delete()

author = models.Author.objects.first()  # type: models.Author
print(author.name)
print(author.books.first().name)
author.delete()

五. 简单认识序列化

  a. 序列化类:api/serializers.py
序列化总结:
1)在自定义的ModelSerializer类中设置class Meta
    model 绑定序列化相关的模型类
    fields 插拔方式指定序列化字段
2)在模型类中通过 方法属性 自定义跨表查询的字段,在fields中插拔
3)如果就使用外键字段完成连表深度查询,用序列化深度
    外键字段 = 外键序列化类(many=True|False)
4) 了解:在ModelSerializer中不建议使用,如何书写了必须在fields中声明使用
     p_n = serializers.SerializerMethodField()
     def get_p_n(self, obj: models.Book):
        return obj.publish.name
 5)   fields = '__all__'  # 所有字段
      exclude = ('id', 'is_delete')  # 刨除某些字段,与__all__不可联用
      depth = 1  # 跨表自动深度(展示外键表的所有字段)
 
from rest_framework import serializers
from . import models


class PublishModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Publish
        fields = ('name', 'address')
class AuthorModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.Author
        fields = ('name', 'age', 'mobile')
class BookModelSerializer(serializers.ModelSerializer):
    # 正向序列化深度
    # (不建议:必须用外键名才需要序列化深度,
    # 建议:自定义外键序列化名,在model类中插拔字段(属性方法))
    publish = PublishModelSerializer()
    authors = AuthorModelSerializer(many=True)

    # 了解:在ModelSerializer中不建议使用,如何书写了必须在fields中声明使用
    # p_n = serializers.SerializerMethodField()
    # def get_p_n(self, obj: models.Book):
    #     return obj.publish.name

    class Meta:
        model = models.Book
        fields = ('name', 'price', 'img', 'publish_name', 'authors_info', 'publish', 'authors')

        # 了解
        # fields = '__all__'  # 所有字段
        # # exclude = ('id', 'is_delete')  # 刨除某些字段
        # depth = 1  # 跨表自动深度(展示外键表的所有字段)
    b. 序列化视图类:api/views.py
from rest_framework.views import APIView
from utils.response import APIResponse
from . import models, serializers
class BookAPIView(APIView):
    def get(self, request, *args, **kwargs):
        book_query = models.Book.objects.all()
        book_ser = serializers.BookModelSerializer(book_query, many=True)
        return APIResponse(0, 'ok', results=book_ser.data)

六.简单认识反序列化

 a.反序列化模型类:api/models.py

# 自定义model类的方法属性,完成插拔式跨表查询
class Book(BaseModel):
    # ...
    @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
    
class Author(BaseModel):
    # ...
    @property
    def mobile(self):
        return self.detail.mobile
b.反序列化类:api/serializers.py

反序列化总结:
1)在自定义的ModelSerializer类中设置class Meta
    model 绑定反序列化相关的模型类
    fields 插拔方式指定反序列化字段
    extra_kwargs 定义系统校验字段的规则
2)可以自定义局部钩子和全局钩子完成字段的复杂校验规则
3)不需要重写create和update完成增加修改,ModelSerializer类已经帮我们实现了
class BookModelDeserializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = ('name', 'price', 'publish', 'authors')
        extra_kwargs = {
            'name': {
                'min_length': 3,
                'error_messages': {
                    'min_length': '太短'
                }
            },
            'publish': {
                'required': True
            },
            'authors': {
                'required': 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
post发送数据:

 

{

        "name": "赵是孤儿",
        "price": 12.22,
        "publish":1,
        "authors":[1,2]

  
    }

 

 

 

c.序列化视图类:api/views.py
from rest_framework.views import APIView
from utils.response import APIResponse

from . import models, serializers
class BookAPIView(APIView):
    def get(self, request, *args, **kwargs):
        book_query = models.Book.objects.all()
        book_ser = serializers.BookModelSerializer(book_query, many=True)
        return APIResponse(0, 'ok', results=book_ser.data)

    def post(self, request, *args, **kwargs):
        book_ser = serializers.BookModelDeserializer(data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return APIResponse(0, 'ok')
        else:
            return APIResponse(1, '添加失败', results=book_ser.errors)

七.序列化与反序列化共存(重点)

整合:
        1)extra_kwargs 中通过 write_only(反序列化) 与 read_only(序列化) 区别
        2)与数据库关联的反序列化字段不要自定义,序列化字段可以任意自定义
        3)参与反序列化,但不参与数据入库的字段,需要在序列化类中自定义 (re_password)
                re_name = serializers.CharField(write_only=True)

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

 

  a.路由(app):

from django.conf.urls import url
from . import views
urlpatterns = [
    # ...
    url(r'^v2/books/$', views.BookV2APIView.as_view()),
    url(r'^v2/books/(?P<pk>.*)/$', views.BookV2APIView.as_view()),
]
b.模型层修订:api/models.py (建立书名与出版社的联合唯一)
class Book(BaseModel):
    # ...
    class Meta:
        # ...
        # 联合唯一 => patch结果修改部分数据就会收到'name'、'publish'联合限制
        unique_together = ('name', 'publish')
c.序列化层:api/serializers.py (可在extra_kwargs中指定序列化(read_only)和反序列化(write_only)字段)
class BookV2ModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = ('name', 'price', 'publish', 'authors', 'img', 'publish_name', 'authors_info')
        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
d.视图层:api/views.py
class BookV2APIView(APIView):
    def get(self, request, *args, **kwargs):
        book_query = models.Book.objects.filter(is_delete=False).all()
        book_ser = serializers.BookV2ModelSerializer(book_query, many=True)
        return APIResponse(0, 'ok', results=book_ser.data)

    def post(self, request, *args, **kwargs):
        book_ser = serializers.BookV2ModelSerializer(data=request.data)
        if book_ser.is_valid():
            book_obj = book_ser.save()
            return APIResponse(0, 'ok',
                results=serializers.BookV2ModelSerializer(book_obj).data
            )
        else:
            return APIResponse(1, '添加失败', results=book_ser.errors)

    def patch(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if not pk:
            return APIResponse(1, 'pk error')
        try:
            book_obj = models.Book.objects.get(is_delete=False, pk=pk)
        except:
            return APIResponse(1, 'pk no book')
        book_ser = serializers.BookV2ModelSerializer(partial=True, instance=book_obj, data=request.data)
        if book_ser.is_valid():
            book_obj = book_ser.save()
            return APIResponse(0, 'ok',
                results=serializers.BookV2ModelSerializer(book_obj).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)
        print(book_query)
        if not book_query.update(is_delete=True):  # 受影响的行得大于0
            return APIResponse(1, '删除失败')
        return APIResponse(0, '删除成功')

 注:单删是通过路由有名分组拿到pk值(get请求),群删是发的json数据{ "pk":[2,3] },后端通过request.data获取

 

 

posted @ 2019-09-04 20:38  纵横捭阖行  阅读(223)  评论(0编辑  收藏  举报