模块六 django基础二


Forms组件

校验字段功能
否则需要自己判断用户输入ajax js可以进行校验获取对应数据和页面提示报错
<form action="" ></form>  不写 默认匹配当前页面的ip+端口/reg  http://127..0.0.1:8000/reg  当前页面请求地址 

from Django import forms
class UserForm(forms.Form):
name = forms.CharField(min_length=4,label=用户名)   # type=text
email = forms.EmailFileld(label="邮箱")  # type=email

form = UserForm({"name":"yuan","email":"123@qq.com","xxx":"alex"})
form.is_valid()  返回布尔值  

form = UserForm(request.POST)  form表单的name属性值应该与forms组件字段一致

if form.is_valid():
print(form.cleaned_data)  # {"name":"yuan","email":"123@qq.com"}
else:
print(form.cleaned_data)  # {"email":"123@qq.com"}
print(form.errors)    # {"name":[",,,,,,,,"]}  错误的 键:错误信息    Errordict
print(form.errors.get("name"))
print(type(form.errors.get("name")))   # Errorlist
print(form.errors.get("name")[0])  # 获取报错信息字符串

if所有字段校验成功则form.cleaned_data: {"name":"yuan","email":"123@qq.com"}   只要有一个校验失败就是FALSE

form设定的规则必须跟传递的值对应可以多不能少多的字段会忽略验证少了就会报错不同也会报错只有所有都校验成功才返回true否则返回false
  • 渲染标签功能
get请求的时候返回form页面
form  = UserForm()
render(request,"index.html",locals())  {"form":form}

<form action="" method="post">
{% csrf_token %}
<p>用户名 {{ form.name }}</p>
<p>密码 {{ form.pwd }}</p>
<p>确认密码 {{ form.r_pwd }}</p>
<p>邮箱 {{ form.email }}</p>
<input type="submit">

</form>

方式二
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div>
<label for="">{{ field.label }}</label>
{{ field }}
</div>
<input type="submit">

</form>

方式三

<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
// {{ form.as_d }}
<input type="submit">

</form>
  • forms 渲染错误信息
{{ form.name.errors.0 }}

<p>用户名 {{ form.name }}</p>

form.errors.get("name")[0]

2
1input保存输入的value值
2验证form 第一个是没有数据的formPOST 是i传递request.POST 数据验证的form
  • 参数配置
class UserForm(forms.Form):
name = forms.CharField(min_length=4,label=用户名,error_message={"required":"改字段不能为空"},widget=widgets.TextInput(attrs={"class":"form-control"}))   # type=text
pwd = forms.CharField(min_length=4,label=密码,widget=widgets.TextInput()/PasswordInput(attrs={"class":"form-control"})) 
email = forms.EmailFileld(label="邮箱",error_message={"required":"改字段不能为空","invalid","格式错误"},widget=widgets.TextInput(attrs={"class":"form-control"}))  # type=email

局部钩子 用于判断单个字段是否满足验证后的需求双重验证
def clean_name(self):
val = self.cleaned_data.get("name")
ret = UserInfo.objects.filter(name=val)

if not ret:
return val
else:
raise ValidationError("该用户已注册!")


全局钩子   用于判断2次密码是否输入相同   多个变量判断
源码
def clean(self):
pass

https://www.cnblogs.com/yuanchenqi/articles/9036474.html

  • 临时想到:之前一直容易的混的地方
前后端双重校验 -- 用户输入  
前端校验  js实现  定义规则 ,直接返回,不通过后端实现  ( JavaScript 表单验证 function )   伪校验     减少请求,提升用户体验
======
后端校验  form实现 
2种实现方式  
基于form 表单 Form 实现校验  刷新 同步   1、保留 input value值  2、校验POST值     
基于ajax 触发 + jQuery事件 button提交  Form 实现校验  不刷新  异步 

后端不通过form,只能手动去判断每个值是否符合要求,多重判断和错误输出,最后结果就是类似form的类来实现 校验
前端通过js + 事件 直接返回,不通过后端实现

js数组  == 列表
object  . 操作 == 字典

HTTP协议的无状态保存

  • 无状态特点:每次请求都是一个新连接,用户登录信息没有,没有状态
要解决登录用户再次访问无状态请求能够保存用户登录状态
cookie 具体一个浏览器针对一个服务器存值 key-value({})

建立请求之间的联系 {islogin:true}

空cookie {}

1更换服务端会写到空的cookie
2除了本地cookie过期每次都会携带去请求得到上次的状态
3更换浏览器也会写空的cookie

响应体
HTTPResponse  有set_cookie方法  下面都继承HTTPResponse 都有set_cookie方法
render()   调用 HTTPResponse
redirect()  调用 HTTPResponse

设置cookie返回可以看到响应体中携带cookie信息
response = HttpResponse("登录成功")
response.set_cookie(is_login,True,max_age=15,)  15秒过期 IE不支持   
import datetime
date=datatime.datetime(year=2018,month=5.day=29,hour=14,minute=3)  设定固定过期时间
有效路径  /index/  下面可以获取到cookie  其他路径下获取不到        后面讲 域名  domain=None,
response.set_cookie(username,user.user,expires=date,path="/index/")
return response

def index(request):
print(request.COOKIES)
is_login = request.COOKIES.get("is_login")

if is_login:
username = request.COOKIES.get("username")
import datetime
now = datatime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
last_time = request.COOKIES.get("last_visit_time","")
response = render(request,"index.html",{"username":username,"last_time":last_time})
response.set_cookie("last_visit_time",now)
return response
else:
return redirect("/login/")

csrftoken 中间件自动添加

  • 请求头中会带着cookie进行请求 ,每次浏览器请求都会在header中携带cookie请求
request.COOKIES

cookie维持会话记录的保存

删除cookie 
response.delete_cookie("cookie_key",path="/",domain=name)
浏览器直接删除cookie 清除过去1小时记录

已浏览的商品  

session  基于cookie开发 存在服务端 
 
request.session["username"] = "yuan"

request.session["is_login"] = True
request.session["username"] = user.user
1生成随机字符串
2设置set_cookie.sessionid=随机字符串
3session表中生成 session-key 对应随机字符串  session-data  {"username":"yuan"}

06

request.seesion["is_login"]  会执行下面3步
1request.COOKIE.get("sessionid")  # 随机字符串
2Django-session表中过滤记录
session-key  session-data
随机字符串     {"username":"yuan","is_login":True}
obj=django-session.objects.filter(session-key=随机字符串).first()
3obj.session-data.get("is_login")

is_login = request.session.get("is_login")
if not is_login:
pass

上次登录时间 
写在login中 request.session["visit_time"] = now
写在index中就是获取上次访问index的时间

session更新操作

if request.COOKIE.get("session"):
更新session记录

else:
生成一条session记录
session注销功能与配置参数
删除键值
del request.session["is_login"]
注销
request.session.flush()
执行三步
1ranon_str = request.COOKIE.get("sessionid")
2django-session.objects.filter(seesion-key=ranon_str).delete()
3response.delete_cookie("sessionid",)

设置cookie
1response.set_cookie(key,value)
2request.COOKIE.get(key)
3request.session[key] = value
1生成随机字符串
2设置set_cookie.sessionid=随机字符串
3session表中生成 session-key 对应随机字符串  session-data  {"username":"yuan"}

4request.session[key]
1request.COOKIE.get("sessionid")  # 随机字符串
2Django-session表中过滤记录
session-key  session-data
随机字符串     {"username":"yuan","is_login":True}
obj=django-session.objects.filter(session-key=随机字符串).first()
3obj.session-data.get("is_login")

5request.session.flush()
1ranon_str = request.COOKIE.get("sessionid")
2django-session.objects.filter(seesion-key=ranon_str).delete()
3response.delete_cookie("sessionid",ranon_str)

