目录
- 一、Q查询进阶操作
- 二、ORM查询优化
- 三、ORM事务操作
- 四、ORM常用字段类型
- 五、ORM常用字段参数
- 六、Ajax
- 七、数据编码格式(Content-Type)
- 八、ajax携带文件数据
- 九、ajax补充说明
- 十、多对多三种创建方式
- 十一、django内置序列化组件(drf前身)
- 十二、批量操作数据
- 十三、分页器思路
- 十四、自定义分页器的使用
- 十五、form组件
- 十六、forms组件渲染标签
- 十七、forms组件展示信息
- 十八、forms组件校验补充
- 十九、forms组件参数补充
- 二十、forms组件源码剖析
- 二十一、modelform组件
- 二十二、django中间件
- 二十三、基于django中间件的功能设计
- 二十四、cookie与session简介
- 二十五、django操作cookie
- 二十六、django操作session
- 二十七、csrf跨站请求伪造
- 二十八、csrf校验策略
- 二十九、csrf相关装饰器
- 三十、auth认证模块
- 三十一、auth认证相关模块及操作
- 三十二、扩展auth_user表
一、Q查询进阶操作
Q查询我们在上周初步学习的时候我们了解到他主要是用于实现查询过程中的一些逻辑运算符的使用,因为ORM中括号内的参数默认是and连接方式进行查询的。
而这里我们介绍的进阶操作,其实就是类似面向对象中的反射方法,实现的功能就是通过获取用户输入的字符串执行,执行对应的查询语句。
from django.db.models import Q
q_obj = Q() # 1.产生q对象
q_obj.connector = 'or' # 默认多个条件的连接是and可以修改为or
q_obj.children.append(('pk', 1)) # 2.添加查询条件
q_obj.children.append(('price__gt', 2000)) # 支持添加多个
res = models.Book.objects.filter(q_obj) # 查询支持直接填写q对象
print(res)
二、ORM查询优化
这里补充了一些方法并介绍了一些orm查询的特性
1.在进行orm查询的时候,他是惰性查询,即当我们执行查询语句的时候,如果不输出结果,orm代码就不回运行
2.orm查询自带分页处理,即查询语句后面会自动跟上limit
3.only和defer
only方法是在进行查询的时候写在末尾的,这里需要提一下orm查询的一些原理,orm是把数据库中的一个表中所有的数据封装到对象中了,因此我们才能使用各种的方法进行快捷查询,而封装数据就导致了效率会变慢,这里的only就是执行需要封装的字段,defer则跟他相反,写在括号内的是不需要封装的字段.
4.select_related与prefetch_related
这里的select_related和prefetch_related都是针对一对多或一对一的表查询进行使用的,select_related相当于是连表查询,prefetch_related相当于是子查询
三、ORM事务操作
在将orm的事务操作之前,我们需要回想一下mysql中的事务的知识点,比如四大特性:原子性、一致性、隔离性、持久性,还有查询时的关键字:start transaction;rollback;commit;savepoint,等等.
学会了mysql中的事务操作,学orm的事务操作就很简单了,django的orm主要提供了三种操作事务的方式:
方式一:
在配置文件中添加配置,这时候的事务是全局有效的
方式二:
在需要执行事务的视图函数上写上事务的装饰器,这时候作用范围是一个事务函数
方式三:
使用with上下文管理操作事务,这时候的事务作用范围只有with内的代码,使用的较多
同时有个小现象需要注意.在使用前面两种方式的时候如果我们的视图函数不适用三板斧返回信息,不会执行事务的回退,而第三种则会回退
四、ORM常用字段类型
常用字段主要就是对应mysql中的数据类型,还有外键的字段.
名称 | 含义 |
---|---|
AutoField() | Int自增列 必须填入参数 primary_key=True 当model中如果没有自增列 则自动会创建一个列名为id的列 |
CharField() | 字符类型 必须提供max_length参数 max_length表示字符长度 |
IntegerField() | 一个整数类型 范围在 -2147483648 to 2147483647 (一般不用它来存手机号(位数也不够) 直接用字符串存) |
BigIntegerField() | 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 |
DateField() | 日期字段 日期格式 YYYY-MM-DD 相当于Python中的datetime.date()实例 |
DateTimeField() | 日期时间字段 格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 相当于Python中的datetime.datetime()实例 |
DecimalField() | 10进制小数 参数 max_digits 小数总长度 decimal_places,小数位长度 |
EmailField() | 字符串类型 Django Admin以及ModelForm中提供验证机制 |
BooleanField() | 布尔值类型 传布尔值存数字0或1 |
TextField() | 文本类型 存储大段文本 |
FileField() | 字符串 路径保存在数据库 文件上传到指定目录 参数 upload_to = " " 上传文件的保存路径 storage = None 存储组件 默认django.core.files.storage.FileSystemStorage |
ForeignKey() | 外键类型在ORM中用来表示外键关联关系 一般把ForeignKey字段设置在 '一对多’中’多’的一方 ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系 |
OneToOneField() | 一对一字段 通常一对一字段用来扩展已有字段 通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联 |
ManyToManyField() | 简单来说就是在多对多表关系并且这一张多对多的关系表是有Django自动帮你建的情况下 下面的方法才可使用create add set remove clear |
五、ORM常用字段参数
常用字段参数就是在使用字段的时候,内部可以添加的起某些作用的参数.
名称 | 含义 |
---|---|
primary_key | 主键 |
verbose_name | 注释 |
max_length | 字段长度 |
max_digits | 小数总共多少位 |
decimal_places | 小数点后面的位数 |
auto_now | 每次操作数据自动更新事件 |
auto_now_add | 首次创建自动更新事件后续不自动更新 |
null | 允许字段为空 |
default | 字段默认值 |
unique | 唯一值 |
db_index | 给字段添加索引 |
choices | 当某个字段的可能性能够被列举完全的情况下使用。如:性别、学历、工作状态、... |
to | 关联表 |
to_field | 关联字段(不写默认关联数据主键) |
on_delete | 当删除关联表中的数据时,当前表与其关联的行的行为。 |
六、Ajax
ajax并不是一门新技术,跟之前的一些知识点一样,只是某些知识点整合起来变成了一格新的功能,同时ajax我们学习的是jquery版本,这就导致我们在使用的时候不能忘记导入jquery的js文件.
同时ajax主要实现的功能可以概括为可以实现异步交互,提交数据不需要刷新网页(局部刷新).
$.ajax({
url:'', // 后端地址 三种填写方式 与form标签的action一致
type:'post', // 请求方式 默认也是get
data:{'v1':v1Val, 'v2':v2Val}, // 发送的数据
success:function (args) { // 后端返回结果之后自动触发 args接收后端返回的数据
$('#d3').val(args)
}
})
七、数据编码格式(Content-Type)
这里可以说是一个小知识点,只是现在来做总结.
之前刚学习的时候我们得知浏览器可以发送get和post请求,post请求需要用到form表单.
接着,在form表单传输文件的时候我们了解到了传输文件的时候需要使用multipart/form-data这个类型的数据传输格式.这时候就可以进行一个小总结,form表单传输数据只有两种格式multipart/form-data和urlencoded(这是默认的传输模式).
接着我们看ajax,ajax默认传输格式也是urlencoded,但是form表单不能传输json格式的数据,而ajax可以.
$.ajax({
url:'',
type:'post',
data:JSON.stringify({'name':'Like', 'pwd':123}), // 必须json格式。传输数据的时候千万不要骗代码,代码没有这么聪明,如果我们把这里的数据值换成字典就会报错。
contentType:'application/json', // 要制定模式 默认Urlencoded
success:function (args) {
}
八、ajax携带文件数据
而在传输文件的时候ajax需要添加两个配置信息
<script>
$('#d3').click(function () {
// 1.先产生一个FormData对象
let myFormDataObj = new FormData();
// 2.往该对象中添加普通数据
myFormDataObj.append('name', 'jason');
myFormDataObj.append('age', 18);
// 3.往该对象中添加文件数据
myFormDataObj.append('file', $('#d2')[0].files[0])
// 4.发送ajax请求
$.ajax({
url:'',
type:'post',
data:myFormDataObj,
// ajax发送文件固定的两个配置
contentType:false,
processData:false,
success:function (args){
alert(args)
}
})
})
</script>
九、ajax补充说明
1.在后端可以用is_ajax判断数据是否为ajax请求发出
2.当我们使用三板斧返回数据的时候,都不会直接被网页接受,而是会被ajax接受,即不能影响整个网页.
3.使用HttpResponse的时候ajax如果传json格式的数据,会在前端变成字符串类型的数据,如果用JsonResponse则会被前端自动解码,这时候就可以在html代码中用点的方式获取一些数据
4.使用ajax的时候我们的数据通常都是返回类似字符串的数据
这时候我们可以自己封装数据,也可以使用使用一些模块(下面会提到)
十、多对多三种创建方式
第一种就是使用ManyToManyField字段创建,封装度高,扩展性低
第二种则是全程由我们创建表,在第三张关系表中使用ForeignKey对另外两张表中的字段进行关联,封装度低,扩展度高,可以自定义一些别的字段
第三种则是半自动创建,我们在创建的时候正常使用ManyToManyField字段,同时我们也自己手动创建第三张关系表,在ManyToManyField的参数中设置需要使用的第三张关系表
authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
十一、django内置序列化组件(drf前身)
上面说到ajax的数据一般都是用一个类似字典格式的数据来输出的,这里就是介绍了serializers模块
# 导入内置序列化模块
from django.core import serializers
# 调用该模块下的方法,第一个参数是你想以什么样的方式序列化你的数据
res = serializers.serialize('json', book_queryset)
return HttpResponse(res)
十二、批量操作数据
当我们执行批量添加数据或是批量修改数据的时候,如果一条条添加,效率很低,但是我们把数据封装到对象中后使用下方的两个方法进行添加,就会变得速度很快
models.Books01.objects.bulk_create
models.Books01.objects.bulk_update
十三、分页器思路
所谓的分页器就是网页下方展示很多记录的时候用来分页的那个东西,这里推导的时候主要有几个重要的点,展示页数的时候需要进行判断,然后页数在展示的时候不能全部展示,然后页数的的上下限应该用变量弄成自动变化的,然后开头和末尾要自定义一些,不能出现负数或是超过显示的数,接着就是让分页器居中.
十四、自定义分页器的使用
这里主要是要学会使用鸡哥提供的自定义分页器,在一个py文件中导入自定义分页器的代码即可,使用方式类似推导中的分页器
十五、form组件
前端我们可以用form表单向后端提交数据,到那时很多时候我们都需要对用户提交的数据进行校验,因此这里介绍了form组件,他主要有三大块功能,自动校验数据,自动生成标签,自动展示信息.
from django import forms
class MyForm(forms.Form):
username = forms.CharField(min_length=3, max_length=8) # username字段最少三个字符最大八个字符
age = forms.IntegerField(min_value=0, max_value=200) # 年龄最小0 最大200
email = forms.EmailField() # 必须符合邮箱格式
上面是form组件的定义,我们发现他的定义其实是一张模型表,我们通过设置字段中的一些参数达到校验的目的,在后端可以先使用is_valid(结果时布尔值)进行校验数据是否符合要求(其他功能需要先执行is_valid才能有结果),然后可以使用cleaned_data进行查询符合条件的数据,用errors查询不符合条件的数据.这里有个小知识点,当数据多传的时候,多出来的数据时不会参与判断的,少传数据却是会变成错误.
十六、forms组件渲染标签
有三种渲染方式,通过渲染可以在前端页面根据后端模型表的字段生成form表单.
第一种方式
{# {{ form_obj.as_p }}#}
{# {{ form_obj.as_ul }}#}
{# {{ form_obj.as_table }}#}
封装度过高,扩展性差(分别表示用不同的标签渲染)
第二种方式
{# {{ form_obj.username.label }}#}
{# {{ form_obj.username }}#}
{# {{ form_obj.age.label }}#}
{# {{ form_obj.age }}#}
{# {{ form_obj.email.label }}#}
{# {{ form_obj.email }}#}
封装度过低,扩展性好
第三种方式
<form action="" method="post" novalidate>
{% for form in form_obj %}
<p>
{{ form.label }}
{{ form }}
<span>{{ form.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit">
</form>
用for循环自动获取数据渲染,同时扩展性也很好
十七、forms组件展示信息
使用forms组件展示信息实现的其实就是错误提示这个功能,当我们发现用户数据的数据格式不对的时候需要给出提示,之前讲的ajax可以做到的局部刷新,forms组件也可以,不过需要一些代码进行逻辑判断
后端不同请求返回的forms对象一定要是相同的变量名
def ab_forms_func(request):
# 1.产生一个空对象
form_obj = MyForm()
if request.method == 'POST':
form_obj = MyForm(request.POST) # request.POST可以看成是一个字典 直接传给forms类校验 字典中无论有多少键值对都没关系 只在乎类中编写的
if form_obj.is_valid(): # 校验数据是否合法
print(form_obj.cleaned_data)
else:
print(form_obj.errors)
# 2.将该对象传递给html文件
return render(request, 'formsPage.html', locals())
html部分代码
{% for form in form_obj %}
<p>
{{ form.label }}
{{ form }}
<span>{{ form.errors.0 }}</span>
</p>
{% endfor %}
在进行数据筛选的时候也可以使用正则,但是要先导入模块
from django.core.validators import RegexValidator
phone = forms.CharField(
validators=[
RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头'),
],
)
如果我们想修改错误信息的语言,我们可以使用error_messages进行自定义,也可以在配置文件中修改语言环境,达到修改语言的目的.
age = forms.IntegerField(min_value=0, max_value=200, label='年龄',
error_messages={
'min_value':'年龄不能小于0岁',
'max_value':'你他喵的200岁以上?',
'required':'年龄不能为空 你妹的'
}
)
十八、forms组件校验补充
这里主要介绍了钩子.
forms组件针对字段数据的校验 提供了三种类型的校验方式(可以一起使用)
第一种类型:直接填写参数 max_length
第二种类型:使用正则表达式 validators
第三种类型:钩子函数 编写代码自定义校验规则
钩子分成局部钩子和全局钩子,这里的钩子就相当于从一块果冻中勾一块出来检查,检查完了给他塞回去.
全局钩子就相当于是所有的数据都拿出来检查,然后再放回去,
局部钩子和全局钩子的返回值都需要返回检查的数据,虽然不返回也会被代码自动返回,但是会多执行一些代码.
十九、forms组件参数补充
min_length 最小字符
max_length 最大字符
min_value 最小值
max_value 最大值
label 字段注释
error_messages 错误提示
validators 正则校验器
initial 默认值
required 是否必填
widget 控制标签的各项属性
widget=forms.widgets.PasswordInput(attrs={'class': 'form-control', 'username': 'jason'})
二十、forms组件源码剖析
我们在前面说过is_valid执行后别的forms组件的方法才有结果.
因此我们点进他的源码进行分析,可以发现他的返回结果分成两部分,前面的部分不重要,主要是判断是否有数据,有数据结果就是True,后面部分才是重点,我们发现后面的errors方法的代码中if条件的判断是隐藏属性errors为None的时候会执行full_clean,二这full_clean就是我们研究的下一个重点
查看他的源码发现他内部有很多方法,前面两个是产生空列表或是对象,后面三个则是用钩子函数校验数据同时校验我们是否返回了钩子执行时勾的数据,没有返回就会帮我们用异常处理返回,到此位置我们就基本明白了他的结果为什么是布尔值,
二十一、modelform组件
通过他的名字我们就可以知道他是model和form结合起来的一个组件,我们在后端进行校验数据,主要是为了方式在写入数据库的时候出现报错,使用别的方式进行校验则太慢了,modelform组件专门就是处理这个问题的,
使用的时候也是类似forms组件需要先定义一个模型表,这个模型表需要继承from django import forms中的forms.ModelForm父类
下面是一些他内部参数的介绍
model = models.Book # 对应的Model中的类
fields = "__all__" # 字段,如果是__all__,就是表示列出所有的字段
exclude = None # 排除的字段
labels = None # 提示信息
help_texts = None # 帮助提示信息
widgets = None # 自定义插件
error_messages = None # 自定义错误信息
二十二、django中间件
在提到中间件的时候就会想到Django生命周期流程图,我们所有的django数据进出都需要通过中间件的检查.
之前我们在使用post传输数据的时候说到需要注释一个配置信息,他就是一个中间件的配置注册信息.
通常来说我们都只自定义中间件的,而这些中间件的代码写在某个文件夹中统一存储,一个中间件使用一个py文件,然后他内部主要有5个方法
process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
process_request方法
主要是在接收了请求后执行的,他处理完后数据才会给到视图层,有多个中间件的时候会根据注册表中的注册顺序顺序执行,如果某个中间件返回的结果不是request这个参数,而是HttpResponse,就会停止执行后面的中间件,而是直接调用这个中间件的process_response结束运行.
process_response
他是返回结果时候执行的即,视图函数执行后返回数据的时候会出发他,他的执行顺序是根据注册表中的顺序的返序执行
同时想要其他中间件也能继续执行,需要返回参数中的response参数,然后如果返回值写HttpResponse就会在这个中间件执行后停止.
下面三个方法了解即可
1.process_view
process_view方法是在Django路由系统之后(路由匹配成功之后),视图系统之前(执行视图函数/类之前)执行的,执行顺序按照MIDDLEWARE中的注册顺序从前到后顺序执行的
2.process_exception
视图函数/类执行报错自动触发(触发顺序是根据注册顺序从后往前执行)
3.process_template_response
视图函数/类返回的HttpResponse对象含有render属性(需要在内部定义一个render函数,并且要把这个函数名称绑定给render属性)并且对应一个方法的时候自动触发(触发顺序是根据注册顺序从后往前执行)
二十三、基于django中间件的功能设计
在学习了django中间件后我们发现他的那个使用方式很方便,需要用的还是注册表中注册即可,不用的时候注释掉即可
因此我们想着模仿一下
这里主要是用importlib模块实现功能的
import importlib
s1 = 'bbb.b' # aaa.bbb.ccc.b
res = importlib.import_module(s1) # 相当于from aaa.bbb.ccc import b
print(res) # <module 'bbb.b' from 'D:\\pythonProject03\\djangomiddle\\bbb\\b.py'>
'''注意字符串的结尾最小单位只能是py文件 不能是py文件里面的变量名'''
思考django中间件是如何处理的(最小单位是类名)
我们创建一个项目,然后用软件开发目录规范设置一个settings文件和视图文件夹,把视图文件夹内的模块,写到双下init中调用,调用的时候是根据配置文件中的注册信息来执行的,通过这样的方式达到模仿中间件的使用方式执行功能的目的.
二十四、cookie与session简介
我们回顾知识点,浏览器跟后端是通过http协议交互的,http协议的四大特性是
1.基于请求响应
2.基于TCP、IP作用于应用层之上的协议
3.无状态
不保存客户端的状态
4.无连接
因此我们可以想到做一些需要登陆的网页,就不能实现状态的记录,因此介绍了cookie和session
cookie
保存在客户端,存储与用户状态相关的信息
session
保存在服务端,存储与用户状态相关的信息
这里我们也可以看出来,session的工作是需要依靠cookie的,毕竟没有cookie就无法记录状态
二十五、django操作cookie
这是给前端设置cookie,即创建cookie
views
def set_cookie(request):
obj = HttpResponse('Happy Happy !!!')
obj.set_cookie('name', 'James')
return obj
如果我们想在后端查看前端传的cookie信息,需要使用request.COOKIES方法
通过上面的两个方法,我们已经可以用cookie获取用户的数据了,但是我们需要用一个装饰器来校验用户信息是否正确.
二十六、django操作session
设置session的代码:
request.session['key'] = value
当我们在设置session的时候会执行下列操作:
- 1.生成一个随机字符串
- 2.对value数据做加密处理 并在django_session表中存储
随机字符串>>>加密数据 - 3.将随机字符串也发送一份给客户端保存(cookie)
sessionid:随机字符串
获取session的代码
request.session.get('key')
在获取session的时候会执行下列操作
- 1.自动获取随机字符串
- 2.去django_session表中根据随机字符串获取加密的数据
- 3.自动解密数据并处理到request.sesion.get()中
这里需要提一下,我们在使用session的时候是需要用到数据库中的一个session表的,但是他很随性,有很多中存储方式,我们使用mysql的时候就是需要对这张表进行数据库迁移,才能使用他
二十七、csrf跨站请求伪造
csrf我们在课上是通过模仿一个钓鱼网站来介绍的
基本情况就是,我们通过隐藏标签,模仿真的网页,让用户转账,然后我们在他转账的时候访问真实的网页提交数据,只是把数据中的转账对象换成自己的,这时候我们意识到数据虽然有了cookie进行校验身份但是还不够安全
二十八、csrf校验策略
因为有csrf跨站请求伪造的存在,因此我们需要对他进行csrf策略进行校验
他的使用很简单,在html文件中写一下就有了
form表单内部添加 {% csrf_token %}
但是ajax请求csrf策略的时候知识点比较多
他有三种请求方式(前端朝后端发数据的时候需要带上csrf的数据,这里是三重携带方式)
// 方式1:自己动手取值 较为繁琐
{#data:{'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()},#}
// 方式2:利用模板语法自动获取(一定要用引号引起来)
{#data:{ 'csrfmiddlewaretoken':'{{ csrf_token }}','username':'jason'},#}
// 方式3:直接引入一个js脚本即可(官网提供的)
参考:https://www.cnblogs.com/Dominic-Ji/p/9234099.html
ps:
当我们使用csrf的时候通常一台电脑产生的csrf只能被那台电脑识别,换成服务器也是一样的.
二十九、csrf相关装饰器
我们可以看到,上面就介绍的csrf启动之后就会让所有的视图都需要进行csrf认证,这明显不符合实际使用情况,
因此当我们想进行自定义的时候就需要用上csrf相关的装饰器
FBV添加装饰器的方式(与正常函数添加装饰器一致)
from django.views.decorators.csrf import csrf_exempt, csrf_protect
# @csrf_exempt
想让csrf校验检测就使用装饰器@csrf_protect,不想让csrf校验检测就使用@csrf_exempt
@csrf_protect
def transfer_func(request):pass
CBV添加装饰器的方式(与正常情况不一样 需要注意)
主要有三种方式
# @method_decorator(csrf_protect, name='post') # 方式2:单独生效
class MyView(views.View):
@method_decorator(csrf_protect) # 方式3:整个类中生效
这里需要会去看CBV的源码剖析,CBV源码内起作用的也是dispaly,通过反射执行字符串表示的功能,因此第三种方式是整个类中生效的。
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
# @method_decorator(csrf_protect) # 方式1:单独生效
def post(self, request):
return HttpResponse('from cbv post view')
注意有一个装饰器是特例只能有一种添加方式>>>:csrf_exempt
只有在dispatch方法处添加才会生效
三十、auth认证模块
这里的auth认证模块,就是我们初次介绍django中路由层中admin路由,这里也需要提一下,auth相关的表也是在数据库中需要用数据库迁移命令映射操作的.用到的表叫User表,auth相关的所有操作都是在这张表中完成的
三十一、auth认证相关模块及操作
基于这张表的所有操作,也就是说基于这张表执行的所有orm操作都是跟auth模块相关的.这里列举一些功能:
导入auth模块
from django.contrib import auth
导入数据库中的表
from django.contrib.auth.models import User
用户注册命令
User.objects.create_user(username=username, password=password)
判断用户名和密码的命令
from django.contrib.auth.models import User
1.普通用户
user = User.objects.create_user(username='用户名',password='密码',...)
2.管理员用户,必须要有email
user = User.object.create_superuser(username='用户名',password='密码',email='邮箱',...)
判断用户是否登陆
request.user.is_authenticated
三十二、扩展auth_user表
当我们在使用这个auth模块的user表时,我们会觉得不满足,想进行拓展,这里有两种拓展方式,第一种就是用一对一的外键字段进行关联,这样等与我在你的基础上进行了扩展
第二种而是就是用我们自己编写的表进行替换
显示编写一个模型表
然后这个模型表需要继承他的User表当父类
然后还要去配置文件中修改使用的表名称.
标签:总结,第十三,form,django,forms,ajax,csrf,数据 From: https://www.cnblogs.com/wxlxl/p/17112428.html