C:\Users\windows\444\dev08\dev08\urls.py
"""dev08 URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include, re_path
from rest_framework.documentation import include_docs_urls
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
schema_view = get_schema_view(
openapi.Info(
title="Lemon API接口文档平台",
default_version='v1',
description="这是一个美轮美奂的接口文档",
terms_of_service="http://api.keyou.site",
contact=openapi.Contact(email="keyou100@qq.com"),
license=openapi.License(name="BSD License"),
),
public=True,
)
urlpatterns = [
path('', include('projects.urls')),
path('interfaces/', include('interfaces.urls')),
path('docs/', include_docs_urls(title='测试平台接口文档', description='这是一个优雅的API接口文档平台')),
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
C:\Users\windows\444\dev08\dev08\settings.py
"""
Django settings for dev08 project.
Generated by 'django-admin startproject' using Django 3.2.5.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = 'django-insecure-n0emb&jl!nt1t(z3&uz2oei-+n2e74z=53wfo*8t-g4czv5m4)'
DEBUG = True
ALLOWED_HOSTS = []
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'drf_yasg',
'projects',
'users',
'interfaces',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'dev08.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'dev08.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
},
'db': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
},
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['rest_framework.filters.SearchFilter', 'rest_framework.filters.OrderingFilter'],
'DEFAULT_PAGINATION_CLASS': "utils.pagination.PageNumberPagination",
'PAGE_SIZE': 4,
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}
C:\Users\windows\444\dev08\projects\views_bk.py
import json
from django.http import HttpResponse, JsonResponse, Http404
from django.views import View
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework import renderers
from rest_framework import filters
from .models import Projects
from . import serializers
from utils.pagination import PageNumberPagination
class ProjectView(GenericAPIView):
"""
可以继承DRF中的APIView视图
1.APIView为View的子类
2.每一个实例方法的第二个参数为Request对象
3.Request在Django的HttpRequest之上做了拓展
》与HttpRequest中解析参数的方法完全兼容
》解析查询字符串参数:GET -> query_params
》解析application/x-www-form-urlencoded参数:POST -> data
》解析application/json参数:body -> data
》解析multipart/form-data参数:POST、FILES -> data
4.提供了认证、授权、限流功能
5.返回DRF中的Response
》为HttpResponse子类
》可以自动根据请求头中的Accept字段,自动返回相应格式的数据
》data接受序列化输出的数据(字典、嵌套字典的列表)
》status接受相应状态码
》headers修改响应头信息(字典)
6.解析器类
》提供针对请求头中Content-Type参数,自动解析请求参数
》默认的解析器有三种:JSONParser(application/json)、FormParser(application/x-www-form-urlencoded)、
MultiPartParser(multipart/form-data)
》有两种方式可以修改使用的解析器类:
方式一:全局settings.py中REST_FRAMEWORK -> DEFAULT_PARSER_CLASSES中指定需要使用的解析器类
方式二:在具体某个类视图中指定parser_classes类属性(列表),优先级高于方式一
7.渲染器类
》提供针对请求头中Accept参数,自动选择需要的渲染器,将数据以特定的格式返回
》默认的渲染器类有二种:JSONRenderer(application/json)、BrowsableAPIRenderer(text/html)
如果未指定Accept或者指定的Accept不为ext/html,默认返回json数据
》有两种方式可以修改使用的解析器类:
方式一:全局settings.py中REST_FRAMEWORK -> DEFAULT_RENDERER_CLASSES中指定需要使用的渲染器类
方式二:在具体某个类视图中指定renderer_classes类属性(列表),优先级高于方式一
8.GenericAPIView类视图
》是APIView的子类,继承了APIView所有功能(认证、授权、限流、Request、Response、解析器、渲染器)
》提供了获取列表数据的相关功能(过滤、排序、分页)
》往往需要重写queryset类属性(定义当前类视图操作的查询集对象)
》往往需要重写serializer_class类属性(定义了当前类视图使用的序列化器类)
》使用get_queryset()方法获取queryset类属性、使用get_serializer()方法获取serializer_class类属性
》提供了get_object()方法,获取某一个模型对象
》在定义url路由时,指定接收主键值的关键字参数名称,默认为pk,如果不为pk的话,必须重写lookup_url_kwarg
》一般lookup_field类属性不需要修改(默认为pk),指定的是过滤模型对象时,使用的关键词参数名称
9.实现搜索过滤功能
》指定过滤引擎,有两种方式
方式一:在全局settings.py中指定DEFAULT_FILTER_BACKENDS(列表,指定过滤引擎的绝对路径字符串)
方式二:在类视图中filter_backends类属性(列表,指定过滤引擎的引用),优先级高于全局
》必须指定search_fields类属性,定义待校验的字段(模型类中的字段名字符串),默认忽略大小写的包含过滤
'^': 'istartswith',
'=': 'iexact',
'@': 'search',
'$': 'iregex',
》如果不指定search_fields类属性,不会进行搜索过滤
》调用视图的filter_queryset()方法,对查询集进行过滤,需要接收queryset查询集参数
》在全局settings.py中使用SEARCH_PARAM参数指定前端过滤
10.实现排序过滤功能
》指定过滤引擎,有两种方式
方式一:在全局settings.py中指定DEFAULT_FILTER_BACKENDS(列表,指定过滤引擎的绝对路径字符串)
方式二:在类视图中filter_backends类属性(列表,指定过滤引擎的引用),优先级高于全局
》必须指定ordering_fields类属性,定义支持排序的字段(模型类中的字段名字符串)
》如果不指定ordering_fields类属性,那么支持模型类中所有的字段进行过滤
》调用视图的filter_queryset()方法,对查询集进行过滤,需要接收queryset查询集参数
》在全局settings.py中使用ORDERING_PARAM参数指定前端过滤
11.实现分页功能
》指定分页引擎类,有两种方式
方式一:在全局settings.py中指定DEFAULT_PAGINATION_CLASS(指定分页引擎的绝对路径字符串)
方式二:在类视图中pagination_class类属性(指定分页引擎的引用),优先级高于全局
》必须在settings.py中DEFAULT_PAGINATION_CLASS指定PAGE_SIZE参数(指定默认每一页显示的数据条数)或者
在自定义分页引擎类指定page_size参数,如果未指定,那么分页功能不会开启
》在获取列表数据接口实例方法中调用paginate_queryset方法,需要接收queryset查询集对象,会返回嵌套page对象的列表
》调用get_paginated_response方法将分页数据返回,需要接收serializer.data参数
》如果需要开启前端能够指定获取每一页的数据条数,往往需要重写分页引擎类PageNumberPagination
》page_size指定默认每一页数据条数
》page_query_param设置前端指定页码的查询字符串参数名称
》page_query_description,对前端指定页码的查询字符串参数的中文描述
》必须重写page_size_query_param类属性,才能开启前端能够指定获取每一页的数据条数的功能
》page_size_query_description是page_size_query_param类属性的中文描述
》max_page_size指定前端每一页数据最大条数的最大值
》如果需要定制分页数据的返回,那么就需要重写get_paginated_response方法
"""
queryset = Projects.objects.all()
serializer_class = serializers.ProjectModelSerializer
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ['=name', '^leader']
def get(self, request):
queryset = self.get_queryset()
queryset = self.filter_queryset(queryset)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(instance=page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(instance=queryset, many=True)
return Response(serializer.data)
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=201)
class ProjectDetailView(GenericAPIView):
queryset = Projects.objects.all()
serializer_class = serializers.ProjectModelSerializer
def get(self, request, pk):
obj = self.get_object()
serializer = self.get_serializer(instance=obj)
return Response(serializer.data)
def put(self, request, pk):
obj = self.get_object()
serializer = self.get_serializer(instance=obj, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=201)
def delete(self, request, pk):
obj = self.get_object()
obj.delete()
return Response(None, status=204)
C:\Users\windows\444\dev08\projects\views.py
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework import filters
from rest_framework import status
from rest_framework import mixins
from rest_framework import generics
from rest_framework import viewsets
from .models import Projects
from . import serializers
from utils.pagination import PageNumberPagination
"""
XxxMixin拓展类
获取列表数据: ListModelMixin拓展类 -> list action方法
创建数据: CreateModelMixin拓展类 -> create action方法
获取详情数据: RetrieveModelMixin拓展类 -> retrieve action方法
更新数据: UpdateModelMixin拓展类 -> update action(完整更新)、partial_update action方法(部分更新)
删除数据: DestroyModelMixin拓展类 -> destroy action方法
XxxXxxAPIView具体的通用视图
获取列表数据: ListAPIView -> 继承ListModelMixin、GenericAPIView
创建数据: CreateAPIView -> 继承CreateModelMixin、GenericAPIView
获取详情数据: RetrieveAPIView -> 继承RetrieveModelMixin、GenericAPIView
更新数据: UpdateAPIView -> 继承UpdateModelMixin、GenericAPIView
删除数据: DestroyAPIView -> 继承DestroyModelMixin、GenericAPIView
获取列表数据、创建数据:ListCreateAPIView
获取数据详情、更新数据:RetrieveUpdateAPIView
获取数据详情、删除数据:RetrieveDestroyAPIView
获取数据详情、更新数据、删除数据:RetrieveUpdateDestroyAPIView
视图集
1.如果需要实现在定义路由条目时,请求方法与要调用的action方法进行一一对应,必须得继承ViewSetMixin
2.ViewSet继承了ViewSetMixin、APIView,具备请求方法与要调用的action方法进行一一对应功能、以及认证授权限流功能,
但是不支持Mixin,因为没有提供get_object()、get_queryset()、geat_serializer()等方法
3.GenericViewSet继承了ViewSetMixin、GenericAPIView,具备请求方法与要调用的action方法进行一一对应功能、以及支持Mixin拓展类
4.ReadOnlyModelViewSet继承了RetrieveModelMixin、ListModelMixin、GenericViewSet,提供了读取数据的2个接口
5.ModelViewSet继承了CreateModelMixin、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin、
ListModelMixin、GenericViewSet,提供了6个接口
6.ViewSetMixin类提供了请求方法与要调用的action方法进行一一对应功能,在定义路由条目时,在as_view()方法中支持接收字典数据,
把请求方法名称字符串作为key,把具体要调用的action方法名字符串作为值
DRF中的路由器
1.自动生成路由条目,默认仅仅只会生成通用action方法的路由,自定义的action默认不会生成
2.需要使用action装饰器装饰自定义action方法,那么才支持自动生成路由条目
》第一个参数为methods指定当前action支持的请求方法,为列表类型(将实例方法名字符串作为元素)
》如果不支持methods,默认为methods=['get']
》第二个参数必须detail关键字参数,指定是否需要接收主键id(是否是详情操作),
detail为False,无需接收主键id,否则为True,需要接收主键id
》url_path指定生成路由条目时路径名称,默认为当前action方法名称
》url_name指定生成的路由条目名称后缀,默认为当前action方法名称
3.生成路由条目的操作方式?
a.定义SimpleRouter路由器对象
b.调用路由器对象的register方法,对路由进行注册
》第一个参数需要设置为r'路由前缀'
》第二个参数需要指定视图集类引用,不能调用as_view()方法
》basename参数指定生成的路由条目名称前缀,如果不指定,那么默认生成的路由条目名称前缀为第一个参数指定的值
c.使用路由器对象.urls属性,能获取自动生成的路由条目
类视图的设计原则(有则用父类,无则自定义,半有半无就重写父类)
1.如果父类有提供相应的资源(Mixin拓展类、具体通用的视图),并且能完全满足需求,就直接使用父类提供的
2.如果父类提供的资源仅仅只是部分不满足需求,重写父类的实现
3.如果父类提供的资源完全不满足需求,直接自定义
4.按需选择父类
"""
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Projects.objects.all()
serializer_class = serializers.ProjectModelSerializer
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ['id', 'name', 'leader']
ordering_fields = ['name', 'leader']
pagination_class = PageNumberPagination
@action(detail=False)
def names(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
@action(detail=True)
def interfaces(self, request, *args, **kwargs):
response = self.retrieve(request, *args, **kwargs)
response.data = response.data.get('interfaces')
return response
def get_serializer_class(self):
if self.action == "names":
return serializers.NamesProjectSerializer
elif self.action == "interfaces":
return serializers.InterfacesProjectSerializer
else:
return super().get_serializer_class()
def filter_queryset(self, queryset):
if self.action == "names":
return queryset
else:
return super().filter_queryset(queryset)
def paginate_queryset(self, queryset):
if self.action == "names":
return
else:
return super().paginate_queryset(queryset)
C:\Users\windows\444\dev08\projects\urls.py
"""
"""
from django.urls import path
from rest_framework import routers
from . import views
router = routers.SimpleRouter()
router.register(r'projects', views.ProjectViewSet, basename='xxx')
urlpatterns = [
]
urlpatterns += router.urls
C:\Users\windows\444\dev08\projects\serializers.py
from rest_framework import serializers
from rest_framework import validators
from .models import Projects
from interfaces.models import Interfaces
class InterfaceSeri(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(min_length=2, max_length=20)
tester = serializers.CharField(read_only=True)
def validate_project_name(value: str):
if '项目' not in value:
raise serializers.ValidationError("项目名称中未包含“项目”关键字")
class ProjectSerializer(serializers.Serializer):
"""
为什么需要使用序列化器类?
1.可以实现序列化输出操作
2.可以实现反序列化输入操作
3.数据校验操作
4.数据操作
如何定义序列化器类?
1.一般在子应用中创建serializers.py文件,用于定义序列化器类
2.必须继承Serializer类或者Serializer的子类
3.序列化输出的参数名要与创建的序列化器字段(序列化器类中的类属性)名称保持一致
4.序列化输出的参数类型要与创建的序列化器字段(序列化器类中的类属性)类型保持一致
5.序列化器字段必须得为Field子类
6.序列化器字段类型种类?
a.IntegerField -> int
b.CharField -> str
c.BooleanField -> bool
如何使用序列化器类实现序列化输出操作?
1.先创建序列化器对象
2.在创建序列化器对象时,使用instance创建参数
3.序列化操作的对象有四种?
a.模型对象 -- 无需传递many关键字参数
b.普通对象 -- 无需传递many关键字参数
c.查询集 -- 必须传递many=True
d.嵌套普通对象的列表 -- 必须传递many=True
4.使用创建序列化器对象.data属性获取序列化之后的数据(字典或者嵌套字典的列表)
定义的序列化器字段以及常用参数?
1.默认定义的序列化器字段,都会进行序列化输出
2.定义的序列化器字段名称和类型必须与模型类中的字段名或者普通对象的属性名保持一致
3.公共的参数
a.label、help_text与ORM模型类中的verbose_name、help_text参数一致
b.如果当前字段仅仅只需反序列化输入,可以设置write_only=True
c.如果当前字段仅仅只需序列化输出,可以设置read_only=True
d.required指定某个字段是否为必传字段,默认required=True,默认定义的字段为必传字段
e.如果required=False,那么该字段为可选参数,如果前端未传递,那么序列化输出时不会输出,如果前端有传递,那么序列化输出时会输出
f.如果同时设置required=False和write_only=True,那么write_only=True参数会被忽略
g.如果同时设置required=False和read_only=True,那么read_only=True参数会被忽略
h.default给某个字段指定默认值
i.如果给某个字段指定了默认值,那么前端未传递该字段,会把该字段作为输入值,如果指定了该字段,那么会使用该字段作为输入值
j.某个字段不同同时指定default=True, required=True
k.allow_null指定某个字段是否允许传递null值,默认不允许传递null值,如果指定了allow_null=True,那么允许该值传递null
l.error_messages可以定制字段的具体错误提示信息,该参数必须传递字典类型,将具体校验规则名称作为key,把报错提示字符串作为value
m.validators参数指定自定义校验规则(校验器),为列表类型
n.可以使用DRF提供的UniqueValidator对字段是否唯一性进行校验,queryset参数为所有模型对象的查询集,message指定校验失败的错误提示信息
o.可以在序列化器类外定义校验函数,作为validators参数值列表中的元素(使用校验函数名)
p.定义校验函数,必须接受一个参数(待校验的值),如果校验不通过,必须得抛出serializers.ValidationError异常,
可以指定具体的报错字符串作为ValidationError参数,如果校验通过,无需返回
q.某个字段的校验顺序:当前字段的类型 -> 当前字段的所有校验规则项进行校验
(段的所有校验关键字参数、validators参数列表中的所有校验规则一般都会被校验) -> 单字段校验方法 -> 多字段的联合校验
r.可以在类里面对单字段进行校验
》单字段校验的方法名称必须为validate_该字段名称
》仅仅只有当定义的字段上所有校验规则通过的情况下,才会在类里面调用对单字段进行校验的方法
4.CharField类型字段
a.默认该字段不允许传递空空字符串
b.可以指定allow_blank=True,那么允许该字段传递空字符串
c.max_length指定最大长度字节数,min_length指定最小字节长度
d.如果前端传递该字段值为str, int, float,会自动转化为字符串类型(不会对类型进行校验),如果传递其他类型,
会校验失败(invalid)
e.trim_whitespace参数默认为True,会自动清空左右两侧的空格
5.IntegerField类型字段
a.max_value参数指定最大值
c.min_value参数指定最小值
6.DateTimeField类型字段
a.format指定格式化字符串,
如何使用序列化器类实现反序列化输入操作?
1.在创建序列化器对象时,使用data传递待校验的参数(需要接收json转化为字典之后的数据)
2.传递给data的参数,必须为待校验的字典数据
3.必须得调用序列化器对象.is_valid()方法,才会开始对数据进行校验
4.仅仅只有调用is_valid()方法之后,才能通过序列化器对象.errors属性获取校验的结果(一般字典类型)
5.默认定义的序列化器字段,都会进行反序列化输入(前端都必须得传递),也会进行序列化输出操作
6.往往把前后向后端发起请求的过程称为写(write),也称为反序列化输入
7.往往把后端向前端响应数据的过程称为读(read),也称为序列化输出
8.如果当前字段仅仅只需反序列化输入,可以设置write_only=True
9.如果当前字段仅仅只需序列化输出,可以设置read_only=True
10.可以在调用序列化器对象.is_valid()方法时,指定raise_exception=True,那么校验不通过时,会抛出异常,否则不会抛出异常
11.如果未调用序列化器对象.is_valid()方法,那么是不会开始校验的,
所以不能调用序列化器对象的errors属性(获取错误提示字典信息)、data属性(序列化输出的数据)、validated_data属性(获取校验通过的数据)
序列化器类中的校验方法?
1.单字段的校验方法
》方法名:validate_字段名称()
》如果校验不通过,必须抛出serializers.ValidationError异常
》如果校验通过,必须将校验通过之后的数据返回
2.多字段联合校验方法
》方法名为:validate()
》会接收上述所有字段校验通过之后的字典
》如果校验不通过,必须抛出serializers.ValidationError异常
》如果校验通过,必须将校验通过之后的数据返回
某个字段的校验顺序:
先调用to_internal_value方法(
for 单字段 in 所有反序列化输入字段的列表:
当前字段的类型 -> 当前字段的所有校验规则项进行校验 -> 单字段校验方法
)
最后再调用validate()方法 ,该方法返回的数据就为序列化器对象.validated_data
关联字段序列化?
1.如果需要获取关联表的数据,需要定义RelatedField类型的字段
2.字段名称有要求
》如果是父表获取从表的数据,那么关联字段的名称默认为从表模型类名小写_set
》如果在定义从表模型类外键字段时,指定了related_name参数,那么elated_name参数值与关联字段的名称一致
3.如果关联字段为PrimaryKeyRelatedField,那么操作的关联表的主键id
》如果需要进行反序列化输入(对数据进行校验),那么必须指定queryset参数,为关联表的查询集对象
》如果不需要进行反序列化输入,那么必须指定read_only=True
》如果关联字段的数据有多条,那么必须指定many=True
4.如果关联字段为StringRelatedField,那么会将关联模型类中定义的__str__方法返回值作为输出内容
》StringRelatedField默认指定了read_only=True
》如果关联字段的数据有多条,那么必须指定many=True
5.如果关联字段为SlugRelatedField,那么操作的关联表的特定字段
》如果需要进行反序列化输入(对数据进行校验),那么必须指定queryset参数,为关联表的查询集对象,同时关联字段最好要有唯一约束
》如果不需要进行反序列化输入,那么必须指定read_only=True
》如果关联字段的数据有多条,那么必须指定many=True
6.由于定义的任意序列化器类最终为Field子类,所有可以作为任意一个序列化器类中的字段来使用
》
序列化器类与模型类的关系?
1.这两个类高度相似
2.当模型类中字段非常多时,定义序列化器类就相当麻烦
3.可以定义模型序列化器,来自动将模型类中的字段生成序列化器中的字段
》必须继承serializers.ModelSerializer父类
》必须在Meta内部类中,使用model类属性关联一个模型类
》需要在在Meta内部类中,使用fields指定模型类中哪些字段需要生成序列化器字段
》如果指定fields = '__all__',那么会将模型类中的所有字段生成序列化器字段
》在生成的字段中,会将主键id设置为IntegerField类型,同时会自动指定read_only=True
》会将DateTimeField类型中有添加auto_now_add或auto_now参数,会自动指定read_only=True
》会将unique=True的字段,自动生成validators=中UniqueValidator唯一约束
》null=True -> allow_null=True
blank=True -> allow_blank=True
default='xxx' -> required=False
》如果只需要将模型类中某些字段生成序列化器字段,可以将这些字段组成元组,传递给fields
》exclude与fields功能相反
》可以在模型序列化器类中定义模型类中的字段(会覆盖自动生成的字段)
》fields类属性,如果指定的说元组类型,那么必须包含所有的序列化器字段(模型类中的、模型类外的)
》如果fields为'__all__'或者exclude,那么无需指定模型类外的字段
》模型序列化器类自带create、update方法,一般无需重写
》可以在Meta内部类中的extra_kwargs类属性中对模型类中自动生成的字段进行修改
将模型类字段名作为key,把具体要修改的参数key-value字典作为值
不同方式定义的序列化器对象的区别?
1.如果在创建序列化器对象时,仅仅只传递instance参数,那么当前只会进行序列化输出操作,不可能进行反序列化输入,
不可以调用序列化器对象的save()的方法
2.如果在创建序列化器对象时,仅仅只传递data参数,那么在调用is_valid()方法后会进行反序列化输入,
同时在调用save方法时,会自动调用序列化器对象的create方法(用于数据的创建操作),会把create方法返回的数据作为
序列化输出的数据源
3.如果在创建序列化器对象时,仅仅只传递data参数,对数据进行校验之后,调用is_valid()方法后,直接调用data属性(未调用save方法),
会把校验通过的数据作为序列化输出的数据源,如果校验通过的数据中有相应的数据,同时该字段需要输出,那么输出的数据中才会有该字段
4.如果在创建序列化器对象时,同时传递instance和data参数,那么在调用save方法时,会自动调用序列化器对象的update方法(用于数据的更新操作),
会把update方法返回的数据作为序列化输出的数据源
"""
id = serializers.IntegerField(max_value=10000, min_value=1,
label='项目id', help_text='项目id',
read_only=True)
name = serializers.CharField(max_length=20, label='项目名称', min_length=3,
help_text='项目名称',
validators=[
validators.UniqueValidator(queryset=Projects.objects.all(), message='项目名称已存在'),
validate_project_name,
],
error_messages={'required': '项目名称为必传字段', 'null': '项目名称不能为null值',
'max_length': '项目名称不能超过20个字节'})
is_execute = serializers.BooleanField()
leader = serializers.CharField()
create_time = serializers.DateTimeField(format='%Y年%m月%d日 %H:%M:%S', read_only=True)
xxx = serializers.CharField(write_only=True)
token = serializers.CharField(read_only=True)
def validate_name(self, value: str):
if not value.startswith('x'):
raise serializers.ValidationError('项目名称必须以“x”开头')
return value
def validate(self, attrs):
if len(attrs.get('name')) <= len(attrs.get('leader')):
raise serializers.ValidationError('项目名称的长度不能小于或等于项目负责人名称长度')
attrs['name'] = attrs.get('name').upper()
attrs.pop('xxx')
return attrs
def create(self, validated_data):
obj = Projects.objects.create(**validated_data)
obj.token = 'dfshofshfo gfhfshfosfsfhsof sf'
return obj
def update(self, instance, validated_data):
instance.name = validated_data.get('name') or instance.name
instance.leader = validated_data.get('leader') or instance.leader
instance.is_execute = validated_data.get('is_execute') or instance.is_execute
instance.desc = validated_data.get('desc') or instance.desc
instance.save()
return instance
class UsersSerializer(serializers.Serializer):
username = serializers.CharField()
score = serializers.IntegerField()
gender = serializers.BooleanField()
class ProjectModelSerializer(serializers.ModelSerializer):
name = serializers.CharField(max_length=20, label='项目名称', min_length=3,
help_text='项目名称',
validators=[
validators.UniqueValidator(queryset=Projects.objects.all(), message='项目名称已存在'),
validate_project_name,
],
error_messages={'required': '项目名称为必传字段', 'null': '项目名称不能为null值',
'max_length': '项目名称不能超过20个字节'})
class Meta:
model = Projects
fields = ('id', 'name', 'leader')
extra_kwargs = {
'name': {
'min_length': 3,
'label': '项目名称',
'validators': [
validators.UniqueValidator(queryset=Projects.objects.all(), message='项目名称已存在'),
validate_project_name,
],
'error_messages': {'required': '项目名称为必传字段', 'null': '项目名称不能为null值',
'max_length': '项目名称不能超过20个字节'}
}
}
def validate(self, attrs):
if len(attrs.get('name')) <= len(attrs.get('leader', '')):
raise serializers.ValidationError('项目名称的长度不能小于或等于项目负责人名称长度')
attrs['name'] = attrs.get('name').upper()
return attrs
class NamesProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Projects
fields = ('id', 'name')
class InterfacesNamesSerializer(serializers.ModelSerializer):
class Meta:
model = Interfaces
fields = ('id', 'name')
class InterfacesProjectSerializer(serializers.ModelSerializer):
interfaces = InterfacesNamesSerializer(many=True)
class Meta:
model = Projects
fields = ('interfaces', )
C:\Users\windows\444\dev08\projects\models.py
from django.db import models
from utils.base_model import BaseModel
class Projects(BaseModel):
name = models.CharField(unique=True, max_length=50, verbose_name='项目名称', help_text='项目名称')
leader = models.CharField(null=True, max_length=20, verbose_name='项目负责人', help_text='项目负责人')
is_execute = models.BooleanField(verbose_name='是否启动项目', help_text='是否启动项目', default=True)
desc = models.TextField(verbose_name='项目描述', help_text='项目描述', null=True, blank=True, default='')
class Meta:
db_table = 'tb_projects'
verbose_name = '项目表'
verbose_name_plural = '项目表'
ordering = ['id']
def __str__(self):
return f"<{self.name}>"
C:\Users\windows\444\dev08\interfaces\views.py
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework import filters
from .models import Interfaces
from . import serializers
class InterfacesView(GenericAPIView):
queryset = Interfaces.objects.all()
serializer_class = serializers.InterfaceModelSerializer
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ['=name']
def get(self, request):
queryset = self.get_queryset()
queryset = self.filter_queryset(queryset)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(instance=page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(instance=queryset, many=True)
return Response(serializer.data)
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=201)
class InterfacesDetailView(GenericAPIView):
queryset = Interfaces.objects.all()
serializer_class = serializers.InterfaceModelSerializer
def get(self, request, *args, **kwargs):
obj = self.get_object()
serializer = self.get_serializer(instance=obj)
return Response(serializer.data)
def put(self, request, *args, **kwargs):
obj = self.get_object()
serializer = self.get_serializer(instance=obj, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=201)
def delete(self, request, *args, **kwargs):
obj = self.get_object()
obj.delete()
return Response(None, status=204)
C:\Users\windows\444\dev08\interfaces\urls.py
from django.urls import path
from . import views
urlpatterns = [
path('<int:pk>/', views.InterfacesDetailView.as_view()),
path('', views.InterfacesView.as_view()),
]
C:\Users\windows\444\dev08\interfaces\serializers.py
"""
"""
from rest_framework import serializers
from rest_framework import validators
from .models import Interfaces
from projects.models import Projects
class InterfaceModelSerializer(serializers.ModelSerializer):
projects_id = serializers.PrimaryKeyRelatedField(queryset=Projects.objects.all())
class Meta:
model = Interfaces
fields = ('id', 'name', 'projects_id')
extra_kwargs = {
'name': {
'min_length': 3
}
}
def validate(self, attrs):
return attrs
def create(self, validated_data):
projects = validated_data.pop('projects_id')
validated_data['projects'] = projects
instance = super().create(validated_data)
return instance
C:\Users\windows\444\dev08\interfaces\models.py
from django.db import models
from utils.base_model import BaseModel
class Interfaces(BaseModel):
name = models.CharField(unique=True, verbose_name='接口名称', help_text='接口名称', max_length=15)
tester = models.CharField(verbose_name='接口测试人员', help_text='接口测试人员', max_length=13)
projects = models.ForeignKey('projects.Projects', on_delete=models.CASCADE,
related_name='interfaces'
)
class Meta:
db_table = 'tb_interfaces'
verbose_name = '接口表'
verbose_name_plural = '接口表'
ordering = ['id']
def __str__(self):
return f"<{self.name}>"
C:\Users\windows\444\dev08\utils\base_model.py
"""
"""
from django.db import models
class BaseModel(models.Model):
"""
a.主要意义是将不同ORM模型类中的公共字段提取出来
b.用于被子模型类继承
"""
id = models.AutoField(primary_key=True, verbose_name='id主键', help_text='id主键')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间', help_text='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间', help_text='更新时间')
class Meta:
abstract = True
C:\Users\windows\444\dev08\utils\pagination.py
from rest_framework.pagination import PageNumberPagination as _PageNumberPagination
class PageNumberPagination(_PageNumberPagination):
page_size = 4
page_query_param = 'page'
page_query_description = '指定获取的页码'
page_size_query_param = 'page_size'
page_size_query_description = '指定每一页的数据条数'
max_page_size = 100
def get_paginated_response(self, data):
response = super().get_paginated_response(data)
response.data['current_page'] = self.page.number
response.data['total_page_count'] = self.page.paginator.num_pages
return response
C:\Users\windows\444\dev08\notes.txt
创建Django项目工程
一、可以打开已经存在的本地django项目
1.点击“Open”
二、可以通过git仓库下载django项目
1.选择git
2.复制粘贴django项目工程的git仓库地址
三、可以创建新的django项目工程
1.虚拟环境的意义
a.为了不同项目之间能够相互独立
b.不同项目如果使用的同一个模块版本不一致或者使用的python版本不一致
2.创建虚拟环境
a.pipenv、virtualenv等
b.创建步骤
》安装pipenv
pip install -i https://pypi.douban.com/simple pipenv
》方式一:通过专业版本Pycharm点击New,选择pipenv
》方式二:先关联系统解释器,然后再手动创建虚拟环境
pipenv shell
pipenv install django
c.下载django
》修改Pipfile中的url
url = "https://pypi.douban.com/simple"
》安装django
pipenv install django
pip install django
d.安装django项目工程
》django-admin startproject 项目名称
会在terminal所在目录下,创建一个新的独立的django项目工程
》django-admin startproject 项目名称 .
会把terminal所在目录作为django项目的根目录,在此目录下创建项目
》项目名称:建议设置为Python中合法的标识符名称
》.代表在当前目录下创建
3.启动django项目
方式一:
a.打开terminal,进入到虚拟环境汇总
b.python manage.py runserver
会默认在127.0.0.1上监听8000端口
c.python manage.py runserver ip:port(ip:可以省略)
d.使用ctrl + c退出项目
方式二:
a.在Pycharm专业版本上创建django的启动器
b.点击右上角的“Add configuration” -> 点击“+”-> 选择“django” -> 设置启动的名称 -> 下方的“fix”
-> Enable Django support -> 选django项目路径 -> settings.py所在路径
四、django项目工程结构
1.与项目名称同名的目录
dev08/ 用于保存django项目各种配置信息,是一个包
asgi.py 用于启动ASGI协议应用服务器的入口文件,在异步项目部署中使用
settings.py 用于重写、指定django的全局配置信息
urls.py 用于创建全局路由信息
wsgi.py 用于启动WSGI协议应用服务器的入口文件,在项目部署上线时使用
db.sqlite3 django默认的关系型文本数据库文件,一般在开发阶段使用
manage.py 是django中命令行管理工具
五、设置版本管理
方式一:
打开VCS -> Enable Version Control -> git
方式二:
》打开terminal,进入虚拟环境
》git init,会在当前路径下进行版本管理,创建一个.git目录(存放git所有的配置信息)
》创建提交者信息
本地:
git config --local user.name "用户名"
git config --local user.email "用户邮箱地址"
全局:
git config --global user.name "用户名"
git config --global user.email "用户邮箱地址"
》将工作区中的修改添加到暂存区中
git add .
》将暂存区中的数据添加至本地仓库
git commit -m "当前版本的注释信息"
》推送代码到远程的步骤
a.在远程代码托管平台创建项目(如:dev08)
b.git remote add origin 项目地址
c.git push -u origin --all
》创建分支
git checkout -b 分支名称
六、子应用
1.子应用的意义
a.便于复用
b.便于管理
2.子应用的创建
社区/专业版:python manage.py startapp 子应用名
专业版:打开Tools下Run manage Task -> startapp 子应用
3.子应用的注册
a.如果需要django能够正常使用子应用,那么必须的注册
b.settings.py中INSTALLED_APPS列表中进行注册
c.方式一:'子应用名' 方式二:'子应用名.apps.子应用名首字母大写Config'
4.子应用的结构
子应用名/ 为python中的包
admin.py 对后台管理站点进行配置
apps.py 用于配置当前子应用的相关信息
models.py 用于定义数据库模型类
tests.py 用于编写当前子应用的单元测试
views.py 用于定义业务逻辑
七、路由
1.什么是路由?
前端用户请求的url与后端函数(视图函数)的映射条目
2.在哪里定义路由?
a.在与项目名同名包下的urls.py中定义
b.全局路由文件
c.urlpatterns列表中定义路由
3.怎样定义路由?
a.使用调用path函数的形式定义
b.path函数第一个参数为url路径
》url路径,去掉“ip、域名:端口号/”之后,剩余的部分
》url路径前往往无需添加“/”,在url路径最后需要添加“/”
c.path函数第二个参数为待调用的函数名(视图函数名称)
d.如果path函数第二个参数为include函数,那么会继续到include指定的子路由中去匹配
e.include需要添加子路由的路径("子应用名.urls")
4.如何寻址?
a.urlpatterns列表从上到下进行匹配
b.如果匹配不成功,会继续往后匹配
c.如果匹配成功,会【自动】调用path函数第二个参数指定的视图函数
d.同时一旦匹配成功会结束匹配
e.如果全部匹配不成功,那么会抛出404异常(404页面)
5.定义路径参数
a.xxx/<类型转化器:参数名称>/
b.自带的类型转化器:int、str、slug、uuid
c.如果路径能与类型转化器进行匹配,那么会把参数作为关键字参数传递给视图函数
d.视图函数是以关键字参数的形式接收的,所有参数名称要与视图函数接收参数的名称保持一致
e.如果类型转化器匹配不成功,会继续往后匹配
八、函数视图
1.函数视图的参数和返回
a.第一个参数为HttpRequest对象
b.当路由匹配成功后,django框架会将前端的请求所有数据封装为HttpRequest,在调用视图函数时自动传递给函数的第一个参数
c.可以使用HttpRequest对象中的method属性获取请求方法名称(大写),可以通过不同的请求方法,执行不一样业务逻辑
b.函数视图必须返回HttpResponse对象或者HttpResponse子类的对象
2.函数视图的弊端
a.代码冗余严重
b.代码复用性差
3.调试
a.断点:以调试模式启动django项目,当代码执行到断点标记的地方,会自动暂停
b.意义:快速排错、快速阅读源码、快速开发代码
c.打断点的注意事项:一般断点打在函数或者方法体内部、尽量不要打在跨越多行的代码
九、类视图
1.定义
a.必须得继承View父类或者View子类
b.不同的请求方法是以实例方法来实现的
c.实例方法名与请求方法的对应关系
get -> GET
post -> POST
put -> PUT
delete -> DELETE
patch -> PATCH
d.每一个实例方法需要返回HttpResponse对象或者HttpResponse子类的对象
e.如果类视图中未定义相应的实例方法,会报405异常
2.路由条目
a.path函数的第一个参数为类视图.as_view()
十、django中的请求
1.请求参数类型
a.路径参数
》在url路径上传递的参数,如:path('xxx/<int:parameter>/')
》在函数视图或者类视图的实例方法中使用关键字作为参数名去接收
b.query string查询字符串参数
》http://xxx.com/?key1=value1&key2=value2&key1=value3
》使用request.GET属性获取
》request.GET返回QueryDict对象,是一个like-dict类字典类型,与字典类似(但区别)
》使用request.GET['key']或者request.GET.get('key'),如果多个参数中有名称相同的参数,获取的是最后一个值
》使用request.GET.getlist('key')去获取有名称相同的参数所有值,返回列表类型
c.请求体参数
》application/x-www-form-urlencoded
》application/json
》multipart/form-data
》xml、text
d.请求头参数
》可以使用request.META获取参数
》request.headers获取参数
十一、查看python源码
1.将光标定位在待查看的类或者函数上,ctrl + b
2.或者使用ctrl + 鼠标左键
十二、django中的响应(HttpResponse、JsonResponse)
1.第一个参数可以传递返回给前端的字符串或者字节数据
2.status关键字参数传递响应状态码,默认为200
3.headers关键字参数传递响应头信息(字典类型)
4.content_type关键字参数传递响应头中的Content-Type
5.可以使用类似字典添加key-value的形式,指定响应头数据(如:response['Content-Type'] = 'application/json')
6.JsonResponse第一个参数为字典或者嵌套字典的列表
7.JsonResponse第一个参数为嵌套字典的列表,必须设置safe=False
十三、ORM框架
1.使用pymysql执行原生sql语句的痛点
a.sql语句比较复杂且不便于管理
b.sql语句安全性无法保证
c.数据表创建、修改、更新、数据添加以及数据的迁移操作是比较麻烦的
d.数据表中添加索引、视图比较困难
e.sql语句性能不够好
2.ORM框架的意义
a.可以解决上述所有的痛点
b.可以大大地提升开发效率
c.也可以直接执行原生的sql语句
3.mysql中有哪些对象?
a.数据库
b.数据表
c.字段
d.记录
十五、当前设计的5个接口,设计的过程?
1.反序列化输入过程
a.将前端传递的参数(json参数、www-form参数)转化为python中的常用数据类型(dict、嵌套字典的列表)
2.数据校验过程
3.数据库操作过程(创建、更新操作、读取操作、删除)
4.序列化输出过程
b.将python中常用的数据类型或者模型类,转化为json字符串(xml字符串)
5.5个接口分别涉及的过程
获取所有的项目数据:数据库操作过程、序列化输出过程
创建项目:反序列化输入过程、数据校验过程、数据库操作过程、序列化输出过程
获取一条项目数据:数据库操作过程、序列化输出过程
更新项目:反序列化输入过程、数据校验过程、数据库操作过程、序列化输出过程
删除项目:数据库操作过程
十五、当前设计的5个接口,有哪些痛点?
a.代码冗余非常严重,不够简洁
b.数据校验极为麻烦,并且可读性差,复用性更差
c.代码没有统一的规范,代码维护性差
d.序列化输出的过程没有差异性
e.在获取所有项目的接口中,有很多功能缺失
》没有提供分页功能
》没有提供过滤功能
》没有提供排序功能
f.整个项目,也有功能缺失
》没有提供认证授权功能
》没有提供限流功能(反爬虫)
》5个接口不同放在同一类视图中定义
》传递参数的方式单一,不支持www-form表单参数
》返回前端数据格式单一,不支持返回html、xml格式的数据
|