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;
width: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})

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM