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