一、基本使用
1.urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# form组件渲染标签
path('ab_form/', views.ab_form),
]
2.views.py
from django.shortcuts import render, HttpResponse
from django import forms
class MyForm(forms.Form):
# 字符串类型最小3位最大8位
username = forms.CharField(min_length=3, max_length=8, label='用户名', error_messages={
'min_length': '用户名最少3位',
'max_length': '用户名最多8位',
'required': '用户名不能为空'
})
password = forms.CharField(min_length=3, max_length=8, label='确认密码', error_messages={
'min_length': '密码最少3位',
'max_length': '密码最多8位',
'required': '密码不能为空'
})
confirm_password = forms.CharField(min_length=3, max_length=8, label='密码', error_messages={
'min_length': '确认密码最少3位',
'max_length': '确认密码最多8位',
'required': '确认密码不能为空'
})
# email字段必须符合邮箱格式
email = forms.EmailField(label='邮箱', error_messages={
'invalid': '邮箱格式不正确',
'required': '邮箱不能为空'
})
def ab_form(request):
# 1.先生成一个空对象
form_obj = MyForm()
if request.method == 'POST':
# 获取用户数据并且校验
form_obj = MyForm(request.POST)
# 判断数据是否合法
if form_obj.is_valid():
# 如果合法 操作数据库存储数据
return HttpResponse('OK')
# 不合法 有错误
# 2.直接将该空对象传递给html页面
return render(request, 'ab_form.html', locals())
3.校验数据
# 在pycharm里面已经帮你准备一个测试环境 python控制台
form app01 import views
# 1.将带有校验的数据组织成字典的形式传入即可
# 校验数据只校验类中出现的字段,多传不影响,多传的字段直接忽略
form_obj = views.MyForm({'username':'zero','password':'123','email':'123'})
# 2.判断数据是否合法 该方法只有在所有的数据全部合法的情况下才会返回True
form_obj.is_valid()
False
# 3.查看所有校验通过的数据
form_obj.cleaned_data
{'username': 'zero', 'password': '123'}
# 4.查看所有不符合校验规则以及不符合的原因
form_obj.errors
{'email': ['Enter a valid email address.']}
4.form组件展示错误信息
# ab_form.html
# novalidate:当提交表单时不对表单数据(输入)进行验证
<form action="" method="post" novalidate>
{#<p>第一种渲染方式:代码书写少,封装程度太高,不便于后续的拓展,一般情况下只在本地测试使用</p>#}
{#{{ form_obj.as_p }}#}
{#{{ form_obj.as_ul }}#}
{#{{ form_obj.as_table }}#}
{#<p>第二种渲染方式:可拓展行强,但是需要书写的代码太多,一般情况下不用</p>#}
{#<p>{{ form_obj.username.label }}:{{ form_obj.username }}</p>#}
{#<p>{{ form_obj.password.label }}:{{ form_obj.password }}</p>#}
{#<p>{{ form_obj.email.label }}:{{ form_obj.email }}</p>#}
<p>第三种渲染方式:推荐使用</p>
{% for form in form_obj %}
<p>
{{ form.label }}:{{ form }}
<span style="color: red">{{ form.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit">
</form>
二、forms组件钩子函数
钩子函数(HOOK):在特定的节点自动触发完成相应操作,钩子函数在forms组件中能够自定义校验规则。
在forms组件中有两类钩子
1.局部钩子:当你需要给单个字段增加校验规则的时候可以使用
2.全局钩子:当你需要给多个字段增加校验规则的时候可以使用
# 钩子函数 在类(MyForm)里面书写方法即可
# 局部钩子
def clean_username(self):
# 获取到用户名
username = self.cleaned_data.get('username')
if '666' in username:
# 提示前端展示错误信息
self.add_error('username', '用户名不能出现666')
# 将钩子函数去出来数据在放回去
return username
# 全局钩子
def clean(self):
password = self.cleaned_data.get('password')
confirm_password = self.cleaned_data.get('confirm_password')
if not password == confirm_password:
self.add_error('confirm_password', '两次密码不一致')
# 将钩子函数去出来数据在放回去
return self.cleaned_data
三、forms组件其它参数及补充知识点
class MyForm(forms.Form):
...
# 单radio值为字符串
gender = forms.fields.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
# 单选select
hobby1 = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好1",
initial=3,
widget=forms.widgets.Select()
)
# 多选select
hobby2 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好2",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
# 单选checkbox
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
# 多选checkbox
hobby3 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好3",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
Field
required=True, 是否允许为空
widget=None, HTML插件
label=None, 用于生成Label标签或显示内容
initial=None, 初始值
help_text='', 帮助信息(在标签旁边显示)
error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'}
validators=[], 自定义验证规则
localize=False, 是否支持本地化
disabled=False, 是否可以编辑
label_suffix=None Label内容后缀
CharField(Field)
max_length=None, 最大长度
min_length=None, 最小长度
strip=True 是否移除用户输入空白
IntegerField(Field)
max_value=None, 最大值
min_value=None, 最小值
FloatField(IntegerField)
...
DecimalField(IntegerField)
max_value=None, 最大值
min_value=None, 最小值
max_digits=None, 总长度
decimal_places=None, 小数位长度
BaseTemporalField(Field)
input_formats=None 时间格式化
DateField(BaseTemporalField) 格式:2015-09-01
TimeField(BaseTemporalField) 格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
DurationField(Field) 时间间隔:%d %H:%M:%S.%f
...
RegexField(CharField)
regex, 自定制正则表达式
max_length=None, 最大长度
min_length=None, 最小长度
error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'}
EmailField(CharField)
...
FileField(Field)
allow_empty_file=False 是否允许空文件
ImageField(FileField)
...
注:需要PIL模块,pip3 install Pillow
以上两个字典使用时,需要注意两点:
- form表单中 enctype="multipart/form-data"
- view函数中 obj = MyForm(request.POST, request.FILES)
URLField(Field)
...
BooleanField(Field)
...
NullBooleanField(BooleanField)
...
ChoiceField(Field)
...
choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),)
required=True, 是否必填
widget=None, 插件,默认select插件
label=None, Label内容
initial=None, 初始值
help_text='', 帮助提示
ModelChoiceField(ChoiceField)
... django.forms.models.ModelChoiceField
queryset, # 查询数据库中的数据
empty_label="---------", # 默认空显示内容
to_field_name=None, # HTML中value的值对应的字段
limit_choices_to=None # ModelForm中对queryset二次筛选
ModelMultipleChoiceField(ModelChoiceField)
... django.forms.models.ModelMultipleChoiceField
TypedChoiceField(ChoiceField)
coerce = lambda val: val 对选中的值进行一次转换
empty_value= '' 空值的默认值
MultipleChoiceField(ChoiceField)
...
TypedMultipleChoiceField(MultipleChoiceField)
coerce = lambda val: val 对选中的每一个值进行一次转换
empty_value= '' 空值的默认值
ComboField(Field)
fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式
fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
MultiValueField(Field)
PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
SplitDateTimeField(MultiValueField)
input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
required=True,
widget=None,
label=None,
initial=None,
help_text=''
GenericIPAddressField
protocol='both', both,ipv4,ipv6支持的IP格式
unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
SlugField(CharField) 数字,字母,下划线,减号(连字符)
...
UUIDField(CharField) uuid类型
四、cookie与session
cookie:服务端保存在客户端浏览器上的信息都可以称之为cookie,它的表现形式一般都是k:v键值对(可以有多个)
session:数据的保存在服务端的并且它的表现形式一般也是k:v键值对(可以有多个)
token:
session虽然数据是保存在服务端的 但是禁不住数据量大
服务端不再保存数据,登录成功之后,将一段信息进行加密处理
将加密之后的结果拼接在信息后面,整体返回给浏览器保存
浏览器下次访问的时候带着信息,服务端自动切去前面的一段信息再次使用自己的加密算法跟浏览器尾部的密文进行比对
总结:
1.cookie就是保存在客户端浏览器上的信息
2.session就是保存在服务端上的信息
3.session是基于cookie工作的(其实大部分的保存用户状态的操作都是需要使用到cookie)
1.cookied操作
设置cookie:obj.set_cookie(key,value)
超时时间:obj.set_cookie(key,value,max_age/expires)
expires针对IE浏览器 数字是以秒为单位
加盐:obj.set_signed_cookie(key,value,salt='盐')
获取:request.COOKIES.get(key)
request.get_signed_cookie(key,salt='盐')
删除:obj.dalete_cookie(key)
校验用户是否登录才能访问视图函数的装饰器
能够记录用户在没有登录之前想要访问的页面,登录之后跳转到对应的页面
request.path
request.path_info
request.get_full_path()
# urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 登录功能
path('login/', views.login),
path('home/', views.home),
path('index/', views.index),
path('func/', views.func),
# 注销功能
path('logout/', views.logout),
# session操作
path('set_session/', views.set_session),
path('get_session/', views.get_session),
path('del_session/', views.del_session),
# CBV添加装饰器
path('mylogin', views.MyLogin.as_view()),
]
# views.py
from django.shortcuts import render, HttpResponse, redirect
# 校验用户是否登录的装饰器
def login_auth(func):
def inner(request, *args, **kwargs):
target_url = request.get_full_path()
if request.COOKIES.get('username'):
res = func(request, *args, **kwargs)
else:
return redirect('/login/?next=%s' % target_url)
return res
return inner
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'qiansuiyou' and password == '123':
# 获取用户上一次要访问的url
target_url = request.GET.get('next') # 这个结果可能是None
if target_url:
obj = redirect(target_url)
else:
# 保存用户登录状态
obj = redirect('/home/')
# 让浏览器记录cookie数据
obj.set_cookie('username', 'qiansuiyou', max_age=3)
# 超时时间3秒到期
# 跳转到一个需要用户登录之后才能看到的页面
return obj
return render(request, 'login.html')
@login_auth
def home(request):
# 获取cookie信息,判断有没有
# if request.COOKIES.get('username') == 'qiansuiyou':
# return HttpResponse('<h3>home页面</h3>')
# 没有登录应该跳转到登录页面
# return redirect('/login/')
return HttpResponse('<h3>home页面</h3>')
@login_auth
def index(request):
return HttpResponse('<h3>index页面</h3>')
@login_auth
def func(request):
return HttpResponse('<h3>func页面</h3>')
@login_auth
def logout(request):
obj = redirect('/login/')
obj.delete_cookie('username')
return obj
# login.html
<form action="" method="post">
<p>username:<input type="text" name="username"></p>
<p>password:<input type="text" name="password"></p>
<input type="submit">
</form>
2.session操作
session数据保存在服务端,给客户返回的是一个随机字符串 sessionid:随机字符串
1.在默认情况下操作session的时候需要django默认的一张django_session表
数据库迁移命令,django会自己创建很多表django_session就是其中的一张
django默认session的过期时间是14天,但是也可以人为的修改
django_session表中的数据条数是取决于浏览器的
同一个计算机上用一个浏览器只会有一条数据生效(当session过期的时候可能会出现多条数据对应一个浏览器)
主要是为了节省服务端数据库资源
设置session:request.session['key'] = value
获取session:request.session.get('key')
设置过期时间:request.session.set_expiry()
括号内可以放四种类型的参数
1.整数 多少秒
2.日期对象 到指定日期就失效
3.0 一旦当前浏览器窗口关闭立刻失效
4.不写 失效时间就取决于django内部全局session默认的失效时间
清除session:
request.session.delete() 只删服务端的,客户端的不删
request.session.flush() 浏览器和服务端都清空
# session是保存服务端的,但是session的保存位置可以有多种选择
1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()
4. 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
其他公用设置项:
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
# views.py
def set_session(request):
request.session['hobby'] = 'girl'
# request.session.set_expiry(3)
"""
1.django内部会帮你生成一个随机的字符串
2.django内部自动将随机字符串和对应的数据存储到django_session表中
2.1先在内存中产生操作数据的缓存
2.2在响应结果django中间件的时候才真正的操作数据库
3.将产生的随机字符串返回给客户端浏览器保存
"""
return HttpResponse('hello')
def get_session(request):
if request.session.get('hobby'):
print(request.session.get('hobby'))
"""
1.自动从浏览器请求中获取sessionid对应的随机字符串
2.拿着随机字符串去django_session表中查找对应的数据
3.如果比对上了,则将对应的数据取出并以字典的形式封装到request.session中
4.如果比对不上,则request.session.get()返回的是None
"""
return HttpResponse('hello')
return HttpResponse('过期了')
def del_session(request):
request.session.delete()
return HttpResponse('删除')
# session版登录验证
from functools import wraps
def check_login(func):
@wraps(func)
def inner(request, *args, **kwargs):
next_url = request.get_full_path()
if request.session.get("user"):
return func(request, *args, **kwargs)
else:
return redirect("/login/?next={}".format(next_url))
return inner
def login(request):
if request.method == "POST":
user = request.POST.get("user")
pwd = request.POST.get("pwd")
if user == "qiansuiyou" and pwd == "123":
# 设置session
request.session["user"] = user
# 获取跳到登陆页面之前的URL
next_url = request.GET.get("next")
# 如果有,就跳转回登陆之前的URL
if next_url:
return redirect(next_url)
# 否则默认跳转到index页面
else:
return redirect("/index/")
return render(request, "login.html")
@check_login
def logout(request):
# 删除所有当前请求相关的session
request.session.delete()
return redirect("/login/")
@check_login
def index(request):
current_user = request.session.get("user", None)
return render(request, "index.html", {"user": current_user})
五、CBV添加装饰器的三种方法
# views.py
from django.views import View
from django.utils.decorators import method_decorator
# @method_decorator(login_auth, name='get') # 方式2(可以添加多个针对不同方法加不同的装饰器)
# @method_decorator(login_auth, name='post')
class MyLogin(View):
@method_decorator(login_auth) # 方式3:它会直接作用于当前类里面的所有的方法
def dispatch(self, request, *args, **kwargs):
...
# @method_decorator(login_auth) # 方式1
def get(self, request):
return HttpResponse("get请求")
def post(self, request):
return HttpResponse("post请求")