https://www.cnblogs.com/yuanchenqi/articles/9036467.html

django用户认证组件 只做登录验证操作 session操作 保存会话记录 逻辑完善 解决多次登录更新session-data产生混淆数据,会在每次登录时候清除对应session-data记录防止更新混淆 简单来说,alex登录只保留alex的键值对,egon只保留egon的键值对,相同浏览器登录携带相同随机字符串默认是更新session-data键值对, 通过认证组件,会自动清理之前session信息(django-session表中对应记录被删除),生成新登录用户的session信息

  • 验证码、保留上次登录时间都做不了

  • 用户认证组件

前提用户表  Django自带的auth_user  扩展OneToOne
功能用session记录登录验证状态
创建超级用户 Python3 manage.py createsuperuser

API
from django.crontrib import auth 
#if 验证成功返回user对象,否则返回None
user = auth.authenticate(username=user,password=pwd)
auth.login(request,user) #  request.user: 当前登录对象
auth.logout(request)

from django.crontrib.auth.models import User    # User==auth_user表
if not request.user.is_authenticated()
user=User.objects.create_user()

补充
匿名用户对象
匿名用户
class models.AnonymouseUser

django.contrib.auth.models.AnonymouseUser 类实现了django.contrib.auth.models.User 接口但具有下面几个不同点

    id 永远为None
    username 永远为空字符串
    get_username() 永远返回空字符串
    is_staff  is_superuser 永远为False
    is_active 永远为 False
    groups  user_permissions 永远为空
    is_anonymous() 返回True 而不是False
    is_authenticated() 返回False 而不是True
    set_password()check_password()save() 和delete() 引发 NotImplementedError
    New in Django 1.8:
    新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User
总结
request.user
if not: auth.login(request,user)  request.user == AnonymouseUser()
else: request.user == 登录对象
request.user 是一个全局变量
在任何视图和模板直接使用

User接口
from django.crontrib.auth.models import User
from django.crontrib import auth 

user = request.POST.get("user")
pwd = request.POST.get("pwd")
#if 验证成功返回user对象,否则返回None
user = auth.authenticate(username=user,password=pwd)
if user:  # 没有登录返回匿名对象,只能打印username 
auth.login(request,user) #  request.user: 当前登录对象   没有登录成功返回None    # auth函数 + 中间件authware实现
可以直接使用包括在模板语言中不需要传直接用
request.user.username
request.user.id
{{ request.user.username }}

同一浏览器测试不同用户登录可以看到session表中会删除对应记录重新生成新的对应用户session记录不会再原有session-data字段上面去更新键值对解决了上面session-data键值对混淆的问题更加严谨

注销功能
基于ajax + button 访问其他url
基于a标签访问其他url 
auth.logout(request)

User对象

匿名对象 返回false

认证装饰器
@login_required

settings.py配置
LOGIN_URL = "/login/"

访问/index跳转到/login登录成功next自动跳转到index页面 /order会自动跳转到order页面    有next就跳转到next页面没有就跳转index页面
next_url = request.GET.get("next","/index/")
return redirect(next_url)

https://www.cnblogs.com/yuanchenqi/articles/9064397.html

中间件

  • django请求生命周期

07

  • 应用场景
统一登录认证
统计访问频率  不管访问那个url,请求20次就关闭 限流

https://www.cnblogs.com/yuanchenqi/articles/9036479.html

默认request return None 
def process_request(self,request):
return None  

jQuery 事件 灵活实现

项目流程

1搞清楚需求(产品经理)
1基于用户认证组件和ajax实现登录验证(图片验证码)
2基于forms组件和ajax实现注册功能  
3设计系统首页(文章列表渲染)
4设计个人站点页面 
5文章详情页
6实现文章点赞功能
7实现文章的评论
---- 文件的评论
---- 评论的评论
8富文本编辑框和防止xss攻击(评论js脚本提交执行)

2设计表结构  所有功能model

