• Django数据操作F和Q、model多对多操作、Django中间件、信号、读数据库里的数据实现分页


    models.tb.objects.all().using('default'),根据using来指定在哪个库里查询,default是settings中配置的数据库的连接名称。

    外话:django中引入现成数据库

    Django引入外部数据库还是比较方便的,步骤如下
    创建一个项目,修改seting文件,在setting里面设置你要连接的数据库类型和连接名称,地址之类,和创建新项目的时候一致
    运行下面代码可以自动生成models模型文件
    python manage.py inspectdb,执行完这条命令后,会出现一堆python代码,将这些代码放到models.py里,然后就可以操作这些类(表)了。
    
    把模型文件导入到app中
    创建一个app
    django-admin.py startapp app
    python manage.py inspectdb > app/models.py
    ok模型文件已经生成好了。下面的工作就和之前一样了

     引入模板变量的补充:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>{{ val }}</h1>
        <script>
    #模板变量也能用到script里面,因为render的时候,是把html页面里的所有代码转化为字符串拼接,不管是html还是script,都转化为字符串。
           alert({{ val }});
        </script>
    </body>
    </html>

    1. Form内容补充

    新建一个项目,新建app(python manage.py startapp app01),比如叫app01。

    settings.py,

    ALLOWED_HOSTS = ['*']
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01',
    ]
    
    STATIC_URL = '/static/'
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR,'static')
    )

    urls.py,

    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/', views.index),
    ]

    models.py,

    from django.db import models
    
    # Create your models here.
    
    class UserType(models.Model):
        caption = models.CharField(max_length=16)
    
    
    class UserInfo(models.Model):
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=32)
        user_type = models.ForeignKey('UserType')

    views.py,

    from django.shortcuts import render
    
    # Create your views here.
    
    from django import forms
    from app01 import models
    
    class IndexForm(forms.Form):
        # c = [
        #     (1, 'ceo'),
        #     (2, 'coo')
        # ]
    
        c = models.UserType.objects.all().values_list('id','caption')
    
        user_type_id = forms.IntegerField(widget=forms.Select(choices=c))
    
    
    
    def index(request):
        # for i in range(10):
        #     models.UserType.objects.create(caption='ce' + str(i))
        c = models.UserType.objects.all().count()
        print(c)
        # models.UserType.objects.create(caption='fdsaf')
        form = IndexForm()
        return render(request,'index.html',{'form':form})

    index.html,

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>yes</h1>
    #form.user_type_id读到的是数据库里的数据。
        {{ form.user_type_id }}
    </body>
    </html>

    上面的代码有个问题,UserType表里新插入了数据,必须得重启django才行,不然只刷新页面的话取到的还是老数据,以下是分析:

    class IndexForm(forms.Form):
    
    #注意这个c是静态属性,所以只会存在一份。
        c = models.UserType.objects.all().values_list('id','caption')
    
        user_type_id = forms.IntegerField(widget=forms.Select(choices=c))
    
    
    def index(request):
        # for i in range(10):
        #     models.UserType.objects.create(caption='ce' + str(i))
        c = models.UserType.objects.all().count()
        print(c)
        # models.UserType.objects.create(caption='fdsaf')
        form = IndexForm()
        return render(request,'index.html',{'form':form})
    
    #“form = IndexForm()”这里实例化对象后,内存里就有了“c”的值,用户刷新页面,虽然又重新执行了一次index方法,也又实例化了IndexForm类,但是“c”还是第一次的值,不会生成第二份。

    正因为有这个问题,所以要么修改了数据库后重启django,要么让用户每次访问页面时都重新读一次数据库数据,也就是每次执行方法时都做一遍读数据库操作, 就要用到构造方法了,代码见下,

    views.py,

    from django.shortcuts import render
    from django import forms
    from app01 import models
    
    class IndexForm(forms.Form):
        c = models.UserType.objects.all().values_list('id','caption')
    
        user_type_id = forms.IntegerField(widget=forms.Select(choices=c))
    
        def __init__(self,*args,**kwargs):
            super(IndexForm,self).__init__(*args,**kwargs)
            self.fields['user_type_id'].widget.choices = models.UserType.objects.all().values_list('id','caption')
    
    
    
    def index(request):
        # for i in range(10):
        #     models.UserType.objects.create(caption='ce' + str(i))
        c = models.UserType.objects.all().count()
        print(c)
        # models.UserType.objects.create(caption='cdo')
        form = IndexForm()
        return render(request,'index.html',{'form':form})
    
    #单独执行一下views.py就添加了一个数据,不重启django,直接刷新页面就会发现新添加的数据出来了。
    def addData():
        models.UserType.objects.create(caption='cccs')
    
    addData()

    2. Django数据操作之F和Q

    2.1 F

    现在有个需求,给所有人的工资都加500,数据库操作怎么做?
    可以写一个for循环,先读出某个人的人工资,然后update加500。
    也可以用F来实现这个功能,见下,
    from django.db.models import F
    models.UserInfo.objects.filter().update(salary=F('salary') + 500)
    这个操作其实是中的F('salary')就是取到数据库里salary的值。

    2.2 Q

    构造检索条件:
        1、传参
            models.UserInfo.objects.filter(id=123,name='zsc')
        2、传字典
            d = {'id':123,'name':'zsc'}
            models.UserInfo.objects.filter(**d)
                <input name='id'/>
                <input name='name'/>
                获取用户输入,并购造成字典,比如说字典是c,
                models.UserInfo.objects.filter(**c)    
            
        3、传Q对象
            #定义一个Q,并给Q制定规则
            q1 = Q()
            q1.connector = 'OR'
            q1.children.append(('id', 1))
            q1.children.append(('id', 10))
            q1.children.append(('id', 9))
            
            #将q1作为查询条件传入,得到的结果是id=1或id=2或id=3的对象。
            models.Tb1.objects.filter(q1)
        
            #将多个Q联合作为查询条件
            #定义一个空Q
            con = Q()
            q1 = Q()
            q1.connector = 'OR'
            q1.children.append(('id', 1))
            q1.children.append(('id', 10))
            q1.children.append(('id', 9))
            
            q2 = Q()
            q2.connector = 'OR'
            q2.children.append(('name', 'z'))
            q2.children.append(('name', 's'))
            q2.children.append(('name', 'c'))
            
            #将q1、q2以and的方式传给con
            con.add(q1, 'AND')
            con.add(q2, 'AND')
            
            #得到的结果是:首先查出id=1或id=2或id=3的对象,再从这些对象中筛出name=z或name=s或name=c的对象。
            models.Tb1.objects.filter(con)
            
            
            con = Q()
            q1 = Q()
            q1.connector = 'OR'
            q1.children.append(('ip', '10.102.33.5'))
            q1.children.append(('ip', '10.102.33.7'))
            q1.children.append(('ip', '10.102.33.8'))
            
            q2 = Q()
            q2.connector = 'OR'
            q2.children.append(('status', '在线'))
            
            #得到的结果是:ip等于10.102.33.5或10.102.33.6或10.102.33.7的,状态是在线的。
            con.add(q1, 'AND')   10.102.33.7
            con.add(q2, 'AND')   10.102.33.8

     3. model之多对多操作(一)

    方式二的话,第三张表是不可见的。

    多对多表示例,

    models.py,

    class Boy(models.Model):
        boyname = models.CharField(max_length=32)
    
    class Girl(models.Model):
        girlname = models.CharField(max_length=32)
    
    #第三张表,是不可见的
        b = models.ManyToManyField('Boy')   

    urls.py,

        url(r'^add_Boy/', views.add_Boy),
        url(r'^add_Girl/', views.add_Girl),
        url(r'^BoyAndGirl/', views.BoyAndGirl),

    views.py,

    def add_Boy(request):
    #用户在浏览器192.168.0.30:8000/add_Boy/?v=张善慈,这就是以get方式访问的,下面的bn就等于张善慈,可添加多个来实验
        bn = request.GET.get('v',None)
        print(bn)
        if bn:
            models.Boy.objects.create(boyname=bn)
        return HttpResponse(bn)
    
    def add_Girl(request):
        gn = request.GET.get('v',None)
        if gn:
            models.Girl.objects.create(girlname=gn)
        return HttpResponse(gn)
    
    def BoyAndGirl(request):
        g1 = models.Girl.objects.get(id=1)
        print(g1.girlname)
        b1 = models.Boy.objects.get(id=1)
    #g1.b就是第三张表了,g1.b.add(b1)就是将b1添加到第三张表里,与g1关联
        g1.b.add(b1)
        
    #g1.b.all()就是获取第三张表的对象,也可以filter什么的
        print(g1.b.all())
    
    
        return HttpResponse('ok')

     4. model之多对多操作(二)

    4.1 根据数字添加关系,批量添加关系

    g1 = models.Girl.objects.get(id=1)
    b1 = models.Boy.objects.get(id=1)
    
    g1.b.add(b1)#这就是将b1添加到第三张表了,还有一种更简单的方式,就是直接将数字添加,见下。
    g1.b.add(1) #这个“1”就是Boy表对应的id值。
    
    #所以g1.b.add(models.Boy.objects.get(id=1))与g1.b.add(1)作用一样,前者还需要查询一下,后者直接插入。
    
    
    
    如果想将Boy表里的所有数据都跟g1建立关系怎么办?首先可以用循环,再就是可以用下面的方式,
    ba = models.Boy.objects.all()
    g1.b.add(*ba) #*ba其实是一个列表,所以也可以直接将id数值以列表的形式传入,见下
    g1.b.add(*[1,2,3])

    4.2 正向和反向

    因为Boy表和Girl表建立了联系,所以Boy表中隐含着一个字段:girl_set。girl_set的作用与Girl表里的“b”作用一样。

    4.3 删除

    def BoyAndGirl(request):
        g1 = models.Girl.objects.get(id=1)
        # b1 = models.Boy.objects.get(id=1)
        g1.b.add(*[1,2,3])
        ba = g1.b.values_list()
        print(ba)
        print('-----')
    
        #g1.b.clear() #删除第三张表所有与g1有关的数据
        #g1.b.remove(1) #删除第三张表与g1有关的并且Boy里的id=1的数据
        g1.b.remove(*[1,3]) #删除第三张表与g1有关的并且Boy里的id=1和id=2的数据
        ba = g1.b.values_list()
        print(ba)
    
        return HttpResponse('ok')

    4.4 跨表查询数据

    models.Girl.objects.all().values('id','name','b__username') #这个“b__”就表示与Girl关联的Boy表。
        models.Boy.objects.all().values('id','name','girl__name') #这个“girl__”就表示与Boy表关联的Grill表。
        #这里有个问题是,Girl表里的第三张表的名字是可见的,就是我们定义的“b”(b = models.ManyToManyField('Boy')),而Boy表里没有关联信息,我们怎么知道应该写“girl”呢,
        #解决方法就是,随便写一个错误的,然后django会提示你能输入哪些,比如models.Boy.objects.all().values('id','name','fdsafdasffds'),打开浏览器页面会报错,见下,

    4.5 使用原生sql

    使用django的ORM做不了所有的SQL操作,就比如更新数据,使用django就是先删除再添加,所以有时候需要使用原生SQL来操作,见下,

    5. Django中间件

    settings.py,

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'md.tt.SimpleMiddleware',#自定义的
        'md.tt.M1',#自定义的
    ]

    5.1 自定义中间件

    5.1.1 django1.9及以前版本的

    settings.py,

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'md.tt.SimpleMiddleware', #自定义的
    ]

    与app01同级目录建一个md目录,然后md下建一个tt.py,内容,

    class M1:
        def process_request(self, request):
            print('M1.request')
    
        def process_response(self, request, response):
            print('M1.response')
            return response

    views.py,

    def md(request):
        print('md')
        return HttpResponse('ok')

    urls.py,

    url(r'^md/', views.md),

    执行结果:

    before
    md
    after

    5.1.2 django1.10

    只有定义类的时候有变化,需要继承自一个类,见下,

    #django1.10自定义的中间件类需要继承这个类
    from django.utils import deprecation
    
    class M1(deprecation.MiddlewareMixin):
        def process_request(self, request):
            print('M1.request')
    
        def process_response(self, request, response):
            print('M1.response')
            return response
    def process_request(self, request)和def process_response(self, request, response)这都是固定的。

    5.2 中间中可以定义的方法

    中间件中可以定义五个方法,分别是:

    • process_request(self,request),首先执行这个方法
    • process_view(self, request, callback, callback_args, callback_kwargs),逐个执行完所有插件的process_request方法后,再折回插件的开头,逐个执行process_view
    • process_template_response(self,request,response),调用render方法时才会调用这个方法,render(request,'index.html')是不调用render方法的,render_to_response('index')会调用render方法,但一般用不到,所以这个方法不常用
    • process_exception(self, request, exception),没出异常的时候不执行,出现异常后,先逐个执行process_exception方法,然后再折回末尾,逐个执行process_response方法
    • process_response(self, request, response),最后执行这个方法。

    方法执行示例,

    from django.utils import deprecation
    class M1(deprecation.MiddlewareMixin):
        def process_request(self,request):
            print('m1.request')
        process_view(self, request, callback, callback_args, callback_kwargs):
            print('m1.view')
        process_exception(self, request, exception):
            print('m1.exception')
        def process_response(self,request,response):
            print('m1.response')
            return response
        
    class M2(deprecation.MiddlewareMixin):
        def process_request(self,request):
            print('m2.request')
        process_view(self, request, callback, callback_args, callback_kwargs):
            print('m2.view')
        process_exception(self, request, exception):
            print('m2.exception')
        def process_response(self,request,response):
            print('m2.response')
            return response
            
    #没有异常的情况下,输出会是:
    m1.request
    m2.request
    m1.view
    m2.view
    m2.response
    m1.response
    
    #有异常的情况下,输出会是:
    m1.request
    m2.request
    m1.view
    m2.view
    m2.exception
    m1.exception
    m2.response
    m1.response

    5.3 新老版本关于process_response的区别

    比如一共有7个中间件,我们在第二个中间件里设置了一个return HttpResponse('out'),那么,

    在django1.9及以前的版本中,将会不执行第二个中间以后的process_request方法,而是直接跳到最后一个中间件,逐个执行process_response方法,也就是依然把所有中间件的process_response方法都执行一遍。

    在django1.10中,依然是不执行第二个中间以后的process_request方法,并且只从第二个中间件开始执行process_response方法,也就是只执行中间件2和中间件1的process_response方法。

    6.信号

    6.1 django内置信号

    Model signals
        pre_init                    # django的modal执行其构造方法前,自动触发
        post_init                   # django的modal执行其构造方法后,自动触发
        pre_save                    # django的modal对象保存前,自动触发
        post_save                   # django的modal对象保存后,自动触发
        pre_delete                  # django的modal对象删除前,自动触发
        post_delete                 # django的modal对象删除后,自动触发
        m2m_changed                 # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
        class_prepared              # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
    Management signals
        pre_migrate                 # 执行migrate命令前,自动触发
        post_migrate                # 执行migrate命令后,自动触发
    Request/response signals
        request_started             # 请求到来前,自动触发
        request_finished            # 请求结束后,自动触发
        got_request_exception       # 请求异常后,自动触发
    Test signals
        setting_changed             # 使用test测试修改配置文件时,自动触发
        template_rendered           # 使用test测试渲染模板时,自动触发
    Database Wrappers
        connection_created          # 创建数据库连接时,自动触发

    对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:

    from django.core.signals import request_finished
        from django.core.signals import request_started
        from django.core.signals import got_request_exception
    
        from django.db.models.signals import class_prepared
        from django.db.models.signals import pre_init, post_init
        from django.db.models.signals import pre_save, post_save
        from django.db.models.signals import pre_delete, post_delete
        from django.db.models.signals import m2m_changed
        from django.db.models.signals import pre_migrate, post_migrate
    
        from django.test.signals import setting_changed
        from django.test.signals import template_rendered
    
        from django.db.backends.signals import connection_created
    
    
        def callback(sender, **kwargs):
            print("xxoo_callback")
            print(sender,kwargs)
    
        xxoo.connect(callback)
        # xxoo指上述导入的内容
    from django.core.signals import request_finished
    from django.dispatch import receiver
    
    @receiver(request_finished)
    def my_callback(sender, **kwargs):
        print("Request finished!")

    6.2 自定义信号

    a. 定义信号
    import django.dispatch
    pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
    b. 注册信号
    def callback(sender, **kwargs):
        print("callback")
        print(sender,kwargs)
     
    pizza_done.connect(callback)
    c. 触发信号
    from 路径 import pizza_done
     
    pizza_done.send(sender='seven',toppings=123, size=456)

    由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。

    7. 读数据库里的数据实现分页

    urls.py,

    url(r'^page/', views.page),

    models.py,

    class UserType(models.Model):
        caption = models.CharField(max_length=16)

    page.html,

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
    
            .pager a{
    display:inline-block;
    20px;
    height:20px; padding: 5px; margin: 5px; background
    -color: aqua; } .pager a.active{ background-color: blueviolet; } </style> </head> <body> <table> <thead> <tr> <td> ID </td> <td> name </td> </tr> </thead> <tbody> {% for i in type_list %} <tr> <td>{{ i.id }}</td> <td>{{ i.caption }}</td> </tr> {% endfor %} </tbody> </table> <div class="pager"> {{ str_page }} </div> </body> </html>

    views.py,

    def page(request):
        # for i in range(100):
        #     models.UserType.objects.create(caption='CE' + str(i))
        p = request.GET.get('p',1)
        p = int(p)
        startpoint = (p - 1) * 10
        endpoint = p * 10
        type_list = models.UserType.objects.all()[startpoint:endpoint]
    
        page_count = models.UserType.objects.all().count()
        a,b = divmod(page_count,10)
        if b == 0:
            pass
        else:
            a = a + 1
        page_list = []
        for i in range(1,a+1):
    #如果循环到当前页,就加一个active类,以区别其他a标签的样式。
            if i == p:
                temp_a = "<a class='active' href='/page?p=%s'>%s</a>" % (i,i,)
            else:
                temp_a = "<a href='/page?p=%s'>%s</a>" % (i,i,)
            page_list.append(temp_a)
        from django.utils.safestring import mark_safe
        str_page = ''.join(page_list)
        str_page = mark_safe(str_page)
        return render(request,'page.html',{'type_list':type_list,'str_page':str_page})

    关于“from django.utils.safestring import mark_safe”的说明:

    为了预防xss攻击,数据库里读出的代码反馈到html页面后仅仅是字符串,而不会解析成html代码;举个例子:用户在评论区写了一个“alert('123')”,然后这段代码被写入数据库,假如将这段代码原样返回给html,那么用户只要打开页面就会执行评论区的“alert('123')”,无限弹窗;所以django不允许直接将代码解析到html页面,必须引入一个mark_safe方法,然后把字符串注册成安全代码模式,再反回给html页面才能解析。

    分页改进:

    显示当前页的前后五页的a标签,不显示多余的。

    views.py,

    def page(request):
        # for i in range(100):
        #     models.UserType.objects.create(caption='CE' + str(i))
        p = request.GET.get('p',1)
        p = int(p)
        startpoint = (p - 1) * 10
        endpoint = p * 10
        type_list = models.UserType.objects.all()[startpoint:endpoint]
    
        page_count = models.UserType.objects.all().count()
        a,b = divmod(page_count,10)
        if b == 0:
            pass
        else:
            a = a + 1
        page_list = []
        if p == 1:
            pre_page = "<a href='javascript:void(0)'>prepage</a>"
        else:
            pre_page = "<a href='/page?p=%s'>%s</a>" % (p-1 ,'prepage',)
        page_list.append(pre_page)
        if p <= 6:
            for i in range(1,12):
                if i == p:
                    temp_a = "<a class='active' href='/page?p=%s'>%s</a>" % (i, i,)
                else:
                    temp_a = "<a href='/page?p=%s'>%s</a>" % (i, i,)
                page_list.append(temp_a)
        elif a-p <= 5:
            for i in range(a-10,a+1):
                if i == p:
                    temp_a = "<a class='active' href='/page?p=%s'>%s</a>" % (i, i,)
                else:
                    temp_a = "<a href='/page?p=%s'>%s</a>" % (i, i,)
                page_list.append(temp_a)
        else:
            for i in range(p-5,p+6):
                if i == p:
                    temp_a = "<a class='active' href='/page?p=%s'>%s</a>" % (i, i,)
                else:
                    temp_a = "<a href='/page?p=%s'>%s</a>" % (i, i,)
                page_list.append(temp_a)
    
        from django.utils.safestring import mark_safe
        str_page = ''.join(page_list)
        str_page = mark_safe(str_page)
        return render(request,'page.html',{'type_list':type_list,'str_page':str_page})
  • 相关阅读:
    CSS选择器的权重与优先规则
    excel上传--phpExcel读取xls、xlsx
    反射与代理设计模式
    Map集合
    接口实际应用-工厂代理模式
    代码模型:对象比较
    Stream数据流
    集合输出接口-Iterator迭代输出-古老枚举输出:Enumeration
    Set集合接口-HashSet_TreeSet理解
    List类集接口-ArrayList
  • 原文地址:https://www.cnblogs.com/fuckily/p/6220740.html
Copyright © 2020-2023  润新知