Django之内置组件
Django组件介绍
分页器在页面中非常常见,当数据库条数数据过多时,页面一次性显示不好看,我们就可以使用页面器,将数据分几次显示
一个简单的分页功能,可以导入用
page_num = request.GET.get('page','1') try: page_num = int(page_num) if page_num <=0: page_num = 1 except Exception as e: page_num =1 #总数量 all_count = len(userlist) #每页显示为10 页 per_num = 10 #总页码数 total_page_num, more =divmod(all_count,per_num) if more: total_page_num += 1 #最多显示的页面数 max_show = 11 half_show = max_show//2 if total_page_num < max_show: page_start = 1 page_end = total_page_num elif page_num <= half_show: page_start =1 page_end =max_show elif page_num + half_show > total_page_num: page_start = total_page_num-max_show +1 page_end = total_page_num else: #起始页面 page_start = page_num - half_show #终止页面 page_end = page_num + half_show start =(page_num-1) * per_num end =page_num*per_num page_list = [] if page_num == 1: page_list.append('<li class="disabled"><a>上一页</a></li>') else: page_list.append('<li ><a href="?page={}">上一页</a></li>'.format(page_num - 1)) for i in range(page_start, page_end + 1): if i == page_num: page_list.append('<li class="active" ><a href="?page={}">{}</a></li>'.format(i, i)) else: page_list.append('<li><a href="?page={}">{}</a></li>'.format(i, i)) if page_num == total_page_num: page_list.append('<li class="disabled"><a>下一页</a></li>') else: page_list.append('<li><a href="?page={}">下一页</a></li>'.format(page_num + 1)) page_html = ''.join(page_list) return render(request, 'user_list.html', {'users': userlist[start:end], 'page_html': page_html})
django框架提供了一个form类,来处理web开发中的表单相关事项.form最常做的是对用户输入的内容进行验证,为此django的forms类提供了全面的内容验证和保留用户上次输入数据的支持
form组件的两大功能:
---对用户提交的内容进行验证(from表单/ajax)
---表留用户上次输入的内容
form组件的几大用处:
1.校验字段功能
1.首先先到导入forms这个模块 from django import forms 2.自己写一个类,并继承forms.Form class Myform(forms.Form): #这行代码的意思,name这字段最长为8,最短为3 name=forms.charField(max_length=8) def index(request): dic={'name':'zh'} #这里就是类的实例化,传的参数必须为一个字典 myform =Myform(dic) #这是对象的绑定方式,它的返回值就是一个布尔值 # True表示你传的dic这个字典满足form里的条件,False就是不满足 # 我们可以通过判断它,再进行逻辑操作,比如该字段符合你的要求,再怎么操作 if myform.is_valid(): return HttpResponse('校验成功') # 走到这一步,代表当中有字段不符合要求 # 它的返回值是一个对象,但是它继承了字典,所以你可以通过get取到错误提示 # 对了,你传的字典的key值必须要和创建的类(Myform)要对应,并且只能多,不能少 # name_error = myform.errors.get('name') # 这样你可以取到name字段出错的原因了 name_error=myform.errors return HttpResponse('校验失败') ## 总结下:1、Myform的实例化必须传字典 2、is_valid()返回值是布尔类型 3、errors 调用这个方法,返回值是对象,你可以通过get取值
2.渲染标签功能
form组件可以在视图函数中使用,也可以在模板中使用
渲染方式一: <form action='' method='post'> 用户名:{{myform:name}} <br> <input type='submit' value = '提交'></input> </form> # 这里的{{myform:name}} 和你写input框是一样的效果,就是属性比input框多一点 渲染方式二(推荐使用): <form action='' method='post'> {% for foo in myform%} {{ foo.lable }} : {{ foo }} <br> <input type='submit' value = '提交'></input> </form> # 页面显示都是一样的,foo.lable不是用户名,是name,但是你可以在创建Myform类时 # 在CharFiel中添加lable='用户名',这样就行了。 渲染方式三: <form action='' method='post'> {{ myform.as_p }} <input type='submit' value = '提交'></input> </form> # 对,方式三就是这么简单,但是拓展性太差了,对不对,所以不推荐使用它
3.渲染错误信息功能
渲染错误信息,之前不是写了error这个方法嘛,他就是装着错误信息的对象, 其实就是让它渲染到页面上,这不就是很简单嘛 拿渲染方式二来举例子吧: <form action='' method='post'> {% for foo in myform%} {{ foo.lable }} : {{ foo }} <span>{{foo.errors.0}}</span><br> <input type='submit' value = '提交'></input> </form> # 来讲下为什么不是用get去取错误信息,首先这个foo是什么?它就是你创建的字段 # 所以直接通过索引取值就好了,那么就应该知道foo.errors貌似就是一个列表对吧 # 模板渲染时我在后台渲染好了,再返回到前台的,那我可以不可以将错误信息传到前台 # 让前台执行DOM操作进行渲染呢? # 我觉得太麻烦,况且前端我。。。(你懂的)
4.组件的参数配置
其实在些Myform,下面的字段还有很多参数,我就写写大概有什么用 max_length # 代表该字段最长为多少 min_length # 代表该字段最短为多少 error_messages # 这是设置错误信息的属性 # 例子 error_messages= {'max_length': '最长八位', 'min_length': '最短三位', 'required': '不能为空'} required # 默认值为True,意思是你传来的字段必须有它,没有的话校验失败 widget=widgets.TextInput() # 你在模板渲染的时候,就会渲染成Input框,type为text 还有其他类型的input框,自己在看看吧 对了,在TextInput(),你可以为input添加属性,attrs={'class':'abc'} 写在括号里面 lable #这个是不是上面讲到了,lable='用户名'
5.钩子
局部钩子 局部钩子说白了就是写一个函数,但是这个函数名必须为clean_name,这个name是可以改变的, 你定义的类里,你想对哪个字段写钩子函数,这个name就为那个字段的名字,比如我想为password这个 字段写钩子函数,那函数名就为clean_password,就这样。 那这个局部钩子有什么用了? 首先你的程序能走到局部钩子这一步,就说明你传的字典中的字段符合要求,这要记清楚,那么我们在 取值就从clean_data中取就好了,clean_data里装的是符合要求的数据,是一个字典。 我们可以从clean_data中取到相应的值再做一次逻辑处理,比如我写clean_name这个局部钩子, 我可以拿到name,对这个name进行一些操作,名字开头不能是数字,名字中不能有有什么字符,这 些等等,看你自己的需求,逻辑代码写好了,最后return name 就好了 全局钩子 全局钩子其实作用差不多的,每个字段你可以进行局部钩子进行逻辑书写,这些处理完成之后,有需要的话, 你再进行全局处理,举个例子就大概能明白,你在写注册用户的时候,是不是有密码,确认密码,你可以进行 布局钩子处理,处理完毕是不是在进行判断,判断他们是否相等,相等的话,就存到数据库中,不相等就抛个 异常,对了对了,上面局部钩子忘记写异常,下面讲讲。 -----1、局部钩子,全局钩子所抛出异常的类型为ValidationError,它是在下面这行代码导入 from django.core.exceptions import ValidationError 2、局部钩子抛出的异常会添加到该字段中的错误信息中,也就是myform.errors.get(字段名)中 3、而全局钩子抛出的异常会添加到__all__中,myform.errors.get('__all__')中可以取到
作用:
1.手动对单表进行增,删,改,查,手动把orm操作获取的数据渲染到模块;(阶段1)
2.Form组件(类),自动生成标签(input,select),并对用户输入的数据做规则验证;(阶段2)
3.ModelForm顾名思义就Form和Django的Model数据库模型结合体,可以简单,方便地对数据库进行增加,编辑操作和验证标签的生成
使用ModelForm
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ form_obj.as_p }} {#<p>姓名:{{form_obj.name }}</p>#} </body> </html>
class BSForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for filed in self.fields.values(): if not isinstance(filed, forms.BooleanField): filed.widget.attrs.update({'class': "form-control"}) class RegForm(BSForm): re_pwd = forms.CharField(widget=forms.PasswordInput, label='确认密码') class Meta: model = models.UserProfile fields='__all__' #获取全部的 exclude=['memo','is_active'] #删除不想要的 labels = { 'username': '用户名' #标签 } widgets = { 'password': forms.PasswordInput(attrs={'class': "form-control", 'k1': 'v1'}), } error_messages = { 'password': { 'required': '必填的' } } def clean(self): pwd = self.cleaned_data.get('password', '') re_pwd = self.cleaned_data.get('re_pwd', '') if pwd == re_pwd: return self.cleaned_data self.add_error('re_pwd', '两次密码不一致') raise ValidationError('两次密码不一直') def reg(request): form_obj=RegForm() if request.method =="POST": form_obj=RegForm(request.POST) if form_obj.is_valid(): # print(form_obj.cleaned_data) # form_obj.cleaned_data.pop('re_pwd') # models.UserProfile.objects.create(**form_obj.cleaned_data) form_obj.save() return redirect(reverse('login')) return render(request,'reg.html',{'form_obj':form_obj})
--MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库
--ORM是'对象-关系-映射'的简称 ,主要任务是:
*根据对象的类型生成表结构
*将对象,列表的操作,转换为sql语句
*将sql查询到的结果转换为对象,列表
--这极大地减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动
--Django中的模型包含存储数据的字段和约束,对应着数据库中唯一的表
1.在models.py中定义模型类,要求继承自models.Model 2.把应用加入settings.py文件的installed_app项 3.生成迁移文件 4.执行迁移生成表 5.使用模型类进行crud操作
在模型中定义属性,会生成表中的字段
django根据属性的类型确定以下信息:
当前选择的数据库支持字段的类型
渲染管理表单时使用的默认html控件
在管理站点最低限度的验证
django会为表增加自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后,则django不会再生成默认的主键列
属性命名限制
不能是python的保留关键字
由于django的查询方式,不允许使用连续的下划线
定义属性时,需要字段类型 字段类型被定义在django.db.models.fields目录下,为了方便使用,被导入到django.db.models中 使用方式 导入from django.db import models 通过models.Field创建字段类型的对象,赋值给属性 对于重要数据都做逻辑删除,不做物理删除,实现方法是定义isDelete属性,类型为BooleanField,默认值为False 字段类型 AutoField:一个根据实际ID自动增长的IntegerField,通常不指定 如果不指定,一个主键字段将自动添加到模型中 BooleanField:true/false 字段,此字段的默认表单控制是CheckboxInput NullBooleanField:支持null、true、false三种值 CharField(max_length=字符长度):字符串,默认的表单样式是 TextInput TextField:大文本字段,一般超过4000使用,默认的表单控件是Textarea IntegerField:整数 DecimalField(max_digits=None, decimal_places=None):使用python的Decimal实例表示的十进制浮点数 DecimalField.max_digits:位数总数 DecimalField.decimal_places:小数点后的数字位数 FloatField:用Python的float实例来表示的浮点数 DateField[auto_now=False, auto_now_add=False]):使用Python的datetime.date实例表示的日期 参数DateField.auto_now:每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false 参数DateField.auto_now_add:当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false 该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键 auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果 TimeField:使用Python的datetime.time实例表示的时间,参数同DateField DateTimeField:使用Python的datetime.datetime实例表示的日期和时间,参数同DateField FileField:一个上传文件的字段 ImageField:继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image 字段选项 通过字段选项,可以实现对字段的约束 在字段对象时通过关键字参数指定 null:如果为True,Django 将空值以NULL 存储到数据库中,默认值是 False blank:如果为True,则该字段允许为空白,默认值是 False 对比:null是数据库范畴的概念,blank是表单验证证范畴的 db_column:字段的名称,如果未指定,则使用属性的名称 db_index:若值为 True, 则在表中会为此字段创建索引 default:默认值 primary_key:若为 True, 则该字段会成为模型的主键字段 unique:如果为 True, 这个字段在表中必须有唯一值 关系 关系的类型包括 ForeignKey:一对多,将字段定义在多的端中 ManyToManyField:多对多,将字段定义在两端中 OneToOneField:一对一,将字段定义在任意一端中 可以维护递归的关联关系,使用'self'指定,详见“自关联” 用一访问多:对象.模型类小写_set
增: models.UserInfo.object.create(name=new_name) 删: models.UserInfo.object.get(id=xxx,None) models.delete() 改: obj = models.UserInfo.object.get(id=xx,None) obj = new_xxx obj.save() #相当于修改后提交数据 查 querylist=models.Entry.objects.all() print([e.title for e in querylist]) print([e.title for e in querylist]) entry = models.Entry.objects.get(id=?)
cookies是浏览器为web服务器存储的一个信息,每次浏览器从某个服务器请求页面时,都会自动带上以前收到的cookie.cookie保存在客户端,安全性较差,注意不要保存没敢信息.
--网络登录
--购物车
def login(request): if request.method == 'GET': return render(request,'login2.html') if request.method == 'POST': u = request.POST.get('username') p = request.POST.get('pwd') dic = user_info.get(u) if not dic: return render(request,'login2.html') current_date = datetime.datetime.utcnow() current_date = current_date + datetime.timedelta(seconds=10) if dic['pwd'] == p: res = redirect('/myapp/index') # res.set_cookie('username',u,max_age=10) #对cookie设置了超时时间和安全设置 res.set_cookie('username',u,expires=current_date,httponly=True) # res.set_signed_cookie('username',u,salt="121221") return res else: return render(request,'login2.html')
详情页面,如果cookie 验证通过则进入index页面,否则刷新进入登录页面
def auth(func): def inner(request,*args,**kwargs): v = request.COOKIES.get('username') if not v: return redirect('/myapp/login') return func(request,*args,**kwargs) return inner @auth def index(request): v = request.COOKIES.get('username') return render(request,'index2.html',{'current_user':v})
session就是保存在后台数据或者缓存中的一个键值对,同样的存储着用户信息,为更好的保护用户隐私,其实是对前端cookie的一个升级的保护措施
当登录成功后,会向后台数据库 与 前端 Cookie同时发放一段随机字符串,分别保存在后台的session中,前端 写到用户浏览器中,用户下次登录时候 拿着浏览器存着的sessionID当做KEY去后台数据库中匹配进行验证登录即可拿到用户相关信息,可以防止敏感信息直接暴露在浏览器上 作者:TianTianBaby223 链接:https://www.jianshu.com/p/a2d696364501 来源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
Django下用session实现登录验证
def sessionLogin(request): if request.method == "GET": return render(request,'sessionLogin.html') elif request.method == "POST": user = request.POST.get('user') pwd = request.POST.get('pwd') if user == 'root' and pwd =="123": #生成随机字符串 #写到用户浏览器 #保存到session中 #在随机字符串对应的字典中设置相关内容... request.session['username'] = user request.session['is_login'] = True if request.POST.get('rmb',None) == '1': request.session.set_expiry(10) return redirect('/myapp/sessionindex') else: return render(request, 'sessionLogin.html')
详情页逻辑
def sessionindex(request): #获取当前用户的随机字符串 #根据随机字符串获取对应信息 if request.session.get('is_login',None): return render(request,'sessionindex.html',{'username':request.session['username']}) else: return HttpResponse('get out')
定义:介于request(请求)与response(响应)处理之间的一道处理过程,相对比较轻量级,位于web服务端与url路由层之间
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', #一些安全设置,比如xss脚本过滤
'django.contrib.sessions.middleware.SessionMiddleware',#session支持中间件,加入这个中间件,会在数据库中生成一个django_session的表。
'django.middleware.common.CommonMiddleware', #通用中间件,会处理一些url
'django.middleware.csrf.CsrfViewMiddleware', #跨域请求伪造中间件,加入这个中间件,在提交表单的时候会必须加入csrf_token,cookie中也会生成一个名叫csrftoken的值,也会在header中加入一个HTTP_X_CSRFTOKEN的值来放置CSRF攻击。
'django.contrib.auth.middleware.AuthenticationMiddleware', #用户授权中间件。他会在每个HttpRequest对象到达view之前添加当前登录用户的user属性,也就是你可以在view中通过request访问user。
'django.contrib.messages.middleware.MessageMiddleware',#消息中间件。展示一些后台信息给前端页面。如果需要用到消息,还需要在INSTALLED_APPS中添加django.contrib.message才能有效。如果不需要,可以把这两个都删除。
'django.middleware.clickjacking.XFrameOptionsMiddleware',#防止通过浏览器页面跨Frame出现clickjacking(欺骗点击)攻击出现。
'A_orm.middlewares.auth.AuthenticationMiddleware',
]
请求进来是自上而下,通过反射找到类,用for循环来执行,可以自定义中间件,但也要写入MIDDLEWAR中
1、process_request(self,request) #请求完执行 2、process_view(self, request, callback, callback_args, callback_kwargs) #如果有返回值,跳转到最后一个中间件,执行最后一个中间件的response方法,逐步返回 3、process_template_response(self,request,response) #默认不执行,只有在视图函数的返回对象中有render方法才会执行 4、process_exception(self, request, exception) #默认啥也不执行,在视图函数出现错误是才执行,返回错误信息 5、process_response(self, request, response) #响应执行
1、做IP限制 放在 中间件类的列表中,阻止某些IP访问了; 2、URL访问过滤 如果用户访问的是login视图(放过) 如果访问其他视图(需要检测是不是有session已经有了放行,没有返回login),这样就省得在 多个视图函数上写装饰器了! 3、缓存(还记得CDN吗?) 客户端请求来了,中间件去缓存看看有没有数据,有直接返回给用户,没有再去逻辑层 执行视图函数
执行顺序:
1.我们要在app01文件下创建一个文件(middlewares)文件,在下面创建一个.py文件写入自定义中间件
2.在setting里面添加中间件,文件路径+功能
3.在views.py你们调用即可.
def index(request): print('1')
定义:用于框架执行操作时解耦,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者
Model signals pre_init # django的model执行其构造方法前,自动触发 post_init # django的model执行其构造方法后,自动触发 pre_save # django的model对象保存前,自动触发 post_save # django的model对象保存后,自动触发 pre_delete # django的model对象删除前,自动触发 post_delete # django的model对象删除后,自动触发 m2m_changed # django的model中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 from django.db.models.signals import pre_init, post_init from django.db.models.signals import pre_save, post_save
Management signals pre_migrate # 执行migrate命令前,自动触发 post_migrate # 执行migrate命令后,自动触发
Request/response signals request_started # 请求到来前,自动触发 request_finished # 请求结束后,自动触发 got_request_exception # 请求异常后,自动触发 from django.core.signals import request_finished from django.core.signals import request_started
setting_changed # 使用test测试修改配置文件时,自动触发 template_rendered # 使用test测试渲染模板时,自动触发 from django.test.signals import setting_changed from django.test.signals import template_rendered
connection_created # 创建数据库连接时,自动触发 from django.db.backends.signals import connection_created