根评论 对文章的评论
子评论 对评论的评论

自关联 自己关联自己  self 
foreignkey 只是为了建立约束关系  外键约束     不用IntegerField
comment 外键会自动加id
null = True

08

3按着每一个功能进行开发
4功能测试
5项目部署上线
静态文件statics  URL="/static" 就是访问静态文件链接地址  
设置static目录  os.path.join(BASEDIR,"static")  能够访问到项目的静态文件
window.onload  要等dom加载完毕才能操作  js引入写在head中需要加入onload  

所有放在 head 标签里的 CSS  JS 文件都会堵塞渲染如果这些 CSS  JS 需要加载和解析很久的话那么页面就会一直显示空白所以 JS 文件要放在底部 HTML 解析完了再加载 JS 文件
CSS放在头部是因为如果CSS放在尾部会让用户第一时间看到没有样式的页面比较的难看’,为了避免这种情况发生将CSS放在头部

入口函数 
window.onload = function(){
alert(1);
}

jQuery
$(document).ready(function(){
alert(1);
})

简写
$(function() {
alert(2);
})
核心区别
1jQuery执行时机 DOM结构加载完执行  onload等全部资源加载完执行包括图片 (时间很长)
2jQuery可执行多次 不会覆盖顺序执行 onload只执行一次 后面会覆盖前面
3jQuery可简写
  • 验证码图片
1验证码生成
2局部刷新 采用jQuery实现图片切换开始想到jQuery绑定事件触发ajax执行访问切换图片  ==> 直接jQuery src即可实现     
加载页面执行 动画效果 需要入口函数
只是事件触发执行直接绑定事件即可不需要在入口函数中触发执行触发点击执行

验证码刷新
$("#valid_code_img").click(function(){
$(this)[0].src +="?"
})

利用session保存验证码   cookie也可以session更好保存在服务端
总结
1一次请求会伴随多次请求
2PIL
3session存储
4验证码刷新

ajax jQuery auth session 实现

传递json数据直接使用JsonResponse(response) 实现json解析 

优化
封装到模块中from导入模块调用对应函数

search 属性是一个可读可写的字符串可设置或返回当前 URL 的查询部分问号 ? 之后的部分
意思就是获取get url链接中的请求地址如果有就直接跳转到指定页面没有就跳转到/index 页面   就跟之前auth组件里面会跳转next一样例如
next_url = request.GET.get("next","/index/")  # 通过URL get请求获取数据 url访问的链接?=order 获取到next页面这里get是浏览器发起的ajax也可以发起GET通过上面的方式获取到get数据   
当前get中有后缀就跳转都对应页面没有就跳转到index页面
if(location.search){
location.href = location.search.slice(6)
}else{
location.href = "/index/"
}
  • 滑动验证码 极验
基于forms组件和ajax实现注册功能
STATIC_URL='/static/'
STATICFILES_DIRS=[os.path.join(BASE_DIR,"static")]

1 基于forms组件设计祖册页面
点击头像 == 点击input

头像预览
1 获取用户选中文件对象
2 获取文件对象的路径
3 修改img的src属性  src=文件对象的路径

method.POST == 'post'

2 错误信息
views:
form.errors  # {"user":[.......]}

3 局部钩子和全局钩子校验
user字段不能重复
两次密码不一致

4 FileField  ImageFiled 

django实现
会将文件对象下载到项目的根目录中avatars文件夹中(如果没有avatar文件夹Django会加自动创建)user_obj的avatar存的是文件的相对路径

5 media配置 

django有两种静态文件
/static/ : js,css,img
/media/ : 用户上传文件

setttings.py
MEDIA_ROOT=os.path.join(BASE_DIR,"media")

MEDIA_URL = "/media/"
提供浏览器访问

只是记住对应url和settings配置即可
  settings.py:
      MEDIA_URL="/media/"

   urls.pt:
      # media配置:
      re_path(r"media/(?P<path>.*)$",serve,{"document_root":settings.MEDIA_ROOT})

