赞
踩
新建文件夹drf 配置解析器 在当前目录下执行
- pip install django
- pip install djangorestframework
- # 创建一个 Django程序 和文件夹同名 注意小数点
- django-admin startproject drf .(目录名字)
- #创建目录 (模块)
- python manage.py startapp api(模块名)
setting.py
目的:纯净版 注释不需要的内容 注册drf 新增drf配置
- INSTALLED_APPS = [
- #"django.contrib.admin",
- #"django.contrib.auth",
- #"django.contrib.contenttypes",
- #"django.contrib.sessions",
- #"django.contrib.messages",
- "django.contrib.staticfiles",
- "rest_framework", #注册rest应用
- "api.apps.ApiConfig" #引入自己的注册的模块名
- ]
- MIDDLEWARE = [
- "django.middleware.security.SecurityMiddleware",
- # "django.contrib.sessions.middleware.SessionMiddleware",
- "django.middleware.common.CommonMiddleware",
- "django.middleware.csrf.CsrfViewMiddleware",
- #"django.contrib.auth.middleware.AuthenticationMiddleware",
- #"django.contrib.messages.middleware.MessageMiddleware",
- "django.middleware.clickjacking.XFrameOptionsMiddleware",
- ]
-
- TEMPLATES = [
- {
- "BACKEND": "django.template.backends.django.DjangoTemplates",
- "DIRS": [],
- "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",
- ],
- },
- },
- ]
-
- #修改时间
- # TIME_ZONE = 'UTC'
- TIME_ZONE = 'Asia/Shanghai'
- USE_I18N = True
- USE_L10N = True
- # USE_TZ = True
- USE_TZ = False
-
-
-
-
- #语言修改
- LANGUAGE_CODE = 'zh-Hans'#en-us为英文
-
- #新增以下drf配置
- REST_FRAMEWORK={
- "UNAUTHENTICATED_USER":None,#配置匿名用户
- "APPEND_SLASH": False, # 去掉url尾部斜杠
- }
- #数据库配置 先注释 后续配置mysql
- DATABASES = {
- "default": {
- # "ENGINE": "django.db.backends.sqlite3",
- # "NAME": BASE_DIR / "db.sqlite3",
- }
- }
django原生可直接读取该文件的配置 但是再drf里封装了一层 需要读取setting配置需要REST_FRAMEWORK这个对象里写入配置
UNAUTHENTICATED_USER 相当于默认匿名用户配置 如果不设置该字段 接口会被鉴权无法访问
pip install django-redis
setting.py文件中 配置redis的缓存
- CACHES = {
- "default":{
- "BACKEND":"django_redis.cache.RedisCache",
- "LOCATION":"redis://127.0.0.1:6379",
- "OPTIONS": {
- "CLIENT_CLASS":"django_redis.client.Defaultclient",
- "PASSWORD": "123"
- }
- }
- }
-BACKEND: 固定
-LOCATION redis的地址
-CLIENT_CLASS 固定
-PASSWORD redis的密码
两种形式,一种函数形式FBV 一种类形式CBV
必须加装饰器@api_view 参数为list 里面为支持的方法["get","post"]
- #方法一 配置路由
- urlpatterns=[
- path('auth/',views.auth),
- ]
- #方法一 函数形式
- from rest_framework.response import Response
- from rest_framework.decorators import api_view
-
- @api_view(["GET"])
- def auth(request):
- return Response('status': True,' message': "success"})
类必须继承rest_framework的APIView 类方法可提供get post等方法
urls里 导出为类的as_view()方法 此方法在继承的父类身上 此方法返回一个函数
- #方法二 类形式
- urlpatterns=[
- path('auth/',UserInFo.as_view()),
- #path('auth/<str:params>/',UserInFo.as_view()) 参数
- ]
-
- #方法二 类形式
- from rest_framework.views import APIView
- from rest_framework.response import Response
- class UserInFo(APIView):
- def get(self,request, *args,**kwargs):
- #可以直接用urls里定义的形参接受参数
- #或者 (self,request,**args,**kwargs)
- #或者 self.args self.kwargs
- return Response({'status': True, 'message': "success"})
-
- #def post(self,request):
- #........
根目录:manage.py
- #引入
- from django.core.management.commands.runserver import Command as Runserver
-
- def main():
- """Run administrative tasks."""
- os.environ.setdefault("DJANGO_SETTINGS_MODULE", "admin.settings")
- try:
- from django.core.management import execute_from_command_line
- except ImportError as exc:
- raise ImportError(
- "Couldn't import Django. Are you sure it's installed and "
- "available on your PYTHONPATH environment variable? Did you "
- "forget to activate a virtual environment?"
- ) from exc
- execute_from_command_line(sys.argv)
-
-
- if __name__ == "__main__":
- Runserver.default_addr = '0.0.0.0' # 修改默认地址
- Runserver.default_port = '8880' # 修改默认端口
- main()
python manage.py runserver
点击编辑配置
配置项目manage.py文件位置
形参为 runserver
应用后点击播放键即可启动
先使用命令或者可视化工具 先建库
有两种库可以选择 pymysql和mysqlclient 选择其中一种方案即可
pip install mysqlclient
修改配置settings.py
- DATABASES = {
- "default": {
- "ENGINE": "django.db.backends.mysql",
- "NAME": "xxx",#数据库名字
- "USER": "root", #账号
- "PASSWORD": "root123", #密码
- "HOST":"127.0.0.1",#数据库地址
- "PORT":3306, #端口一般默认
- }
- }
NAME:数据库名称
USER:数据库用户名
PASSWORD:数据库密码
HOST:数据库主机地址
PORT:数据库端口号
pip install pymysql
- DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'xxx',
- 'USER': 'root',
- 'PASSWORD': '123456',
- 'HOST': 'localhost',
- 'PORT': '3306',
- 'OPTIONS': {
- 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
- 'charset': 'utf8mb4',
- },
- 'MYSQL': {
- 'driver': 'pymysql',
- 'charset': 'utf8mb4',
- },
- }
- }
字段解释同mysqlclient
- import pymysql
- pymysql.install_as_MySQLdb()
在api模块下 models.py里
- #引入django models模块
- from django.db import models
-
- #假定建一个用户类 必须继承models 会自动创建表名为 :模块名字+类名字
- class UserInfo(models.Model):
- id = models.BigAutoFileId(primary_key=True) #自增id 这行可以不写 django会默认添加
- name = models.CharField(max_length=32) #字符串
- age = models.IntegerField()#数字
-
- #参数详解
- #age = =models.IntegerField(verbose_name="年龄") verbose_name表示写入备注
- #age = =models.IntegerField(default=2) default代表插入默认数据
- #age = =models.IntegerField(null=True,blank=True) 代表可以为空
-
- #连表 假如和部门关联
- #depart = models.ForeignKey(to="关联表的类名",to_field="关联表的key",on_delete=models.CASCADE)
- #on_delete: 表示级联删除 如果关联表的值被删除 怎么处理当前值
- # models.CASCADE 当前值也会被删除
- # models.SET_NULL 当前值也会置空
- #注意 虽然这里的键名为sex django里面会自动改为 建名+_id :sex_id
-
- #约束 参数名choices 此值只能选择1或者2
- gender_choices=((1,"男"),(2,"女"))
- gender=models.SmallIntegerField(choices=gender_choices)
-
当写入这个类 django 会自动转换为sql语句
等同于:
- create table app_userinfo(
- id bigint auto_increment primary key,
- name varchahar(32),
- age int
- )
- #表名为 :模块名字+类名字
在跟目录执行以下命令 既可以创建表 (需要注册 INSTALLED_APPS app 原因:默认在去找注册的app里下面的models是否有内容)
- python manage.py makemigrations
- python manage.py migrate
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 当对象被保存时,将该字段的值设置为当前时间 |
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,则在表中会为此字段创建索引 |
db_column | 字段的名称 |
重写字段映射方法
- class DateTimeFieldFormat(models.DateTimeField):
- """
- 数据库datetime类型字段格式化(%Y-%m-%d %H:%M:%S)
- precision:需要保留的小数位数
- """
-
- def __init__(self, verbose_name=None, name=None, precision=0, **kwargs):
- self.precision = precision
- super().__init__(verbose_name, name, **kwargs)
-
- def db_type(self, connection):
- return 'datetime(%d)' % self.precision
-
-
-
- class UserInfoModel(models.Model):
- username = models.CharField(verbose_name="用户名", max_length=16, unique=True, default='admin')
- password = models.CharField(verbose_name="密码", max_length=16, default='123')
- create_time = DateTimeFieldFormat(verbose_name="创建时间", auto_now_add=True)
- update_time = DateTimeFieldFormat(verbose_name="修改时间", auto_now=True)
- UserInfo.objects.create(name="张三", age=12, gender=1)
- #等同于 insert into app_userinfo(name)values("张三")...
查询全部:类名.objects.all() 结果为一个列表
- list=UserInfo.objects.all()
- for item in list:
- print(item.name)
条件查询 类名.objects.filter(条件)
- list=UserInfo.objects.filter(id=1)
- #结果也是列表
-
- obj=UserInfo.objects.filter(id=1).first()
- #获取第一个单独数据 得到一个对象
修改某条 : 类名.objects.filter(条件).update(age=12) 修个某条为12
修改全部:类名.objects.all().update(age=12) 修改全部age为12
- UserInfo.objects.filter(id=2).update(name=12)
- #返回一个int 1或者0 表示成功或者失败
删除某条 : 类名.objects.filter(条件).delete()
删除全部:类名.objects.all().delete()
- UserInfo.objects.filter(name="张三").delete()
- #返回一个元组 (0,{})或者(1,{'api.UserInfo': 1}) 表示成功或者失败
request是被drf封装了一层新的request对象,django原生的request 封装在新对象的_request属性里,可以使用request._request访问原生request 但django为了方便 封装时通过__getattribute__和__getattr__做了处理 直接使用request.method也能访问到request._request.method
不用的话 默认支持JSONParser,FormParser,MultiPartParser三种解析器
需要改默认 需要改全局配置 setting里新增
例:DEFAULT_PARSER_CLASSES":["rest_framework.parsers.JSONParser"]
- from rest_framework.parsers import JSONParser, FormParser
- from rest_framework.negotiation import DefaultContentNegotiation
-
- class UserInFo(APIView):
- def get(self,request, *args,**kwargs):
- # 所有解析器
- parser_classes = [JSONParser,FormParser] #JSON格式和formdata
- # 根据请求,匹配对应的解析器 寻找
- content_negotiation_class = DefaultContentNegotiation
-
- #获取参数方法
- request.query_params.get("id") #获取get url参数 id
-
- #post请求
- #request.data
- from rest_framework.parsers import MultiPartParser
- class UserInFo(APIView):
- def post(self,request,params):
- parser_classes = [ MultiPartParser] #文件解析器包括 文件和其他表单
- #获取文件对象
- file=request.data.get("img")
1.读取请求头的content-type类型读取
2.根据不同类型获取解析器
- #http://127.0.0.1:8000/getUserInfo/?id=1&&name=xxxx
- request.query_params.get("id") #获取get url参数 id
-
- request.data #获取请求体 post 参数
应用场景:判断是否带有token
- from rest_framework.authentication import BaseAuthentication
- #from rest_framework.exceptions import AuthenticationFailed 抛出认证失败异常
-
- class AuthView(BaseAuthentication):
- def authenticate(self,request): #认证方法 authenticate[固定方法名字]
- #例:
- #无token返回 raise AuthenticationFailed({"code":999:"error":"认证失败"})
- #有返回XXX元组
- def authenticate_header(self,request):# authenticate方法异常后控制WWW-Authenticate返回值
- 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类
- #方法一
- class Login(APIView):
- authentication_classes=[] #如果未空数组则不需要验证 如果为[AuthView] 则使用配置的方法认证可配置多个方法用,隔开
- def get(self,request, *args,**kwargs):
- #....
方法二:全局配置setting.py 新增字段DEFAULT_AUTHENTICATION_CLASSES
- REST_FRAMEWORK={
- "UNAUTHENTICATED_USER":Nnoe,#配置匿名用户
- "DEFAULT_AUTHENTICATION_CLASSES":[认证组件地址(例api.auth.AuthView)]
- }
如果同时配置:先去全局去读,再去局部读取 局部优先级更高
authentication_classes配置多个验证(全局同理) [认证类1,认证类2,认证类3]
假如 list值全为[None,None,None] 程序会继续执行,相当于允许匿名用户 self.user=None self.auth=None
假如 有一个抛出异常则会 后续不执行
假如 有一个成功返回元组 则会后续不执行
只有遇到None的情况才会往后执行 相当于逻辑或的关系
应用场景:角色权限、多条件同时成立
- from rest_framework.permissions import BasePermission
-
-
- class PermissionView(BasePermission):
- #定义错误信息
- message={"status":False,"msg":"无权访问"}
- def has_permission(self,request): #认证方法 has_permission[固定方法名字]
- #self.message = xxx 可以修改错误信息
- #返回True或者False
-
权限类必须继承permissions里的BasePermission类,同时必须定义has_permission方法
has_permission方法返回值:True和false
自定义错误信息 self.message
方法一:单个接口使用 permission_classes字段 注意单个接口必须继承drf的APIView类
- #方法一
- class GetOrderView(APIView):
- permission_classes=[PermissionView] #list 值为上一步定义的权限类名
- def get(self,request, *args,**kwargs):
- #....
方法二:全局配置setting.py 新增字段DEFAULT_PERMISSION_CLASSES
- REST_FRAMEWORK={
- "UNAUTHENTICATED_USER":Nnoe,#配置匿名用户
- "DEFAULT_PERMISSION_CLASSES":['权限组件地址(例api.auth.PermissionView)'] #值为list可配置多个
- }
permission_classes配置多个验证(全局同理) [权限类1,权限类2,权限类3]
在默认情况下:
1.必须满足每个权限类 返回True 才算通过 是并且的关系
2.一旦遇到Flase 立即返回 后续补执行
改变默认情况下 权限认证规则 不需要满足且的关系 例如改变成满足一个就可以
定义类方法check_permissions
- from rest_framework.permissions import BasePermission
-
-
- class PermissionView(BasePermission):
- # message={"status":False,"msg":"无权访问"}
- # def has_permission(self,request):
- #返回True或者Flase
-
- def check_permissions(self,request):
- # 用self.get_permissions()获取所有权限类
- for per in self.get_permissions():
- per.has_permission(self,request) #获取权限验证结果
- #-------根据结果处理------
- #如果不做处理 直接return 表示不需要拦截
- #拦截: 调用 self.permission_denied(request,"错误信息")
check_permissions方法 不做任何处理 相当于通过
调用self.permission_denied(request,"错误信息") 表示拦截
应用场景:限制频繁访问 例ip限制
- from rest_framework.throttling import SimpleRateThrottle
- from django.core.cache import cache as default_cache# 引入reids缓存模块
-
- #继承SimpleRateThrottle 类
- class BaseThrottle(SimpleRateThrottle):
- scope = "ip" # 定义此限流类的名字
- THROTTLE_RATES = {"ip": "5/m"} #访问频次
- cache = default_cache #redis缓存
- def get_cache_key(self, request, view):#必须继承的方法
- ident = self.get_ident(request) # 获取请求用户IP 作为唯一标识 可自定义唯一标识
- return self.cache_format % {'scope': self.scope, 'ident': ident}
scope:当前限流类的名字 ,作用为 可根据此名字 找到对应THROTTLE_RATES的限流频率
ident: 对于限制唯一标识
THROTTLE_RATES:可在当前类里定义 格式为 {限流类名:次数/时间 } 也可以全局配置在setting.py 里
- REST_FRAMEWORK={
- "DEFAULT_THROTTLE_RATES": {
- "XXX":"2/m", #根据限流类 scope定义的名字 为key 值为限流频率
- "XX":"3/m"
- }
- }
- class Login(APIView):
- throttle_classes=[BaseThrottle] #引入上文编写的限流类放入list
- def get(self,request, *args,**kwargs):
- #....
设置 settings
- REST_FRAMEWORK = {
- "VERSION_PARAM': "V",
- "DEFAULT_VERSION":"V1",#默认版本
- "ALLOWED_VERSIONS": ["v1","v2","v3"], #设置有几个版本
- "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning"#此项配置为全局配置
-
- }
#DEFAULT_VERSIONING_CLASS 为全局配置 可选参数有URLPathVersioning AcceptHeaderVersioning QueryParameterVersioning 配置了之后不必再每个视图类里写入versioning_class=xxxx
urls.py
- from django.urls import path,re_path
-
- urlpatterns=[
- #方法一 <str:version> 代表版本
- path('api/<str:version>/auth/',UserInFo.as_view())
-
- #方法二正则
- re_path(r'^api/(?P<version>\w+)/auth/$),UserInFo.as_view())
- ]
视图组件
- from rest_framework.versioning import URLPathVersioning #获取url版本
- #用户相关类
- class UserInFo(APIView):
- versioning_class=URLPathVersioning #
- def get(self,request,*args,**kwargs):
- print(request.version)#获取版本
例如header里
Accept:application/json; version=1.0
urls.py
- from django.urls import path
- urlpatterns=[
- path('api/auth/',UserInFo.as_view())
- ]
视图组件
- from rest_framework.versioning import AcceptHeaderVersioning #获取请求头版本
- #用户相关类
- class UserInFo(APIView):
- versioning_class=AcceptHeaderVersioning
- def get(self,request,*args,**kwargs):
- print(request.version)#获取版本
从数据库获取的QuerySet或数据对象 转化为JSON
假设models有以下数据
- #假如models.py有以下内容
- from django.db import models
- class UserModel(models.Model):
- name=models.CharField(verbose_name="姓名")
- age=models.IntegerField(verbose_name="年龄")
- gender=models.SmallIntegerField(verbose_name="性别",choices=((1,"男"),(2,"女")))
新建序列化器
- from rest_framework import serializers
- class UserSerializer(serializers.Serializer): #继承Serializer 也可以继承自定义的序列化器
- name = serializers.CharField()
- age = serializers.IntegerField()
- #定义需要获取的键名 此例相当于获取name和age
使用 单个对象序列化
- class UserView(APIView):
- def get(self,request,*args,**kwargs):
- userInfo=models.UserModel.objects.all().first() #假如获取数据库一条数据
- ser=UserSerializer(instance=userInfo)#参数为数据库数据
- print(ser.data)
- #此时相当与ser.data 为{name:'XXX',age:11} 没有gender字段
多个对象序列化
- class UserView(APIView):
- def get(self,request,*args,**kwargs):
- userInfo=models.UserModel.objects.all() #假如获取数据库多条数据
-
- ser=UserSerializer(instance=userInfo,many=True)#参数many为True
- print(ser.data)
- #[{name:'XXX',age:12},{name:'YYYY',age:11}]
假设models有以下数据
- #假如models.py有以下内容
- from django.db import models
- #用户表
- class UserModel(models.Model):
- name=models.CharField(verbose_name="姓名")
- age=models.IntegerField(verbose_name="年龄")
-
- gender=models.SmallIntegerField(verbose_name="性别",choices=((1,"男"),(2,"女")))
- #假设关联部门
- depart = models.ForeignKey(verbose_name="部门",to="Depart",on_delete=models.CASCADE)
- ctime = models.DateTimeField(verbose_name="时间",auto_now_add=True)
- #一对多 关系
- tags = models.ManyToManyField(verbose_name="标签",to="Tag")
-
- #部门表
- class DepartModel(models.Model):
- title = models.CharField(verbose_name="部门",max_length=32)
- sort = models.IntegerField(verbose_name="顺序")
- count = models.IntegerField(verbose_name="人数")
-
- #标签表
- class TagModel(models.Model):
- caption = modes.CharField(verbose_name="标签",max_length=32)
ModelSerializer 将会序列化 全部models里定义的字段
- from rest_framework import serializers
- from django.db import models
-
- class UserSerializer(serializers.ModelSerializer):
- class Meta:
- model = models.UserModel
- fields = "__all__" #取出全部数据
fields = "__all__" #取出全部数据
fields =["name","age"] 也可以指定字段 也可以指定没有的字段 但是要赋值
使用
同上文serializers
- from rest_framework import serializers
- from django.db import models
-
- class UserSerializer(serializers.ModelSerializer):
- gender_text = serializers.CharField(source="get_gender_display")
- #新增返回字段gender_text 值为自己的自定义的 相当于执行get_gender_display 获取choices 1为男
- class Meta:
- model = models.UserModel
- fields = ["name","age","gender","gender_text"]
- #此时取值为 [{"name":"XX","age":11,"gender":1,"gender_text":"男"}]
- from rest_framework import serializers
- from django.db import models
-
- class UserSerializer(serializers.ModelSerializer):
- #depart = serializers.CharField(source="depart") #这样会拿到关联depart所有数据
- depart = serializers.CharField(source="depart.title") #这样会拿到关联depart 部门名字
- class Meta:
- model = models.UserModel
- fields = ["name","age","gender","depart"]
- #此时取值为 {"name":"XX","age":11,"gender":1,"depart":"运营部"}
- from rest_framework import serializers
- from django.db import models
-
- class UserSerializer(serializers.ModelSerializer):
- ctime = serializers.CharField(format="%Y-%m-%d") #格式化时间字段
- class Meta:
- model = models.UserModel
- fields = ["name","age","gender","ctime"]
- #此时取值为 {"name":"XX","age":11,"gender":1,"ctime":"2022-11-11"}
- from rest_framework import serializers
- from django.db import models
-
- class UserSerializer(serializers.ModelSerializer):
- xxx=serializers.SerializerMethodField() #设置自定义方法 xxx为自定义的返回键名
- class Meta:
- model = models.UserModel
- fields = ["name","age","gender","ctime","xxx"]
-
- def get_xxx(self,obj):
- return "姓名:{},年龄{}".format(obj.name,obj.age)
SerializerMethodField会自动触发 名为 get_xxx的方法 xxx为自定义的返回值健名 值为此方法的返回值
参数 obj为当前对象
主要针对于 ManyToManyField和ForeignKey
- from rest_framework import serializers
- from django.db import models
-
- class UserSerializer(serializers.ModelSerializer):
- tag=serializers.SerializerMethodField() #设置自定义方法
- class Meta:
- model = models.UserModel
- fields = ["name","age","gender","ctime","tag"]
-
- def get_tag(self,obj):
- query=obj.tags.all() #获取tag关联表所有的值
- res=[{'id':tag.id,'caption':tag.caption} for tag in query] #循环取值 相当于tag字段值为数组
-
- return res
借用序列化器
- from rest_framework import serializers
- from django.db import models
-
- #部门序列化器
- class DepartSerializer(serializers.ModelSerializer):
- class Meta:
- model=models.DepartModel
- fields = "__all__"
- #标签序列号器
- class TagSerializer(serializers.ModelSerializer):
- class Meta:
- model=models.TagModel
- fields = "__all__"
-
- class UserSerializer(serializers.ModelSerializer):
- depart=DepartSerializer() #可以直接等于这个类的示例
- tag=TagSerializer(many=True) #多个加参数many
- class Meta:
- model = models.UserModel
- fields = ["name","age","gender","depart","tag"]
也是基于序列化器 做入参校验
定义序列化器 ,主要为参数required=True
- from rest_framework import serializers
- class UserSerializer(serializers.Serializer):
- name = serializers.CharField(required=True)
- age = serializers.IntegerField(required=True)
-
- from rest_framework import serializers
- from django.core.validators import RegexValidator
-
- class UserSerializer(serializers.Serializer):
- name = serializers.CharField(required=True,max_length=20,min_length=10)
- age = serializers.CharField(validators=[RegexValidator(r"\d+",message="年龄为数字")])
-
- # name = serializers.CharField(read_only=True,write_only=True)
max_length 字符串最大长度
min_length 最小长度
validators:[] 值为数组,自定义校验方法
-RegexValidator(正则,信息) 正则校验
-message失败后的原因
定义validate_+ 字段名字方法 返回bool 用exceptions.ValidationError抛出异常
- from rest_framework import serializers
- from django.core.validators import RegexValidator
- from rest_framework import exceptions
-
- class UserSerializer(serializers.Serializer):
- age = serializers.CharField(validators=[RegexValidator(r"\d+",message="年龄为数字")])
- class Meta:
- model=models.UserModel
- fields = "__all__"
- #定义方法
- def validate_age(self,value):
- if len(value)>2:
- raise exceptions.ValidationError("年龄不能超过两位数")
- #self.initial_data 获取当前所有参数
- return value
validate_执行顺序 根据代码先后 根据fields里数据顺序先后
全局钩子(额外校验 ,非单独 很少用)
- from rest_framework import serializers
- from django.core.validators import RegexValidator
- from rest_framework import exceptions
-
- class UserSerializer(serializers.Serializer):
- age = serializers.CharField(validators=[RegexValidator(r"\d+",message="年龄为数字")])
- #此方法为全局钩子函数 整体
- def validate():
- raise exceptions.ValidationError("全局钩子校验失败")
setting.py drf配置中 可新增字段"NON_FIELD_ERRORS_KEY":xxx 统一全局钩子 返回错误字段的键
方法一:判断 is_valid方法 返回值校验
- class UserView(APIView):
- def post(self,request,*args,**kwargs):
- ser=UserSerializer(data=request.data)#使用data 传入接收参数
- if ser.is_valid(): #is_valid方法返回不
- print(ser.validated_data)#validated_data方法获取 校验后的新json数据
- else:
- print(ser.errors) #校验不通过的原因
返回错误信息的是英文如何调整:
在setting.py中 新增字段 LANGUAGE_CODE="zh-hans" 处于REST_FRAMEWORK外面
英文的话值为 en-us
方法二:使用raise_exceptio参数
- class UserView(APIView):
- def post(self,request,*args,**kwargs):
- ser=UserSerializer(data=request.data)
- ser.is_valid(raise_exception=True): #raise_exception参数为True
该参数为True后 如果校验成功 程序继续执行 失败后 直接返回异常
缺点:返回值结构固定
基于字段extra_kwargs对象 添加 对象键名为 字段名 值为校验条件
- class UserSerializer(serializers.ModelSerializer):
- # 这里也可以定义 自定义字段校验 例:gender_text=xxxx fields就数组新增该字段
- class Meta:
- model = models.UserModel
- fields = ["name","age"]
- extra_kwargs = {
- "name": {"max_length": 5,"min_length": 1},
- "age":{....}
- }
和Serializer差不多 但是可以直接使用 save把数据存入数据库
- class UserView(APIView):
- def post(self,request,*args,**kwargs):
- ser=UserSerializer(data=request.data)
- if ser.is_valid():
- print(ser.validated_data)
- ser.save()#使用此方法 可以将数据直接存入数据库
- else:
- print(ser.errors)
使用此方法注意:但是用户传入的字段必须和数据库字段一一对应
1.如果校验必填字段少了这里会报错
解决:ser.save(age=11)此方法可以传入参数给默认值
2.如果用户多传字段
解决:ser.validated_data.pop("more") 可以让存储数据库 删除该字段
如果是ForeignKey 和ManyToManyField会自动校验 关系表和关联表 里有无该主键
如要做特殊校验 需要在钩子函数中处理 钩子函数中 会返回该对象 关联后的值 可直接操作
主要用户两个参数
read_only 仅仅序列化时读取 write_only仅仅校验写入数据库
使用时候 案例:
- class UserView(APIView):
- def post(self,request,*args,**kwargs):
- ser=UserSerializer(data=request.data) #校验
- if ser.is_valid():
- ser.save()
- #如果同时用一个 那么不用再次初始化UserSerializer 直接save之后使用data就是被同一个序列化器处理后的data
- return Response(ser.data) #返回序列化结果
- else:
- print(ser.errors)
序列化器和验证器
- class UserSerializer(serializers.ModelSerializer):
- gender_text = serializers.CharField(source="get_gender_display",read_only=True)#设定仅读
- class Meta:
- model = models.UserModel
- fields = ["name","age","gender","gender_text"]
- extra_kwargs = {
- "id": {"read_only": True},#设定仅读
- "gender": {"write_only":True}#设定仅写
- }
Serializer同理, 相当于用户传入 gender 1 那么给他返回对应的字典{gender_text:男} 不会有字段{gender:1}和id
在校验时候 id 和gender_text 为必填
应用场景:假如需要校验gender参数必须传1或者2,返回时却要返回关联的关系(男或女)
- from collections import OrderedDict
- from rest_framework.fields import SkipField
- from rest_framework.relations import PKOnLyObject
-
- class UserSerializer(serializers.ModelSerializer):
- class Meta:
- model = models.UserModel
- fields = ["name","age","gender"]
- extra_kwargs = {
- "id": {"read_only": True}#设定仅读
- }
- #自定义钩子
- def to_representation(self, instance):
- ret = OrderedDict()
- fields = self._readable_fields
-
- for field in fields:
- #这里相当于 函数名字按照xx 匹配 使用时候就是 xx_键名
- if hasattr(self,'xx_%s' % field.field_name):
- value = getattr(self,'xx_%s' % field.field_name)(instance)
- ret[field.field_name] = value
- else:
- try:
- attribute = field.get_attribute(instance)
- except SkipField:
- continue
- check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
- if check_for_none is None:
- ret[field.field_name] = None
- else:
- ret[field.field_name] = field.to_representation(attribute)
- return ret
-
- #使用
- def xx_gender(self,obj):
- return obj.get_gender_display() #结果为男
xx_%s 正则匹配 可以为自定义方法取个名字
在使用的时候 用xx_参数名 就i可以
定义一个工具函数 hook.py
- from collections import OrderedDict
- from rest_framework.fields import SkipField
- from rest_framework.relations import PKOnlyObject
- from rest_framework import serializers
-
- class HookSerializer(serializers.ModelSerializer):
- def to_representation(self, instance):
- ret = OrderedDict()
- fields = self._readable_fields
-
- for field in fields:
- #这里相当于 函数名字按照xx 匹配 使用时候就是 xx_键名
- if hasattr(self,'xx_%s' % field.field_name):
- value = getattr(self,'xx_%s' % field.field_name)(instance)
- ret[field.field_name] = value
- else:
- try:
- attribute = field.get_attribute(instance)
- except SkipField:
- continue
- check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
- if check_for_none is None:
- ret[field.field_name] = None
- else:
- ret[field.field_name] = field.to_representation(attribute)
- return ret
使用时候继承HookSerializer类就可以
- from api.utlis.hook import HookSerializer#引入HookSerializer
- class UserSerializer(HookSerializer,serializers.ModelSerializer):
- class Meta:
- #....
- def xx_():
-
为了方便返回格式统一封装response 新建utils文件夹 新建response.py文件
- from rest_framework.response import Response
-
-
- class APIResponse(Response):
- """
- 二次封装Response
- """
- def __init__(self, code=0, data_msg='ok', data=None, http_status=None, headers=None, exception=False,
- **kwargs):
- results = {
- 'code': code,
- 'msg': data_msg
- }
-
- # 如果返回结果就将返回结果赋给到data中
- if data is not None:
- results["data"] = data
-
- # 如果传递其他的参数,将会被放到kwargs中被接收
- if kwargs is not None:
- for k, v in kwargs.items():
- # 采用反射的方法,去赋值
- setattr(results, k, v) # results[k] = v
- super().__init__(data=results, status=http_status, headers=headers, exception=exception)
- #使用
- return APIResponse(200,'操作成功') #需要返回data 加上data=xxx
-
- #返回样式
- {
- "code": 200,
- "msg": "操作成功"
- }
utils文件夹 新建logger.py文件,采用loguru 得先安装
pip install loguru
- """
- # @software: PyCharm
- # @file:middle_log.py
- # @project: 封装日志
- """
- import os
- import time
- from loguru import logger
- from pathlib import Path
- import sys
-
- t = time.strftime("%Y_%m_%d")
-
- BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
-
-
- class Logger:
- __instance = None
- logger.remove()
- logger.add(sys.stdout,
- format="<green>{time:YYYYMMDD HH:mm:ss}</green> | " # 颜色>时间
- "{process.name} | "
- "{thread.name} | "
- "<cyan>{module}</cyan>.<cyan>{function}</cyan>" # 模块名.方法名
- ":<cyan>{line}</cyan> | " # 行号
- "<level>{level}</level>: " # 等级
- "<level>{message}</level>", # 日志内容
- )
- logger.add(os.path.join(BASE_DIR, f"log/interface_log_{t}.log"), format='{time:YYYYMMDD HH:mm:ss} - ' # 时间
- "{process.name} | " # 进程名
- "{thread.name} | " # 进程名
- '{module}.{function}:{line} - {level} -{message}',
- rotation="500MB", encoding="utf-8", enqueue=True,
- retention="10 days")
-
- def __new__(cls, *args, **kwargs):
- if not cls.__instance:
- cls.__instance = super(Logger, cls).__new__(cls, *args, **kwargs)
-
- return cls.__instance
-
- def info(self, msg):
- return logger.info(msg)
-
- def debug(self, msg):
- return logger.debug(msg)
-
- def warning(self, msg):
- return logger.warning(msg)
-
- def error(self, msg):
- return logger.error(msg)
-
-
- loggings = Logger()
- if __name__ == '__main__':
- loggings.info("2222222222222")
- loggings.debug("333333333333")
- loggings.warning("444444444444")
- loggings.error("555555555555")
utils文件夹 新建exceptions.py文件
自定义 异常处理人为抛出异常,系统异常统一处理
- from rest_framework.views import exception_handler as drf_exception_handle
- from rest_framework.response import Response
- from utils.logger import logger
- from utils.response import APIResponse
-
-
- class MyError(Exception):
- def __init__(self, value):
- self.value = value
-
- def __str__(self):
- return repr(self.value)
-
-
- def exception_handler(exc, context):
- request = context.get('request')
- view = context.get('view')
- token = request.headers.get("token") or "未登录用户"
- if isinstance(exc, MyError):
- logger.warning('请求失败:用户:【%s】,请求地址:【%s】,失败原因:【%s】' % (token, request.get_full_path(), str(exc)))
- return APIResponse(1001, str(exc), data=None)
- logger.error('用户:【%s】,使用:【%s】 请求,请求:【%s】 地址,视图函数:【%s】,错误:【%s】' % (
- token, request.method, request.get_full_path(), str(view), str(exc)
- ))
- return APIResponse(999, '服务器出错,请联系系统管理员')
setting.py文件中
- REST_FRAMEWORK = {
- 'EXCEPTION_HANDLER': 'utils.exceptions.exception_handler',
- }
使用jwt 先安装
pip install djangorestframework-jwt
setting.py新增以下内容
- import datetime
- INSTALLED_APPS = [
- ...
- 'rest_framework_jwt'#注册应用
- ]
-
- REST_FRAMEWORK = {
- 'DEFAULT_AUTHENTICATION_CLASSES': ( #全局认证组件
- 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
- )
- }
- JWT_AUTH = {
- 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),#过期时间7天
- 'JWT_ALLOW_REFRESH': True,#允许刷新
- }
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',
- import os
- from pathlib import Path
-
- #项目根路径
- BASE_DIR = Path(__file__).resolve().parent.parent
- # 秘钥,涉及到加密的django中,都会用它
- SECRET_KEY = "django-insecure-#gnxfjf&9@wokaeb33p5puv+^==#ky*6zp(&rhcb!s1+iw#w_r"
-
- #代码可以热更新 项目上线,要改成false
- DEBUG = True
- #允许我的项目部署在哪个ip地址上,* 表示允许部署在所
- ALLOWED_HOSTS = ["*"]
-
-
- # django 是多个app组成的,里面配置app,默认带的app,django内置的app
- #内置app:
- # admin后台管理,
- # auth权限管理,
- # contenttypes表中存app也表的关系,
- # sessions session表,django的session相关
- # messages:消息框架,flask讲闪现,是一样的东西
- # staticfiles:静态资源的
- INSTALLED_APPS = [
- # "django.contrib.admin",
- # "django.contrib.auth",
- # "django.contrib.contenttypes",
- # "django.contrib.sessions",
- # "django.contrib.messages",
- "django.contrib.staticfiles",
- "rest_framework",
- "api.apps.ApiConfig"
- ]
- # 中间件
- MIDDLEWARE = [
- "django.middleware.security.SecurityMiddleware", # 安全相关中间件
- # "django.contrib.sessions.middleware.SessionMiddleware", # session相关中间件
- "django.middleware.common.CommonMiddleware", # 带不带 / 问题
- "django.middleware.csrf.CsrfViewMiddleware",# csrf 认证,生成csrf串
- # "django.contrib.auth.middleware.AuthenticationMiddleware", # 用户认证
- # "django.contrib.messages.middleware.MessageMiddleware", #消息框架相关
- "django.middleware.clickjacking.XFrameOptionsMiddleware",
- ]
- # 根路由
- ROOT_URLCONF = "admin.urls"
- # 模板文件
- TEMPLATES = [
- {
- "BACKEND": "django.template.backends.django.DjangoTemplates",
- "DIRS": [],
- "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",
- ],
- },
- },
- ]
- # 项目运行的配置---》项目上线运行,使用uwsgi 运行 application()
- WSGI_APPLICATION = "admin.wsgi.application"
-
- # 数据库配置,mysql 主从搭建完
- DATABASES = {
- "default": {
- "ENGINE": "django.db.backends.mysql",
- "NAME": "test", # 数据库名字
- "USER": "root", # 账号
- "PASSWORD": "3735199", # 密码
- "HOST": "127.0.0.1", # 数据库地址
- "PORT": 3306, # 端口一般默认
- }
- }
-
-
- 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",
- },
- ]
-
- # 国际化 语言时间等
- TIME_ZONE = 'Asia/Shanghai'
- USE_I18N = True
- USE_L10N = False
- USE_TZ = False
-
- #返回校验错误为中文
- LANGUAGE_CODE = "zh-hans"
- # 静态资源
- STATIC_URL = "static/"
-
- #更改了id字段的字段类型
- DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
- #DRF配置
- REST_FRAMEWORK = {
- "UNAUTHENTICATED_USER": None, # 配置匿名用户
- 'APPEND_SLASH': False, # 去掉url尾部斜杠
- "DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S",
- 'EXCEPTION_HANDLER': 'utils.exceptions.exception_handler',
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。