当前位置:   article > 正文

drf-Djangorestframework小白也能上手的教程

drf-Djangorestframework小白也能上手的教程

一、安装

新建文件夹drf 配置解析器 在当前目录下执行

  1. pip install django
  2. pip install djangorestframework

二、搭建项目(纯净版)

  1. # 创建一个 Django程序 和文件夹同名 注意小数点
  2. django-admin startproject drf .(目录名字)
  3. #创建目录 (模块)
  4. python manage.py startapp api(模块名)

1.注释不需要的配置

setting.py

目的:纯净版 注释不需要的内容 注册drf 新增drf配置

  1. INSTALLED_APPS = [
  2. #"django.contrib.admin",
  3. #"django.contrib.auth",
  4. #"django.contrib.contenttypes",
  5. #"django.contrib.sessions",
  6. #"django.contrib.messages",
  7. "django.contrib.staticfiles",
  8. "rest_framework", #注册rest应用
  9. "api.apps.ApiConfig" #引入自己的注册的模块名
  10. ]
  11. MIDDLEWARE = [
  12. "django.middleware.security.SecurityMiddleware",
  13. # "django.contrib.sessions.middleware.SessionMiddleware",
  14. "django.middleware.common.CommonMiddleware",
  15. "django.middleware.csrf.CsrfViewMiddleware",
  16. #"django.contrib.auth.middleware.AuthenticationMiddleware",
  17. #"django.contrib.messages.middleware.MessageMiddleware",
  18. "django.middleware.clickjacking.XFrameOptionsMiddleware",
  19. ]
  20. TEMPLATES = [
  21. {
  22. "BACKEND": "django.template.backends.django.DjangoTemplates",
  23. "DIRS": [],
  24. "APP_DIRS": True,
  25. "OPTIONS": {
  26. "context_processors": [
  27. "django.template.context_processors.debug",
  28. "django.template.context_processors.request",
  29. #"django.contrib.auth.context_processors.auth",
  30. # "django.contrib.messages.context_processors.messages",
  31. ],
  32. },
  33. },
  34. ]
  35. #修改时间
  36. # TIME_ZONE = 'UTC'
  37. TIME_ZONE = 'Asia/Shanghai'
  38. USE_I18N = True
  39. USE_L10N = True
  40. # USE_TZ = True
  41. USE_TZ = False
  42. #语言修改
  43. LANGUAGE_CODE = 'zh-Hans'#en-us为英文
  44. #新增以下drf配置
  45. REST_FRAMEWORK={
  46. "UNAUTHENTICATED_USER":None,#配置匿名用户
  47. "APPEND_SLASH": False, # 去掉url尾部斜杠
  48. }
  49. #数据库配置 先注释 后续配置mysql
  50. DATABASES = {
  51. "default": {
  52. # "ENGINE": "django.db.backends.sqlite3",
  53. # "NAME": BASE_DIR / "db.sqlite3",
  54. }
  55. }

django原生可直接读取该文件的配置 但是再drf里封装了一层 需要读取setting配置需要REST_FRAMEWORK这个对象里写入配置

UNAUTHENTICATED_USER 相当于默认匿名用户配置 如果不设置该字段 接口会被鉴权无法访问

2.配置redis

pip install django-redis

setting.py文件中 配置redis的缓存

  1. CACHES = {
  2. "default":{
  3. "BACKEND":"django_redis.cache.RedisCache",
  4. "LOCATION":"redis://127.0.0.1:6379",
  5. "OPTIONS": {
  6. "CLIENT_CLASS":"django_redis.client.Defaultclient",
  7. "PASSWORD": "123"
  8. }
  9. }
  10. }

-BACKEND: 固定

-LOCATION redis的地址

-CLIENT_CLASS 固定

-PASSWORD redis的密码

3.编写路由和视图

两种形式,一种函数形式FBV 一种类形式CBV

1.函数形式

必须加装饰器@api_view 参数为list 里面为支持的方法["get","post"]

  1. #方法一 配置路由
  2. urlpatterns=[
  3. path('auth/',views.auth),
  4. ]
  1. #方法一 函数形式
  2. from rest_framework.response import Response
  3. from rest_framework.decorators import api_view
  4. @api_view(["GET"])
  5. def auth(request):
  6. return Response('status': True,' message': "success"})
2.类形式

类必须继承rest_framework的APIView 类方法可提供get post等方法

urls里 导出为类的as_view()方法 此方法在继承的父类身上 此方法返回一个函数

  1. #方法二 类形式
  2. urlpatterns=[
  3. path('auth/',UserInFo.as_view()),
  4. #path('auth/<str:params>/',UserInFo.as_view()) 参数
  5. ]
  1. #方法二 类形式
  2. from rest_framework.views import APIView
  3. from rest_framework.response import Response
  4. class UserInFo(APIView):
  5. def get(self,request, *args,**kwargs):
  6. #可以直接用urls里定义的形参接受参数
  7. #或者 (self,request,**args,**kwargs)
  8. #或者 self.args self.kwargs
  9. return Response({'status': True, 'message': "success"})
  10. #def post(self,request):
  11. #........

4.配置端口

根目录:manage.py

  1. #引入
  2. from django.core.management.commands.runserver import Command as Runserver
  3. def main():
  4. """Run administrative tasks."""
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "admin.settings")
  6. try:
  7. from django.core.management import execute_from_command_line
  8. except ImportError as exc:
  9. raise ImportError(
  10. "Couldn't import Django. Are you sure it's installed and "
  11. "available on your PYTHONPATH environment variable? Did you "
  12. "forget to activate a virtual environment?"
  13. ) from exc
  14. execute_from_command_line(sys.argv)
  15. if __name__ == "__main__":
  16. Runserver.default_addr = '0.0.0.0' # 修改默认地址
  17. Runserver.default_port = '8880' # 修改默认端口
  18. main()

5.运行

1.方法一: 命令行
python manage.py runserver
2.方法二:配置pycharm

点击编辑配置

配置项目manage.py文件位置

形参为 runserver

应用后点击播放键即可启动

三、MySql

1.连接配置

建库

先使用命令或者可视化工具 先建库

npm包选择

有两种库可以选择 pymysql和mysqlclient 选择其中一种方案即可

1、mysqlclient
pip install mysqlclient

修改配置settings.py

  1. DATABASES = {
  2. "default": {
  3. "ENGINE": "django.db.backends.mysql",
  4. "NAME": "xxx",#数据库名字
  5. "USER": "root", #账号
  6. "PASSWORD": "root123", #密码
  7. "HOST":"127.0.0.1",#数据库地址
  8. "PORT":3306, #端口一般默认
  9. }
  10. }

NAME:数据库名称

USER:数据库用户名

PASSWORD:数据库密码

HOST:数据库主机地址

PORT:数据库端口号

2、pymysql
2.1安装
pip install pymysql
2.2修改配置settings.py
  1. DATABASES = {
  2. 'default': {
  3. 'ENGINE': 'django.db.backends.mysql',
  4. 'NAME': 'xxx',
  5. 'USER': 'root',
  6. 'PASSWORD': '123456',
  7. 'HOST': 'localhost',
  8. 'PORT': '3306',
  9. 'OPTIONS': {
  10. 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
  11. 'charset': 'utf8mb4',
  12. },
  13. 'MYSQL': {
  14. 'driver': 'pymysql',
  15. 'charset': 'utf8mb4',
  16. },
  17. }
  18. }

字段解释同mysqlclient

2.3修改配置settings.py 同级文件 __init__.py
  1. import pymysql
  2. pymysql.install_as_MySQLdb()

2.数据库初始化

在api模块下 models.py里

  1. #引入django models模块
  2. from django.db import models
  3. #假定建一个用户类 必须继承models 会自动创建表名为 :模块名字+类名字
  4. class UserInfo(models.Model):
  5. id = models.BigAutoFileId(primary_key=True) #自增id 这行可以不写 django会默认添加
  6. name = models.CharField(max_length=32) #字符串
  7. age = models.IntegerField()#数字
  8. #参数详解
  9. #age = =models.IntegerField(verbose_name="年龄") verbose_name表示写入备注
  10. #age = =models.IntegerField(default=2) default代表插入默认数据
  11. #age = =models.IntegerField(null=True,blank=True) 代表可以为空
  12. #连表 假如和部门关联
  13. #depart = models.ForeignKey(to="关联表的类名",to_field="关联表的key",on_delete=models.CASCADE)
  14. #on_delete: 表示级联删除 如果关联表的值被删除 怎么处理当前值
  15. # models.CASCADE 当前值也会被删除
  16. # models.SET_NULL 当前值也会置空
  17. #注意 虽然这里的键名为sex django里面会自动改为 建名+_id :sex_id
  18. #约束 参数名choices 此值只能选择1或者2
  19. gender_choices=((1,"男"),(2,"女"))
  20. gender=models.SmallIntegerField(choices=gender_choices)

当写入这个类 django 会自动转换为sql语句

等同于:

  1. create table app_userinfo(
  2. id bigint auto_increment primary key,
  3. name varchahar(32),
  4. age int
  5. )
  6. #表名为 :模块名字+类名字

在跟目录执行以下命令 既可以创建表 (需要注册 INSTALLED_APPS app 原因:默认在去找注册的app里下面的models是否有内容)

  1. python manage.py makemigrations
  2. python manage.py migrate

参数model属性

AutoField

一个自动递增的整型字段,添加记录时会自动增长,AutoField 字段通常会用于充当数据表的主键,若模型没有指定主键字段,则Django 自动添加一个 AutoField字段

IntegerField

整数

SmallIntegerField

具有较小的输入范围,具体范围依赖于所使用的数据库

BigIntegerField

64位整型字段

BinaryField

二进制数据字段,只能通过 types 对其赋值

FloatField

浮点型字段,定义本字段时必须传入 max_digits 和decimal_places 参数,用于定义总位数(不包括小数点和符号)和小数位数

DecimalField

十进制浮点数,max_digits参数表示总位,decimal_places 参数表示小数位数

CommaSeparatedIntegerField

用于存放逗号分隔的整数值,相较与普通的 CharField, 它有特殊的表单数据验证要求

DurationField

存储时间周期,用 Python 的 timedelta 类型构建

EmailField

带检查 Email 合法性的CharField

FileField

单个文件上传字段,在定义本字段时传入参数 upload_to,用于保存上载文件的服务器文件系统路径,这个路径必须 包含 striftime formatting, 该格式将上载 文件的 date/time 替换

FilePathField

按目录限制规则选择文件,定义本字段必须传入参数 path ,用于限制目录

ImageField

类似于 FileField,同时验证上传对象是否是一个合法的图片,有两个可选参数 height_field 和 width_field,如果提供这两个参数,则图片将按照提供的高度和宽度规格保存,该字段要求安装 Python Imaging 库

IPAddressField

一个字符串形式的IP地址,例如: 192.23.250.2

NullBooleanField

类似于 BooleanField, 但比其多一个None选项

PhoneNumberField

带有美国风格的电话号码校验的 CharField,格式为 XXX-XXX-XXXX

SlugField

只包含字母、数字、下划线和连字符的输入字段,通常用于URL

URL

用于保存URL

USStateField

美国州名的缩写字段,由两个字母组成

XMLField

XML字段是具有XML合法验证的TextField

BooleanField

布尔字段

CharField

字符串字段,用于较短的字符串

TextField

大容量文本字段

DateField

日期字段auto_now 当对象被保存时,将该字段的值设置为当前时间
auto_now_add 当对象被首次创建时,将该字段的值设置为当前时间

DateTimeField

类似于 DateField,但同时支持于时间的输入

TimeField

时间字段,类似于 DateTimeField,但只能表达和输入时间

模型类属性参数

max_length

定义字符的长度,例如 : headline = models.CharField(max_length=255)

primary_key

设置一个模型的主键字段,例如:primary_key=true

null

定义是否允许相对应的数据库字段为Null,默认设置为 False

blank

定义字段是否可以为空

choices

定义字段的可选值,本字段的值应该是一个包含二维元素的元组,元组的每个元素的第一个值是实际存储的值,,第二个值是HTML页面进行选择时显示的值

default

设置默认值,例如: default="1"

help_text

HTML页面中输入控件的帮助字符串

unique

是否为字段定义数据库的唯一约束

verbose_name

人性化名称 例如:name = models.CharField(verbose_name="姓名",max_length=12,)

db_index

若值为True,则在表中会为此字段创建索引
默认False

db_column

字段的名称
如果未指定,则使用属性的名称

关于auto_now和auto_now_add时间格式化问题

重写字段映射方法

  1. class DateTimeFieldFormat(models.DateTimeField):
  2. """
  3. 数据库datetime类型字段格式化(%Y-%m-%d %H:%M:%S)
  4. precision:需要保留的小数位数
  5. """
  6. def __init__(self, verbose_name=None, name=None, precision=0, **kwargs):
  7. self.precision = precision
  8. super().__init__(verbose_name, name, **kwargs)
  9. def db_type(self, connection):
  10. return 'datetime(%d)' % self.precision
  11. class UserInfoModel(models.Model):
  12. username = models.CharField(verbose_name="用户名", max_length=16, unique=True, default='admin')
  13. password = models.CharField(verbose_name="密码", max_length=16, default='123')
  14. create_time = DateTimeFieldFormat(verbose_name="创建时间", auto_now_add=True)
  15. update_time = DateTimeFieldFormat(verbose_name="修改时间", auto_now=True)

3.数据增删改查

新增
  1. UserInfo.objects.create(name="张三", age=12, gender=1)
  2. #等同于 insert into app_userinfo(name)values("张三")...
查询

查询全部:类名.objects.all() 结果为一个列表

  1. list=UserInfo.objects.all()
  2. for item in list:
  3. print(item.name)

条件查询 类名.objects.filter(条件)

  1. list=UserInfo.objects.filter(id=1)
  2. #结果也是列表
  3. obj=UserInfo.objects.filter(id=1).first()
  4. #获取第一个单独数据 得到一个对象
修改

修改某条 : 类名.objects.filter(条件).update(age=12) 修个某条为12

修改全部:类名.objects.all().update(age=12) 修改全部age为12

  1. UserInfo.objects.filter(id=2).update(name=12)
  2. #返回一个int 1或者0 表示成功或者失败
删除

删除某条 : 类名.objects.filter(条件).delete()

删除全部:类名.objects.all().delete()

  1. UserInfo.objects.filter(name="张三").delete()
  2. #返回一个元组 (0,{})或者(1,{'api.UserInfo': 1}) 表示成功或者失败

四、request对象

request是被drf封装了一层新的request对象,django原生的request 封装在新对象的_request属性里,可以使用request._request访问原生request 但django为了方便 封装时通过__getattribute__和__getattr__做了处理 直接使用request.method也能访问到request._request.method

1.解析器

不用的话 默认支持JSONParser,FormParser,MultiPartParser三种解析器

需要改默认 需要改全局配置 setting里新增

例:DEFAULT_PARSER_CLASSES":["rest_framework.parsers.JSONParser"]

单个配置解析器JSONParser,FormParser
  1. from rest_framework.parsers import JSONParser, FormParser
  2. from rest_framework.negotiation import DefaultContentNegotiation
  3. class UserInFo(APIView):
  4. def get(self,request, *args,**kwargs):
  5. # 所有解析器
  6. parser_classes = [JSONParser,FormParser] #JSON格式和formdata
  7. # 根据请求,匹配对应的解析器 寻找
  8. content_negotiation_class = DefaultContentNegotiation
  9. #获取参数方法
  10. request.query_params.get("id") #获取get url参数 id
  11. #post请求
  12. #request.data
文件解析器
  1. from rest_framework.parsers import MultiPartParser
  2. class UserInFo(APIView):
  3. def post(self,request,params):
  4. parser_classes = [ MultiPartParser] #文件解析器包括 文件和其他表单
  5. #获取文件对象
  6. file=request.data.get("img")

2.获取参数

1.读取请求头的content-type类型读取

2.根据不同类型获取解析器

  1. #http://127.0.0.1:8000/getUserInfo/?id=1&&name=xxxx
  2. request.query_params.get("id") #获取get url参数 id
  3. request.data #获取请求体 post 参数

五、认证组件

应用场景:判断是否带有token

新建
  1. from rest_framework.authentication import BaseAuthentication
  2. #from rest_framework.exceptions import AuthenticationFailed 抛出认证失败异常
  3. class AuthView(BaseAuthentication):
  4. def authenticate(self,request): #认证方法 authenticate[固定方法名字]
  5. #例:
  6. #无token返回 raise AuthenticationFailed({"code":999:"error":"认证失败"})
  7. #有返回XXX元组
  8. def authenticate_header(self,request):# authenticate方法异常后控制WWW-Authenticate返回值
  9. return "token" #返回一个字符串即可

注意:此认证类组件 不能放在视图views中 可单独使用一个文件

类名随意 只要继承BaseAuthentication类就一定有authenticate方法

authenticate 方法说明 主要用户认证条件

#返回值(三种):

#成功:返回元组(user,token) 一般这种格式相当(request.user,request.auth)会自动赋值 后续直接访问

#失败:引入drf里exceptions类 抛出异常 raise AuthenticationFailed({"code": 999,"error":"认证失败"})

#未知:返回None 可用于匿名用户

authenticate_header 方法说明:

当authenticate方法异常后 控制响应头里WWW-Authenticate返回值 返回值为此方法return的值

该方法如果没有返回值 可能响应状态码受影响例如本该401 结果返回403

使用

方法一:单个接口使用 authentication_classes字段 注意单个接口必须继承drf的APIView类

  1. #方法一
  2. class Login(APIView):
  3. authentication_classes=[] #如果未空数组则不需要验证 如果为[AuthView] 则使用配置的方法认证可配置多个方法用,隔开
  4. def get(self,request, *args,**kwargs):
  5. #....

方法二:全局配置setting.py 新增字段DEFAULT_AUTHENTICATION_CLASSES

  1. REST_FRAMEWORK={
  2. "UNAUTHENTICATED_USER":Nnoe,#配置匿名用户
  3. "DEFAULT_AUTHENTICATION_CLASSES":[认证组件地址(例api.auth.AuthView)]
  4. }

如果同时配置:先去全局去读,再去局部读取 局部优先级更高

使用多个认证类

authentication_classes配置多个验证(全局同理) [认证类1,认证类2,认证类3]

假如 list值全为[None,None,None] 程序会继续执行,相当于允许匿名用户 self.user=None self.auth=None

假如 有一个抛出异常则会 后续不执行

假如 有一个成功返回元组 则会后续不执行

只有遇到None的情况才会往后执行 相当于逻辑或的关系

六、权限组件

应用场景:角色权限、多条件同时成立

新建
  1. from rest_framework.permissions import BasePermission
  2. class PermissionView(BasePermission):
  3. #定义错误信息
  4. message={"status":False,"msg":"无权访问"}
  5. def has_permission(self,request): #认证方法 has_permission[固定方法名字]
  6. #self.message = xxx 可以修改错误信息
  7. #返回True或者False

权限类必须继承permissions里的BasePermission类,同时必须定义has_permission方法

has_permission方法返回值:True和false

自定义错误信息 self.message

使用

方法一:单个接口使用 permission_classes字段 注意单个接口必须继承drf的APIView类

  1. #方法一
  2. class GetOrderView(APIView):
  3. permission_classes=[PermissionView] #list 值为上一步定义的权限类名
  4. def get(self,request, *args,**kwargs):
  5. #....

方法二:全局配置setting.py 新增字段DEFAULT_PERMISSION_CLASSES

  1. REST_FRAMEWORK={
  2. "UNAUTHENTICATED_USER":Nnoe,#配置匿名用户
  3. "DEFAULT_PERMISSION_CLASSES":['权限组件地址(例api.auth.PermissionView)'] #值为list可配置多个
  4. }
使用多个权限类

permission_classes配置多个验证(全局同理) [权限类1,权限类2,权限类3]

在默认情况下:

1.必须满足每个权限类 返回True 才算通过 是并且的关系

2.一旦遇到Flase 立即返回 后续补执行

改变并权限类且关系

改变默认情况下 权限认证规则 不需要满足且的关系 例如改变成满足一个就可以

定义类方法check_permissions

  1. from rest_framework.permissions import BasePermission
  2. class PermissionView(BasePermission):
  3. # message={"status":False,"msg":"无权访问"}
  4. # def has_permission(self,request):
  5. #返回True或者Flase
  6. def check_permissions(self,request):
  7. # 用self.get_permissions()获取所有权限类
  8. for per in self.get_permissions():
  9. per.has_permission(self,request) #获取权限验证结果
  10. #-------根据结果处理------
  11. #如果不做处理 直接return 表示不需要拦截
  12. #拦截: 调用 self.permission_denied(request,"错误信息")

check_permissions方法 不做任何处理 相当于通过

调用self.permission_denied(request,"错误信息") 表示拦截

七、限流组件

应用场景:限制频繁访问 例ip限制

新建
  1. from rest_framework.throttling import SimpleRateThrottle
  2. from django.core.cache import cache as default_cache# 引入reids缓存模块
  3. #继承SimpleRateThrottle 类
  4. class BaseThrottle(SimpleRateThrottle):
  5. scope = "ip" # 定义此限流类的名字
  6. THROTTLE_RATES = {"ip": "5/m"} #访问频次
  7. cache = default_cache #redis缓存
  8. def get_cache_key(self, request, view):#必须继承的方法
  9. ident = self.get_ident(request) # 获取请求用户IP 作为唯一标识 可自定义唯一标识
  10. return self.cache_format % {'scope': self.scope, 'ident': ident}

scope:当前限流类的名字 ,作用为 可根据此名字 找到对应THROTTLE_RATES的限流频率

ident: 对于限制唯一标识

THROTTLE_RATES:可在当前类里定义 格式为 {限流类名:次数/时间 } 也可以全局配置在setting.py 里

  1. REST_FRAMEWORK={
  2. "DEFAULT_THROTTLE_RATES": {
  3. "XXX":"2/m", #根据限流类 scope定义的名字 为key 值为限流频率
  4. "XX":"3/m"
  5. }
  6. }
使用
  1. class Login(APIView):
  2. throttle_classes=[BaseThrottle] #引入上文编写的限流类放入list
  3. def get(self,request, *args,**kwargs):
  4. #....
全局同认证权限同理

八、版本

设置 settings

  1. REST_FRAMEWORK = {
  2. "VERSION_PARAM': "V",
  3. "DEFAULT_VERSION":"V1",#默认版本
  4. "ALLOWED_VERSIONS": ["v1","v2","v3"], #设置有几个版本
  5. "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning"#此项配置为全局配置
  6. }

#DEFAULT_VERSIONING_CLASS 为全局配置 可选参数有URLPathVersioning AcceptHeaderVersioning QueryParameterVersioning 配置了之后不必再每个视图类里写入versioning_class=xxxx

1.版本在url中

urls.py

  1. from django.urls import path,re_path
  2. urlpatterns=[
  3. #方法一 <str:version> 代表版本
  4. path('api/<str:version>/auth/',UserInFo.as_view())
  5. #方法二正则
  6. re_path(r'^api/(?P<version>\w+)/auth/$),UserInFo.as_view())
  7. ]

视图组件

  1. from rest_framework.versioning import URLPathVersioning #获取url版本
  2. #用户相关类
  3. class UserInFo(APIView):
  4. versioning_class=URLPathVersioning #
  5. def get(self,request,*args,**kwargs):
  6. print(request.version)#获取版本

2.版本在请求头中

例如header里

Accept:application/json; version=1.0

urls.py

  1. from django.urls import path
  2. urlpatterns=[
  3. path('api/auth/',UserInFo.as_view())
  4. ]

视图组件

  1. from rest_framework.versioning import AcceptHeaderVersioning #获取请求头版本
  2. #用户相关类
  3. class UserInFo(APIView):
  4. versioning_class=AcceptHeaderVersioning
  5. def get(self,request,*args,**kwargs):
  6. print(request.version)#获取版本

九、序列化

从数据库获取的QuerySet或数据对象 转化为JSON

假设models有以下数据

  1. #假如models.py有以下内容
  2. from django.db import models
  3. class UserModel(models.Model):
  4. name=models.CharField(verbose_name="姓名")
  5. age=models.IntegerField(verbose_name="年龄")
  6. gender=models.SmallIntegerField(verbose_name="性别",choices=((1,"男"),(2,"女")))

1.serializers

新建序列化器

  1. from rest_framework import serializers
  2. class UserSerializer(serializers.Serializer): #继承Serializer 也可以继承自定义的序列化器
  3. name = serializers.CharField()
  4. age = serializers.IntegerField()
  5. #定义需要获取的键名 此例相当于获取name和age

使用 单个对象序列化

  1. class UserView(APIView):
  2. def get(self,request,*args,**kwargs):
  3. userInfo=models.UserModel.objects.all().first() #假如获取数据库一条数据
  4. ser=UserSerializer(instance=userInfo)#参数为数据库数据
  5. print(ser.data)
  6. #此时相当与ser.data 为{name:'XXX',age:11} 没有gender字段

多个对象序列化

  1. class UserView(APIView):
  2. def get(self,request,*args,**kwargs):
  3. userInfo=models.UserModel.objects.all() #假如获取数据库多条数据
  4. ser=UserSerializer(instance=userInfo,many=True)#参数many为True
  5. print(ser.data)
  6. #[{name:'XXX',age:12},{name:'YYYY',age:11}]

2.ModelSerializer

假设models有以下数据

  1. #假如models.py有以下内容
  2. from django.db import models
  3. #用户表
  4. class UserModel(models.Model):
  5. name=models.CharField(verbose_name="姓名")
  6. age=models.IntegerField(verbose_name="年龄")
  7. gender=models.SmallIntegerField(verbose_name="性别",choices=((1,"男"),(2,"女")))
  8. #假设关联部门
  9. depart = models.ForeignKey(verbose_name="部门",to="Depart",on_delete=models.CASCADE)
  10. ctime = models.DateTimeField(verbose_name="时间",auto_now_add=True)
  11. #一对多 关系
  12. tags = models.ManyToManyField(verbose_name="标签",to="Tag")
  13. #部门表
  14. class DepartModel(models.Model):
  15. title = models.CharField(verbose_name="部门",max_length=32)
  16. sort = models.IntegerField(verbose_name="顺序")
  17. count = models.IntegerField(verbose_name="人数")
  18. #标签表
  19. class TagModel(models.Model):
  20. caption = modes.CharField(verbose_name="标签",max_length=32)

ModelSerializer 将会序列化 全部models里定义的字段

  1. from rest_framework import serializers
  2. from django.db import models
  3. class UserSerializer(serializers.ModelSerializer):
  4. class Meta:
  5. model = models.UserModel
  6. fields = "__all__" #取出全部数据

fields = "__all__" #取出全部数据

fields =["name","age"] 也可以指定字段 也可以指定没有的字段 但是要赋值

使用

同上文serializers

source方法

1.choices数据
  1. from rest_framework import serializers
  2. from django.db import models
  3. class UserSerializer(serializers.ModelSerializer):
  4. gender_text = serializers.CharField(source="get_gender_display")
  5. #新增返回字段gender_text 值为自己的自定义的 相当于执行get_gender_display 获取choices 1为男
  6. class Meta:
  7. model = models.UserModel
  8. fields = ["name","age","gender","gender_text"]
  9. #此时取值为 [{"name":"XX","age":11,"gender":1,"gender_text":"男"}]
2.联表数据
  1. from rest_framework import serializers
  2. from django.db import models
  3. class UserSerializer(serializers.ModelSerializer):
  4. #depart = serializers.CharField(source="depart") #这样会拿到关联depart所有数据
  5. depart = serializers.CharField(source="depart.title") #这样会拿到关联depart 部门名字
  6. class Meta:
  7. model = models.UserModel
  8. fields = ["name","age","gender","depart"]
  9. #此时取值为 {"name":"XX","age":11,"gender":1,"depart":"运营部"}

format方法

  1. from rest_framework import serializers
  2. from django.db import models
  3. class UserSerializer(serializers.ModelSerializer):
  4. ctime = serializers.CharField(format="%Y-%m-%d") #格式化时间字段
  5. class Meta:
  6. model = models.UserModel
  7. fields = ["name","age","gender","ctime"]
  8. #此时取值为 {"name":"XX","age":11,"gender":1,"ctime":"2022-11-11"}

自定义方法

  1. from rest_framework import serializers
  2. from django.db import models
  3. class UserSerializer(serializers.ModelSerializer):
  4. xxx=serializers.SerializerMethodField() #设置自定义方法 xxx为自定义的返回键名
  5. class Meta:
  6. model = models.UserModel
  7. fields = ["name","age","gender","ctime","xxx"]
  8. def get_xxx(self,obj):
  9. return "姓名:{},年龄{}".format(obj.name,obj.age)

SerializerMethodField会自动触发 名为 get_xxx的方法 xxx为自定义的返回值健名 值为此方法的返回值

参数 obj为当前对象

嵌套方法

主要针对于 ManyToManyField和ForeignKey

一对多(傻瓜式写法)
  1. from rest_framework import serializers
  2. from django.db import models
  3. class UserSerializer(serializers.ModelSerializer):
  4. tag=serializers.SerializerMethodField() #设置自定义方法
  5. class Meta:
  6. model = models.UserModel
  7. fields = ["name","age","gender","ctime","tag"]
  8. def get_tag(self,obj):
  9. query=obj.tags.all() #获取tag关联表所有的值
  10. res=[{'id':tag.id,'caption':tag.caption} for tag in query] #循环取值 相当于tag字段值为数组
  11. return res
正式写法

借用序列化器

  1. from rest_framework import serializers
  2. from django.db import models
  3. #部门序列化器
  4. class DepartSerializer(serializers.ModelSerializer):
  5. class Meta:
  6. model=models.DepartModel
  7. fields = "__all__"
  8. #标签序列号器
  9. class TagSerializer(serializers.ModelSerializer):
  10. class Meta:
  11. model=models.TagModel
  12. fields = "__all__"
  13. class UserSerializer(serializers.ModelSerializer):
  14. depart=DepartSerializer() #可以直接等于这个类的示例
  15. tag=TagSerializer(many=True) #多个加参数many
  16. class Meta:
  17. model = models.UserModel
  18. fields = ["name","age","gender","depart","tag"]

3.数据校验

也是基于序列化器 做入参校验

1.Serializer

1.定义

定义序列化器 ,主要为参数required=True

  1. from rest_framework import serializers
  2. class UserSerializer(serializers.Serializer):
  3. name = serializers.CharField(required=True)
  4. age = serializers.IntegerField(required=True)
参数:其他校验
  1. from rest_framework import serializers
  2. from django.core.validators import RegexValidator
  3. class UserSerializer(serializers.Serializer):
  4. name = serializers.CharField(required=True,max_length=20,min_length=10)
  5. age = serializers.CharField(validators=[RegexValidator(r"\d+",message="年龄为数字")])
  6. # name = serializers.CharField(read_only=True,write_only=True)

max_length 字符串最大长度

min_length 最小长度

validators:[] 值为数组,自定义校验方法

-RegexValidator(正则,信息) 正则校验

-message失败后的原因

钩子函数

定义validate_+ 字段名字方法 返回bool 用exceptions.ValidationError抛出异常

  1. from rest_framework import serializers
  2. from django.core.validators import RegexValidator
  3. from rest_framework import exceptions
  4. class UserSerializer(serializers.Serializer):
  5. age = serializers.CharField(validators=[RegexValidator(r"\d+",message="年龄为数字")])
  6. class Meta:
  7. model=models.UserModel
  8. fields = "__all__"
  9. #定义方法
  10. def validate_age(self,value):
  11. if len(value)>2:
  12. raise exceptions.ValidationError("年龄不能超过两位数")
  13. #self.initial_data 获取当前所有参数
  14. return value

validate_执行顺序 根据代码先后 根据fields里数据顺序先后

全局钩子(额外校验 ,非单独 很少用)

  1. from rest_framework import serializers
  2. from django.core.validators import RegexValidator
  3. from rest_framework import exceptions
  4. class UserSerializer(serializers.Serializer):
  5. age = serializers.CharField(validators=[RegexValidator(r"\d+",message="年龄为数字")])
  6. #此方法为全局钩子函数 整体
  7. def validate():
  8. raise exceptions.ValidationError("全局钩子校验失败")

setting.py drf配置中 可新增字段"NON_FIELD_ERRORS_KEY":xxx 统一全局钩子 返回错误字段的键

2.使用

方法一:判断 is_valid方法 返回值校验

  1. class UserView(APIView):
  2. def post(self,request,*args,**kwargs):
  3. ser=UserSerializer(data=request.data)#使用data 传入接收参数
  4. if ser.is_valid(): #is_valid方法返回不
  5. print(ser.validated_data)#validated_data方法获取 校验后的新json数据
  6. else:
  7. print(ser.errors) #校验不通过的原因

返回错误信息的是英文如何调整:

在setting.py中 新增字段 LANGUAGE_CODE="zh-hans" 处于REST_FRAMEWORK外面

英文的话值为 en-us

方法二:使用raise_exceptio参数

  1. class UserView(APIView):
  2. def post(self,request,*args,**kwargs):
  3. ser=UserSerializer(data=request.data)
  4. ser.is_valid(raise_exception=True): #raise_exception参数为True

该参数为True后 如果校验成功 程序继续执行 失败后 直接返回异常

缺点:返回值结构固定

2.ModelSerializer

1.定义

基于字段extra_kwargs对象 添加 对象键名为 字段名 值为校验条件

  1. class UserSerializer(serializers.ModelSerializer):
  2. # 这里也可以定义 自定义字段校验 例:gender_text=xxxx fields就数组新增该字段
  3. class Meta:
  4. model = models.UserModel
  5. fields = ["name","age"]
  6. extra_kwargs = {
  7. "name": {"max_length": 5,"min_length": 1},
  8. "age":{....}
  9. }
2.使用

和Serializer差不多 但是可以直接使用 save把数据存入数据库

  1. class UserView(APIView):
  2. def post(self,request,*args,**kwargs):
  3. ser=UserSerializer(data=request.data)
  4. if ser.is_valid():
  5. print(ser.validated_data)
  6. ser.save()#使用此方法 可以将数据直接存入数据库
  7. else:
  8. print(ser.errors)

使用此方法注意:但是用户传入的字段必须和数据库字段一一对应

1.如果校验必填字段少了这里会报错

解决:ser.save(age=11)此方法可以传入参数给默认值

2.如果用户多传字段

解决:ser.validated_data.pop("more") 可以让存储数据库 删除该字段

3.复杂表结构校验 例联表

如果是ForeignKey 和ManyToManyField会自动校验 关系表和关联表 里有无该主键

如要做特殊校验 需要在钩子函数中处理 钩子函数中 会返回该对象 关联后的值 可直接操作

4.同时校验和序列化

主要用户两个参数

read_only 仅仅序列化时读取 write_only仅仅校验写入数据库

使用时候 案例:

  1. class UserView(APIView):
  2. def post(self,request,*args,**kwargs):
  3. ser=UserSerializer(data=request.data) #校验
  4. if ser.is_valid():
  5. ser.save()
  6. #如果同时用一个 那么不用再次初始化UserSerializer 直接save之后使用data就是被同一个序列化器处理后的data
  7. return Response(ser.data) #返回序列化结果
  8. else:
  9. print(ser.errors)

序列化器和验证器

  1. class UserSerializer(serializers.ModelSerializer):
  2. gender_text = serializers.CharField(source="get_gender_display",read_only=True)#设定仅读
  3. class Meta:
  4. model = models.UserModel
  5. fields = ["name","age","gender","gender_text"]
  6. extra_kwargs = {
  7. "id": {"read_only": True},#设定仅读
  8. "gender": {"write_only":True}#设定仅写
  9. }

Serializer同理, 相当于用户传入 gender 1 那么给他返回对应的字典{gender_text:男} 不会有字段{gender:1}和id

在校验时候 id 和gender_text 为必填

自定义钩子

原理

应用场景:假如需要校验gender参数必须传1或者2,返回时却要返回关联的关系(男或女)

  1. from collections import OrderedDict
  2. from rest_framework.fields import SkipField
  3. from rest_framework.relations import PKOnLyObject
  4. class UserSerializer(serializers.ModelSerializer):
  5. class Meta:
  6. model = models.UserModel
  7. fields = ["name","age","gender"]
  8. extra_kwargs = {
  9. "id": {"read_only": True}#设定仅读
  10. }
  11. #自定义钩子
  12. def to_representation(self, instance):
  13. ret = OrderedDict()
  14. fields = self._readable_fields
  15. for field in fields:
  16. #这里相当于 函数名字按照xx 匹配 使用时候就是 xx_键名
  17. if hasattr(self,'xx_%s' % field.field_name):
  18. value = getattr(self,'xx_%s' % field.field_name)(instance)
  19. ret[field.field_name] = value
  20. else:
  21. try:
  22. attribute = field.get_attribute(instance)
  23. except SkipField:
  24. continue
  25. check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
  26. if check_for_none is None:
  27. ret[field.field_name] = None
  28. else:
  29. ret[field.field_name] = field.to_representation(attribute)
  30. return ret
  31. #使用
  32. def xx_gender(self,obj):
  33. return obj.get_gender_display() #结果为男

xx_%s 正则匹配 可以为自定义方法取个名字

在使用的时候 用xx_参数名 就i可以

封装

定义一个工具函数 hook.py

  1. from collections import OrderedDict
  2. from rest_framework.fields import SkipField
  3. from rest_framework.relations import PKOnlyObject
  4. from rest_framework import serializers
  5. class HookSerializer(serializers.ModelSerializer):
  6. def to_representation(self, instance):
  7. ret = OrderedDict()
  8. fields = self._readable_fields
  9. for field in fields:
  10. #这里相当于 函数名字按照xx 匹配 使用时候就是 xx_键名
  11. if hasattr(self,'xx_%s' % field.field_name):
  12. value = getattr(self,'xx_%s' % field.field_name)(instance)
  13. ret[field.field_name] = value
  14. else:
  15. try:
  16. attribute = field.get_attribute(instance)
  17. except SkipField:
  18. continue
  19. check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
  20. if check_for_none is None:
  21. ret[field.field_name] = None
  22. else:
  23. ret[field.field_name] = field.to_representation(attribute)
  24. return ret

使用时候继承HookSerializer类就可以

  1. from api.utlis.hook import HookSerializer#引入HookSerializer
  2. class UserSerializer(HookSerializer,serializers.ModelSerializer):
  3. class Meta:
  4. #....
  5. def xx_():

十、封装response

为了方便返回格式统一封装response 新建utils文件夹 新建response.py文件

  1. from rest_framework.response import Response
  2. class APIResponse(Response):
  3. """
  4. 二次封装Response
  5. """
  6. def __init__(self, code=0, data_msg='ok', data=None, http_status=None, headers=None, exception=False,
  7. **kwargs):
  8. results = {
  9. 'code': code,
  10. 'msg': data_msg
  11. }
  12. # 如果返回结果就将返回结果赋给到data
  13. if data is not None:
  14. results["data"] = data
  15. # 如果传递其他的参数,将会被放到kwargs中被接收
  16. if kwargs is not None:
  17. for k, v in kwargs.items():
  18. # 采用反射的方法,去赋值
  19. setattr(results, k, v) # results[k] = v
  20. super().__init__(data=results, status=http_status, headers=headers, exception=exception)
  1. #使用
  2. return APIResponse(200,'操作成功') #需要返回data 加上data=xxx
  3. #返回样式
  4. {
  5. "code": 200,
  6. "msg": "操作成功"
  7. }

十一、日志封装

utils文件夹 新建logger.py文件,采用loguru 得先安装

pip install loguru 
  1. """
  2. # @software: PyCharm
  3. # @file:middle_log.py
  4. # @project: 封装日志
  5. """
  6. import os
  7. import time
  8. from loguru import logger
  9. from pathlib import Path
  10. import sys
  11. t = time.strftime("%Y_%m_%d")
  12. BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
  13. class Logger:
  14. __instance = None
  15. logger.remove()
  16. logger.add(sys.stdout,
  17. format="<green>{time:YYYYMMDD HH:mm:ss}</green> | " # 颜色>时间
  18. "{process.name} | "
  19. "{thread.name} | "
  20. "<cyan>{module}</cyan>.<cyan>{function}</cyan>" # 模块名.方法名
  21. ":<cyan>{line}</cyan> | " # 行号
  22. "<level>{level}</level>: " # 等级
  23. "<level>{message}</level>", # 日志内容
  24. )
  25. logger.add(os.path.join(BASE_DIR, f"log/interface_log_{t}.log"), format='{time:YYYYMMDD HH:mm:ss} - ' # 时间
  26. "{process.name} | " # 进程名
  27. "{thread.name} | " # 进程名
  28. '{module}.{function}:{line} - {level} -{message}',
  29. rotation="500MB", encoding="utf-8", enqueue=True,
  30. retention="10 days")
  31. def __new__(cls, *args, **kwargs):
  32. if not cls.__instance:
  33. cls.__instance = super(Logger, cls).__new__(cls, *args, **kwargs)
  34. return cls.__instance
  35. def info(self, msg):
  36. return logger.info(msg)
  37. def debug(self, msg):
  38. return logger.debug(msg)
  39. def warning(self, msg):
  40. return logger.warning(msg)
  41. def error(self, msg):
  42. return logger.error(msg)
  43. loggings = Logger()
  44. if __name__ == '__main__':
  45. loggings.info("2222222222222")
  46. loggings.debug("333333333333")
  47. loggings.warning("444444444444")
  48. loggings.error("555555555555")

十二、全局错误处理

utils文件夹 新建exceptions.py文件

自定义 异常处理人为抛出异常,系统异常统一处理

  1. from rest_framework.views import exception_handler as drf_exception_handle
  2. from rest_framework.response import Response
  3. from utils.logger import logger
  4. from utils.response import APIResponse
  5. class MyError(Exception):
  6. def __init__(self, value):
  7. self.value = value
  8. def __str__(self):
  9. return repr(self.value)
  10. def exception_handler(exc, context):
  11. request = context.get('request')
  12. view = context.get('view')
  13. token = request.headers.get("token") or "未登录用户"
  14. if isinstance(exc, MyError):
  15. logger.warning('请求失败:用户:【%s】,请求地址:【%s】,失败原因:【%s】' % (token, request.get_full_path(), str(exc)))
  16. return APIResponse(1001, str(exc), data=None)
  17. logger.error('用户:【%s】,使用:【%s】 请求,请求:【%s】 地址,视图函数:【%s】,错误:【%s】' % (
  18. token, request.method, request.get_full_path(), str(view), str(exc)
  19. ))
  20. return APIResponse(999, '服务器出错,请联系系统管理员')

setting.py文件中

  1. REST_FRAMEWORK = {
  2. 'EXCEPTION_HANDLER': 'utils.exceptions.exception_handler',
  3. }

十三、token鉴权

使用jwt 先安装

pip install djangorestframework-jwt

setting.py新增以下内容

  1. import datetime
  2. INSTALLED_APPS = [
  3. ...
  4. 'rest_framework_jwt'#注册应用
  5. ]
  6. REST_FRAMEWORK = {
  7. 'DEFAULT_AUTHENTICATION_CLASSES': ( #全局认证组件
  8. 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
  9. )
  10. }
  11. JWT_AUTH = {
  12. 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),#过期时间7
  13. 'JWT_ALLOW_REFRESH': True,#允许刷新
  14. }

