• 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
  • 相关阅读:
    分布式配置 SSH 免密登陆
    转载--宏观认识大数据圈
    转载--存储是怎样炼成的
    转载--关于hdfs
    绕不开的hadoop
    sqoop 使用
    Excel VBA解读(54):排序——Sort方法
    MSSQL附加数据库时提示以下错误: 无法打开物理文件“***.mdf”。操作系统错误 5:“5(拒绝访问。)”。 (Microsoft SQL Server,错误: 5120)
    Delphi Code Editor 之 编辑器选项
    解决StrToDateTime()不是有效日期类型的问题
  • 原文地址:https://www.cnblogs.com/tianshuai1/p/11042166.html
Copyright © 2020-2023  润新知