联想到的
文件上传with open(xx,'wb') as f,  data = f.read()   data发送过去
application/json  ajax传递 json到服务端  
$(".btn").click(function(){
$.ajax({
url: "", // 默认当前ip:端口
type:"post", 
contentType: "application/json",
data:JSON.stringify({
a:1,
b:2
}),
body中a=1&b=2  == {'a':1,'b':2}
首页 - 导航区域

登录注册实现
django admin (不是必须的)
django内部一个组件:后台数据管理组件(web页面)
python manage.py createsuperuser 针对用户认证组件对应的用户表

个人站点页面的文章查询 
url做分发,多个APP需要做分发





ajax是发送后端请求,只是js实现而已,最终还是跟后端交互
表单验证是前端还是后端验证?
目前form(刷页面) 和 ajax (不刷页面)都是基于后端验证   前端没有验证 
模板语言实现input data保存  和 报错返回不同样式,可以取代纯前端验证

结合jQuery事件 + ajax 触发,不需要等待用户提交,移开鼠标就验证当前input内容并返回错误信息,从而实现了快速验证+友好体验
取代纯前端js表单验证,如果觉得请求过多,可以只在提交的时候显示,这个根据业务场景来实现,并不是技术问题
js验证可以减少后端请求次数,仅仅而已,体验不同,目前无需关注

前端验证是为了提供更好的用户体验;
后端验证是为了保证数据满足业务条件(business invariants);

后端验证必须存在
前端是为了更好的用户体验

从必要的角度来说,前端可以不做验证,但是后端的验证是必须做的。
为什么呢?
前端不做验证,但是在表单提交的时候,后端验证的结果会返回前端,前端需要解析结果,显示对应的内容。对于验证不通过的情况,需要展示相应的不通过的原因。
后端验证一定要有,是因为后端才是接收数据,并对数据进行操作和存储的。只要保证了接收到的数据有足够的验证,就能保证最终存储的数据是满足了业务验证条件的,不是“脏数据”。
如果仅有前端验证,没有后端验证。在前后端分离的情况下,用户可以绕过前端,直接访问后端接口。那么前端验证也就失效了。也就等于没有对数据的业务验证了。
同时,如果仅有前端验证,部分验证功能不好满足。比如判断数据库是否存在相同的用户名,此时需要查询数据库才能判断。如果前端不同后端交互,那么是不清楚用户名是否重复的。
K: 后端有,前端可无

就拿设定密码这事举例,比如网站要求密码必须包含数字、字母和特殊字符,如果只做后端验证,用户就很容易有挫败感,很多用户都没心情仔细看网页上的说明的,写上最喜欢的密码123456或者自己的生日数字就发送了,然后后端验证不通过,bang!失败,用户于是只好重新来,这种体验......虽然安全,但是并不是最好。
如果加上前段验证,用户只输入123456的时候,就可以在密码框下面动态显示密码不够安全,还缺啥啥啥,这样用户容易知道自己该做的啥,再点击发送按钮前就让密码符合规则的概率大,而且体验也好很多。

url、model、templates -- 模板语言 + 嵌套继承、view 逻辑
请求生命周期  中间件
ajax与form表单提交方式思考,前后端验证  前端只是减少请求 + 提高用户体验,后端保证数据输入,具体看业务需求,不需要体验后端验证就够了,不在乎请求量,后端也可以做每个input框的验证,
ajax绑定button点击提交跟ajax绑定鼠标选中事件发送请求是一个流程,只是不同事件触发而已,也可以做到页面每个input提示错误,而且是跟后端交互实现的错误提示
ajax与form都是后端验证,只是刷不刷新页面的问题,form会刷新,ajax不会刷新,体验更好
纯前端验证 比如 在input输入框后面加提示,打钩,显示输入的格式什么的等等,更好的用户体验,减少对后端的请求量,不然每个input框都去做一次ajax请求,比较耗资源
具体看应用场景,业务需求