JWT_AUTH扩展

JWT_SECRET_KEY: 'your_secret_key',#密钥

JWT_ALGORITHM: 'HS256',

JWT_VERIFY: True,

JWT_VERIFY_EXPIRATION: True,

JWT_EXPIRATION_DELTA: datetime.timedelta(seconds=3600),#token的有效期

JWT_ALLOW_REFRESH: True,

JWT_REFRESH_EXPIRATION_DELTA: datetime.timedelta(days=7),#刷新JWT的过期时间

JWT_AUTH_HEADER_PREFIX: 'JWT',

十四、配置文件详解

  1. import os
  2. from pathlib import Path
  3. #项目根路径
  4. BASE_DIR = Path(__file__).resolve().parent.parent
  5. # 秘钥,涉及到加密的django中,都会用它
  6. SECRET_KEY = "django-insecure-#gnxfjf&9@wokaeb33p5puv+^==#ky*6zp(&rhcb!s1+iw#w_r"
  7. #代码可以热更新 项目上线,要改成false
  8. DEBUG = True
  9. #允许我的项目部署在哪个ip地址上,* 表示允许部署在所
  10. ALLOWED_HOSTS = ["*"]
  11. # django 是多个app组成的,里面配置app,默认带的app,django内置的app
  12. #内置app:
  13. # admin后台管理,
  14. # auth权限管理,
  15. # contenttypes表中存app也表的关系,
  16. # sessions session表,django的session相关
  17. # messages:消息框架,flask讲闪现,是一样的东西
  18. # staticfiles:静态资源的
  19. INSTALLED_APPS = [
  20. # "django.contrib.admin",
  21. # "django.contrib.auth",
  22. # "django.contrib.contenttypes",
  23. # "django.contrib.sessions",
  24. # "django.contrib.messages",
  25. "django.contrib.staticfiles",
  26. "rest_framework",
  27. "api.apps.ApiConfig"
  28. ]
  29. # 中间件
  30. MIDDLEWARE = [
  31. "django.middleware.security.SecurityMiddleware", # 安全相关中间件
  32. # "django.contrib.sessions.middleware.SessionMiddleware", # session相关中间件
  33. "django.middleware.common.CommonMiddleware", # 带不带 / 问题
  34. "django.middleware.csrf.CsrfViewMiddleware",# csrf 认证,生成csrf串
  35. # "django.contrib.auth.middleware.AuthenticationMiddleware", # 用户认证
  36. # "django.contrib.messages.middleware.MessageMiddleware", #消息框架相关
  37. "django.middleware.clickjacking.XFrameOptionsMiddleware",
  38. ]
  39. # 根路由
  40. ROOT_URLCONF = "admin.urls"
  41. # 模板文件
  42. TEMPLATES = [
  43. {
  44. "BACKEND": "django.template.backends.django.DjangoTemplates",
  45. "DIRS": [],
  46. "APP_DIRS": True,
  47. "OPTIONS": {
  48. "context_processors": [
  49. "django.template.context_processors.debug",
  50. "django.template.context_processors.request",
  51. # "django.contrib.auth.context_processors.auth",
  52. # "django.contrib.messages.context_processors.messages",
  53. ],
  54. },
  55. },
  56. ]
  57. # 项目运行的配置---》项目上线运行,使用uwsgi 运行 application()
  58. WSGI_APPLICATION = "admin.wsgi.application"
  59. # 数据库配置,mysql 主从搭建完
  60. DATABASES = {
  61. "default": {
  62. "ENGINE": "django.db.backends.mysql",
  63. "NAME": "test", # 数据库名字
  64. "USER": "root", # 账号
  65. "PASSWORD": "3735199", # 密码
  66. "HOST": "127.0.0.1", # 数据库地址
  67. "PORT": 3306, # 端口一般默认
  68. }
  69. }
  70. AUTH_PASSWORD_VALIDATORS = [
  71. {
  72. "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
  73. },
  74. {
  75. "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
  76. },
  77. {
  78. "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
  79. },
  80. {
  81. "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
  82. },
  83. ]
  84. # 国际化 语言时间等
  85. TIME_ZONE = 'Asia/Shanghai'
  86. USE_I18N = True
  87. USE_L10N = False
  88. USE_TZ = False
  89. #返回校验错误为中文
  90. LANGUAGE_CODE = "zh-hans"
  91. # 静态资源
  92. STATIC_URL = "static/"
  93. #更改了id字段的字段类型
  94. DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
  95. #DRF配置
  96. REST_FRAMEWORK = {
  97. "UNAUTHENTICATED_USER": None, # 配置匿名用户
  98. 'APPEND_SLASH': False, # 去掉url尾部斜杠
  99. "DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S",
  100. 'EXCEPTION_HANDLER': 'utils.exceptions.exception_handler',
  101. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/561476
推荐阅读
相关标签
  

闽ICP备14008679号