Django之内置组件

Django组件介绍

        分页器的使用

        Form

        modelForm

        orm

        cookie和session

        中间件

        信号

 

1.分页器的使用

分页器在页面中非常常见,当数据库条数数据过多时,页面一次性显示不好看,我们就可以使用页面器,将数据分几次显示

一个简单的分页功能,可以导入用

  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})
分页

2.Form组件

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__')中可以取到
     
说明

3.ModelForm

作用:

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})
后端

4.orm

--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=?)
orm操作

5.cookie和session

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')

6.中间件

定义:介于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')

7.信号

定义:用于框架执行操作时解耦,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者

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
Test signals
connection_created          # 创建数据库连接时,自动触发
from django.db.backends.signals import connection_created
Database Wrappers

 

posted @ 2019-06-17 21:01  帅小伙⊙∪⊙  阅读(2317)  评论(0编辑  收藏  举报