框架,即framework,特指爲解決一個開放性問題而設計的具備必定約束性的支撐結構。php
使用框架能夠幫你快速開發特定的系統。html
簡單地說,就是你用別人搭建好的舞臺來作表演。前端
嘗試搭建一個簡單的web框架:python
由於咱們不但願接觸到TCP鏈接、HTTP原始請求和響應格式,因此,須要一個統一的接口,讓咱們專心用Python編寫Web業務。mysql
這個接口就是WSGI:Web Server Gateway Interface。jquery
#---------------------myweb.py------------------------ from wsgiref.simple_server import make_server def foo1(request): f=open("alex.html","rb") data=f.read() f.close() return [data] def foo2(request): f=open("egon.html","rb") data=f.read() f.close() return [data] def reg(request): f=open("register.html","rb") data=f.read() f.close() return [data] def login(request): f=open("login.html","rb") data=f.read() f.close() return [data] def auth(request): print("++++++",request) user_union,pwd_union=request.get("QUERY_STRING").split("&") _,user=user_union.split("=") _,pwd=pwd_union.split("=") if user=="yuan" and pwd=="123": return [b"login successful"] else: return [b"user or password exists errors"] def routers(): URLpattern=( ("/login",login), ("/auth",auth), ("/alex",foo1), ("/egon",foo2), ("reg",reg) ) return URLpattern def application(environ,start_response): print("environ",environ) path=environ.get("PATH_INFO") print("path",path) start_response("200 ok",[("content-type","text/html")]) urlpattern=routers() func=None for item in urlpattern: if path==item[0]: func=item[1] break if func: return func(environ) else: return [b"404"] t=make_server("",8880,application) t.serve_forever() # 開始監聽t請求:
#------------------------login.html-------------------------- <body> <h1>登陸頁面</h1> <form action="http://127.0.0.1:8880/auth"> <p>姓名<input type="text" name="user"></p> <p>密碼<input type="password" name="pwd"></p> <input type="submit" value="提交"> </form> </body>
#---------------------egon.html-------------------------------- <body> <h1>welcome to egon's home</h1> </body> #----------------------alex.html---------------------------------- <body> <h1>welcome to alex's home</h1> </body>
注意:git
application()函數必須由WSGI服務器來調用。有不少符合WSGI規範的服務器,咱們能夠挑選一個來用。
Python內置了一個WSGI服務器,這個模塊叫wsgiref
application()函數就是符合WSGI標準的一個HTTP處理函數,它接收兩個參數:
//environ:一個包含全部HTTP請求信息的dict對象;
//start_response:一個發送HTTP響應的函數。
在application()函數中,調用:
start_response('200 OK', [('Content-Type', 'text/html')])
就發送了HTTP響應的Header,注意Header只能發送一次,也就是隻能調用一次start_response()函數。
start_response()函數接收兩個參數,一個是HTTP響應碼,一個是一組list表示的HTTP Header,每
個Header用一個包含兩個str的tuple表示。
一般狀況下,都應該把Content-Type頭髮送給瀏覽器。其餘不少經常使用的HTTP Header也應該發送。
而後,函數的返回值b'<h1>Hello, web!</h1>'將做爲HTTP響應的Body發送給瀏覽器。
有了WSGI,咱們關心的就是如何從environ這個dict對象拿到HTTP請求信息,而後構造HTML,
經過start_response()發送Header,最後返回Body。
此外,Django還有一個url分發器,它的做用是將一個個URL的頁面請求分發給不一樣的view處理,view再調用相應的Model和Template程序員
下面進入正式部分。web
主要知識點框架羅列:ajax
路由配置系統(URLconf)
pip install django
(雙等號能夠指定安裝版本,好比我的安裝1.11.4版本,就可使用:pip install django==
1.11.4)
說明:使用pip install django命令進行安裝時,會自動刪除舊版本,再安裝新版本
進入python shell,運行以下代碼:
import django django.get_version()
django-admin startproject project_name
項目結構:
例如建立一個名爲test1的項目,結構以下:
說明:
項目與應用關係:
一個項目有多個應用
一個應用能夠被多個項目擁有
命令:
python manage.py startapp blog
python manage.py runserver IP PORT
這樣咱們的django就啓動起來了!當咱們訪問:http://127.0.0.1:8080/時就能夠看到:
注意:Django 1.7.1 及以上的版本須要用如下命令 python manage.py makemigrations python manage.py migrate
python manage.py flush 此命令會詢問是 yes 仍是 no, 選擇 yes 會把數據所有清空掉,只留下空表。
python manage.py shell
python manage.py dbshell
python manage.py
查看全部的命令,忘記子名稱的時候特別有用。
在settings.py文件中,經過DATABASES項進行數據庫設置。
Django默認使用SQLite數據庫,同時支持MySQL等主流數據庫。
<1> sqlite django默認使用sqlite的數據庫,默認自帶sqlite的數據庫驅動 , 引擎名稱:django.db.backends.sqlite3 在settings裏有以下設置:
<2> mysql 引擎名稱:django.db.backends.mysql
mysql驅動程序 MySQLdb(mysql python) mysqlclient MySQL PyMySQL(純python的mysql驅動程序)
若是咱們想要更改成MySQL數據庫,須要修改以下:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'books', #你的數據庫名稱 'USER': 'root', #你的數據庫用戶名 'PASSWORD': '', #你的數據庫密碼 'HOST': '', #你的數據庫主機,留空默認爲localhost 'PORT': '3306', #你的數據庫端口 } }
NAME即數據庫的名字,在mysql鏈接前該數據庫必須已經建立,而上面的sqlite數據庫下的db.sqlite3則是項目自動建立 USER和PASSWORD分別是數據庫的用戶名和密碼。 設置完後,再啓動咱們的Django項目前,咱們須要激活咱們的mysql。 而後,啓動項目,會報錯:no module named MySQLdb 這是由於django默認你導入的驅動是MySQLdb,但是MySQLdb對於py3有很大問題,因此咱們須要的驅動是PyMySQL 因此,咱們只須要找到項目名文件下的__init__,在裏面寫入: import pymysql pymysql.install_as_MySQLdb() 問題解決!
settings文件中static的配置以下:
STATIC文件還能夠配置STATICFILES_DIRS,指定額外的靜態文件存儲位置。
#注意1: #爲了後端的更改不會影響前端的引入,避免形成前端大量修改 STATIC_URL = '/static/' #引用名 STATICFILES_DIRS = ( os.path.join(BASE_DIR,"statics") #實際名 ,即實際文件夾的名字 ) #django對引用名和實際名進行映射,引用時,只能按照引用名來,不能按實際名去找
例如:咱們寫一個模板文件,通常會須要引入JS文件
咱們常寫的格式爲:
#<script src="/statics/jquery-3.2.1.js"></script>
#--------------錯誤--------------------------
正確引用方式:
必須用STATIC_URL = '/static/':
#<script src="/static/jquery-3.2.1.js"></script>
#注意2(statics文件夾寫在不一樣的app下,靜態文件的調用): STATIC_URL = '/static/' STATICFILES_DIRS=( ('hello',os.path.join(BASE_DIR,"app01","statics")) , ) #<script src="/static/hello/jquery-3.2.1.js"></script>
#注意3: STATIC_URL = '/static/' {% load staticfiles %} # <script src={% static "jquery-3.2.1.js" %}></script>
應用場景:對於每次建立一個對象,想顯示對應的raw sql,須要在settings加上日誌記錄部分:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
例:
ROOT_URLCONF = 'BlogSM.urls' (建立的項目名稱爲:BlogSM)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^add/',views.add),
url(r'^$',views.add), #(此條通常用做增長用戶體驗,好比首頁展現特定內容)
]
http://www.itcast.cn/python/1/?i=1&p=new,只匹配「/python/1/」部分
urlpatterns = [ url(r'^articles/2003/$', views.special_case_2003), url(r'^articles/([0-9]{4})/$', views.year_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ]
一些請求的例子: /articles/2005/3/ 不匹配任何URL 模式,由於列表中的第三個模式要求月份應該是兩個數字。 /articles/2003/ 將匹配列表中的第一個模式不是第二個,由於模式按順序匹配,第一個會首先測試是否匹配。 /articles/2005/03/ 請求將匹配列表中的第三個模式。Django 將調用函數 views.month_archive(request, '2005', '03')。
正則表達式非命名組(經過圓括號),經過位置參數傳遞給視圖
正則表達式命名組,經過關鍵字參數傳遞給視圖
語法:(?P<name>pattern)
,其中name
是組的名稱,pattern
是要匹配的模式。
例如:
urlpatterns = [ url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ]
]
/articles/2005/03/ 請求將調用views.month_archive(request, year='2005', month='03')函數 /articles/2003/03/03/ 請求將調用函數views.article_detail(request, year='2003', month='03', day='03')。
URLconf 在請求的URL 上查找,將它當作一個普通的Python 字符串。不包括GET和POST參數以及域名。 例如,http://www.example.com/myapp/ 請求中,URLconf 將查找myapp/。 在http://www.example.com/myapp/?page=3 請求中,URLconf 仍將查找myapp/。 URLconf 不檢查請求的方法。換句話講,全部的請求方法 —— 同一個URL的POST、GET、HEAD等等 —— 都將路由到相同的函數。
每一個捕獲的參數都做爲一個普通的Python 字符串傳遞給視圖,不管正則表達式使用的是什麼匹配方式。例如,下面這行URLconf 中: url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), views.year_archive() 的year 參數將是一個字符串
有一個方便的小技巧是指定視圖參數的默認值。 下面是一個URLconf 和視圖的示例:
# URLconf from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/$', views.page), url(r'^blog/page(?P<num>[0-9]+)/$', views.page), ] # View (in blog/views.py) def page(request, num="1"): ...
在上面的例子中,兩個URL模式指向同一個視圖views.page
—— 可是第一個模式不會從URL 中捕獲任何值。若是第一個模式匹配,page()
函數將使用num
參數的默認值"1"。若是第二個模式匹配,page()
將使用正則表達式捕獲的num
值。
from django.conf.urls import include, url urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^blog/', include('blog.urls')), ]
匹配過程:先與主URLconf匹配,成功後再用剩餘的部分與應用中的URLconf匹配
請求http://www.itcast.cn/booktest/1/ 在sesstings.py中的配置: url(r'^booktest/', include('booktest.urls', namespace='booktest')), 在booktest應用urls.py中的配置 url(r'^([0-9]+)/$', views.detail, name='detail'), 匹配部分是:/booktest/1/ 匹配過程:在settings.py中與「booktest/」成功,再用「1/」與booktest應用的urls匹配
''' urlpatterns = [ url(r'^index',views.index,name='INDEX'), ] ################### def index(req): if req.method=='POST': username=req.POST.get('username') password=req.POST.get('password') if username=='alex' and password=='123': return HttpResponse("登錄成功") return render(req,'index.html') ##################### <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {# <form action="/index/" method="post">#} <form action="{% url 'INDEX' %}" method="post"> 用戶名:<input type="text" name="username"> 密碼:<input type="password" name="password"> <input type="submit" value="submit"> </form> </body> </html> ####################### '''
Django原生自帶幾個默認視圖用於處理HTTP錯誤
<!DOCTYPE html> <html> <head> <title></title> </head> <body> 找不到了 <hr/> {{request_path}} </body> </html>
在settings.py中修改調試
DEBUG = False ALLOWED_HOSTS = ['*', ]
請求一個不存在的地址
http://127.0.0.1:8000/test/
defaults.bad_request(request, template_name='400.html') 錯誤來自客戶端的操做 當用戶進行的操做在安全方面可疑的時候,例如篡改會話cookie
''' http請求-響應過程當中有兩個核心對象: http請求對象:HttpRequest http響應響應:HttpResponse 所在位置:django.http '''
一個視圖函數,或者簡短來講叫作視圖,是一個簡單的Python函數,它接受web請求,而且返回web響應。
響應能夠是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片. . . 是任何東西均可以。
不管視圖自己包含什麼邏輯,都要返回響應。
返回當前日期和時間: def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
每一個視圖函數都應接收HttpRequest對象做爲第一個參數,通常叫作request。
--------------render(request, template_name[, context]) 結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的 HttpResponse 對象。 參數: request: 用於生成響應的請求對象。 template_name:要使用的模板的完整名稱,可選的參數 context:添加到模板上下文的一個字典。默認是一個空字典。若是字典中的某個值是可調用的,視圖將在渲染模板以前調用它。 content_type:生成的文檔要使用的MIME類型。默認爲DEFAULT_CONTENT_TYPE 設置的值。 status:響應的狀態碼。默認爲200。
-----------------------------------url.py url(r"login", views.login), url(r"yuan_back", views.yuan_back), -----------------------------------views.py def login(req): if req.method=="POST": if 1: # return redirect("/yuan_back/") name="yuanhao" return render(req,"my backend.html",locals()) return render(req,"login.html",locals()) def yuan_back(req): name="苑昊" return render(req,"my backend.html",locals()) -----------------------------------login.html <form action="/login/" method="post"> <p>姓名<input type="text" name="username"></p> <p>性別<input type="text" name="sex"></p> <p>郵箱<input type="text" name="email"></p> <p><input type="submit" value="submit"></p> </form> -----------------------------------my backend.html <h1>用戶{{ name }}你好</h1> #總結: render和redirect的區別: # 1 若是 render的頁面須要模板語言渲染,須要的將數據庫的數據加載到html,那麼全部的這一部分 # 除了寫在yuan_back的視圖函數中,必須還要寫在login中,代碼重複,沒有解耦. # 2 the most important: url沒有跳轉到/yuan_back/,而是還在/login/,因此當刷新後 # 又得從新登陸.
dict.get('鍵',default) 或簡寫爲 dict['鍵']
dict.getlist('鍵',default) GET屬性
def getTest1(request): return render(request,'booktest/getTest1.html') def getTest2(request): return render(request,'booktest/getTest2.html') def getTest3(request): return render(request,'booktest/getTest3.html')
url(r'^getTest1/$', views.getTest1), url(r'^getTest2/$', views.getTest2), url(r'^getTest3/$', views.getTest3),
<html> <head> <title>Title</title> </head> <body> 連接1:一個鍵傳遞一個值 <a href="/getTest2/?a=1&b=2">gettest2</a><br> 連接2:一個鍵傳遞多個值 <a href="/getTest3/?a=1&a=2&b=3">gettest3</a> </body> </html>
def getTest2(request): a=request.GET['a'] b=request.GET['b'] context={'a':a,'b':b} return render(request,'booktest/getTest2.html',context)
<html> <head> <title>Title</title> </head> <body> a:{{ a }}<br> b:{{ b }} </body> </html>
def getTest3(request): a=request.GET.getlist('a') b=request.GET['b'] context={'a':a,'b':b} return render(request,'booktest/getTest3.html',context)
<html> <head> <title>Title</title> </head> <body> a:{% for item in a %} {{ item }} {% endfor %} <br> b:{{ b }} </body> </html>
def postTest1(request): return render(request,'booktest/postTest1.html')
url(r'^postTest1$',views.postTest1)
建立模板postTest1.html
<html> <head> <title>Title</title> </head> <body> <form method="post" action="/postTest2/"> 姓名:<input type="text" name="uname"/><br> 密碼:<input type="password" name="upwd"/><br> 性別:<input type="radio" name="ugender" value="1"/>男 <input type="radio" name="ugender" value="0"/>女<br> 愛好:<input type="checkbox" name="uhobby" value="胸口碎大石"/>胸口碎大石 <input type="checkbox" name="uhobby" value="跳樓"/>跳樓 <input type="checkbox" name="uhobby" value="喝酒"/>喝酒 <input type="checkbox" name="uhobby" value="登山"/>登山<br> <input type="submit" value="提交"/> </form> </body> </html>
def postTest2(request): uname=request.POST['uname'] upwd=request.POST['upwd'] ugender=request.POST['ugender'] uhobby=request.POST.getlist('uhobby') context={'uname':uname,'upwd':upwd,'ugender':ugender,'uhobby':uhobby} return render(request,'booktest/postTest2.html',context)
url(r'^postTest2$',views.postTest2)
<html> <head> <title>Title</title> </head> <body> {{ uname }}<br> {{ upwd }}<br> {{ ugender }}<br> {{ uhobby }} </body> </html>
注意:使用表單提交,註釋掉settings.py中的中間件crsf
from django.http import HttpResponse def index(request): return HttpResponse('你好')
from django.http import JsonResponse def index2(requeset): return JsonResponse({'list': 'abc'})
cookie的工做原理是:由服務器產生內容,瀏覽器收到請求後保存在本地;當瀏覽器再次訪問時,瀏覽器會自動帶上cookie,這樣服務器就能經過cookie的內容來判斷這個是「誰」了。
cookie雖然在必定程度上解決了「保持狀態」的需求,可是因爲cookie自己最大支持4096字節,以及cookie自己保存在客戶端,可能被攔截或竊取,所以就須要有一種新的東西,它能支持更多的字節,而且他保存在服務器,有較高的安全性。這就是session。
咱們能夠給每一個客戶端的cookie分配一個惟一的id,這樣用戶在訪問時,經過cookie,服務器就知道來的人是「誰」。而後咱們再根據不一樣的cookie的id,在服務器上保存一段時間的私密資料,如「帳號密碼」等等。
三、總結而言:cookie彌補了http無狀態的不足,讓服務器知道來的人是「誰」;可是cookie以文本的形式保存在本地,自身安全性較差;因此咱們就經過cookie識別不一樣的用戶,對應的在session裏保存私密的信息以及超過4096字節的文本。
場景:
一個登錄頁面,在驗證了用戶名和密碼的正確性後跳轉到後臺的頁面。
可是測試發現,若是繞過登錄頁面,直接輸入後臺的url地址也能夠直接訪問的。這個顯然是不合理的。
咱們缺失的就是cookie和session配合的驗證。
每當咱們使用一款瀏覽器訪問一個登錄頁面的時候,一旦咱們經過了認證。服務器端就會發送一組隨機惟一的字符串(假設是123abc)到瀏覽器端,這個被存儲在瀏覽端的東西就叫cookie。而服務器端也會本身存儲一下用戶當前的狀態,好比login=true,username=hahaha之類的用戶信息。可是這種存儲是以字典形式存儲的,字典的惟一key就是剛纔發給用戶的惟一的cookie值。那麼若是在服務器端查看session信息的話,理論上就會看到以下樣子的字典
{'123abc':{'login':true,'username:hahaha'}}
由於每一個cookie都是惟一的,因此咱們在電腦上換個瀏覽器再登錄同一個網站也須要再次驗證。那麼爲何說咱們只是理論上看到這樣子的字典呢?由於處於安全性的考慮,其實對於上面那個大字典不光key值123abc是被加密的,value值{'login':true,'username:hahaha'}在服務器端也是同樣被加密的。因此咱們服務器上就算打開session信息看到的也是相似與如下樣子的東西
{'123abc':dasdasdasd1231231da1231231}
---------------------views.py def login(request): if request.method == "POST": user=request.POST.get("user") pwd=request.POST.get("pwd") if user == "kaylee" and pwd == "123": print(request.COOKIES) #第一次:{} print(request.session) #第一次:<django.contrib.sessions.backends.db.SessionStore object at 0x0000000003D4D0F0> obj=redirect("/index/") #這一步不會走index視圖,注意 return redirect纔會重定向 obj.set_cookie("Yuan123",11111111,max_age= 10) #三個參數:key,value,期限 return obj return render(request,"login.html") def index(request): print("+++++++++++++",request.COOKIES) #第一次{} 第二次:{'Yuan123': '11111111'} 第三次:{'Yuan123': '11111111'} print("-------------",request.session) #<django.contrib.sessions.backends.db.SessionStore object at 0x0000000003A9A828> is_login=request.COOKIES.get("Yuan123",None) if is_login: return render(request,"index.html") else: return redirect("/login/")
--------------------------------views.py from django.shortcuts import render from django.shortcuts import redirect def login(request): if request.method=="POST": username=request.POST['username'] pwd=request.POST['passwd'] if username=='abc' and pwd=='123': #設置session內部的字典內容 request.session['is_login']='true' request.session['username']='abc' #登陸成功就將url重定向到後臺的url return redirect('/backend/') #登陸不成功或第一訪問就停留在登陸頁面 return render(request,'login.html') def backend(request): """ 這裏必須用讀取字典的get()方法把is_login的value缺省設置爲False, 當用戶訪問backend這個url先嚐試獲取這個瀏覽器對應的session中的 is_login的值。若是對方登陸成功的話,在login裏就已經把is_login 的值修改成了True,反之這個值就是False的 """ is_login=request.session.get('is_login',False) #若是爲真,就說明用戶是正常登錄的 if is_login: #獲取字典的內容並傳入頁面文件 cookie_content=request.COOKIES session_content=request.session username=request.session['username'] return render(request,'backend.html', { 'cookie_content':cookie_content, 'session_content':session_content, 'username':username }) else: """ 若是訪問的時候沒有攜帶正確的session, 就直接被重定向url回login頁面 """ return redirect('/login/') def logout(request): """ 直接經過request.session['is_login']回去返回的時候, 若是is_login對應的value值不存在會致使程序異常。因此 須要作異常處理 """ try: #刪除is_login對應的value值 del request.session['is_login'] except KeyError: pass #點擊註銷以後,直接重定向回登陸頁面 return redirect('/login/')
--------------------------------------urls.py from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.login), url(r'^backend/', views.backend), url(r'^logout/', views.logout), ]
--------------------backend.html-----------------內容截取 <div class="container"> <h2>cookie 內容是 {{ cookie_content }}</h2> <h2>session 內容是 {{ session_content }}</h2> <h2>登陸用戶名 :{{ username }}</h2> <a href="http://830909.blog.51cto.com/logout/">註銷</a> </div>
頁面顯示結果:
從上圖中咱們看到有一下幾點:
一、login頁面正確登陸的話,後臺頁面能夠獲取到瀏覽器攜帶的cookie的。
二、第一行的sessionid其實就是cookie值
三、session的內容是加密的,從客戶端獲取不到session的內容
四、服務端能夠經過預設的key值取出session的內容並打印到前端
從火狐瀏覽器裏查看cookie
django的session默認是存儲在數據庫裏的,咱們再到數據庫查看一下真正session內容
cookie、session總結:
# 一、獲取Cookie: # request.COOKIES['key'] # request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) # 參數: # default: 默認值 # salt: 加密鹽 # max_age: 後臺控制過時時間 # 二、設置Cookie: # rep = HttpResponse(...) 或 rep = render(request, ...) # # rep.set_cookie(key,value,...) # rep.set_signed_cookie(key,value,salt='加密鹽',...) # 參數: # key, 鍵 # value='', 值 # max_age=None, 超時時間 # expires=None, 超時時間(IE requires expires, so set it if hasn't been already.) # path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:跟路徑的cookie能夠被任何url的頁面訪問 # domain=None, Cookie生效的域名 # secure=False, https傳輸 # httponly=False 只能http協議傳輸,沒法被JavaScript獲取(不是絕對,底層抓包能夠獲取到也能夠被覆蓋) # 因爲cookie保存在客戶端的電腦上,因此,JavaScript和jquery也能夠操做cookie。 # <script src='/static/js/jquery.cookie.js'></script> # $.cookie("list_pager_num", 30,{ path: '/' });
Django中默認支持Session,其內部提供了5種類型的Session供開發者使用:
一、數據庫Session
jango默認支持Session,而且默認是將Session數據存儲在數據庫中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默認) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串(默認) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路徑(默認) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默認) SESSION_COOKIE_SECURE = False # 是否Https傳輸cookie(默認) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http傳輸(默認) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默認) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否關閉瀏覽器使得Session過時(默認) SESSION_SAVE_EVERY_REQUEST = False # 是否每次請求都保存Session,默認修改以後才保存(默認) b. 使用 def index(request): # 獲取、設置、刪除Session中數據 request.session['k1'] request.session.get('k1',None) request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在則不設置 del request.session['k1'] # 全部 鍵、值、鍵值對 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 用戶session的隨機字符串 request.session.session_key # 將全部Session失效日期小於當前日期的數據刪除 request.session.clear_expired() # 檢查 用戶session的隨機字符串 在數據庫中是否 request.session.exists("session_key") # 刪除當前用戶的全部Session數據 request.session.delete("session_key") ...
二、緩存Session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的緩存別名(默認內存緩存,也能夠是memcache),此處別名依賴緩存的設置 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路徑 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https傳輸cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http傳輸 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否關閉瀏覽器使得Session過時 SESSION_SAVE_EVERY_REQUEST = False # 是否每次請求都保存Session,默認修改以後才保存 b. 使用 同上
三、文件Session
. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 緩存文件路徑,若是爲None,則使用tempfile模塊獲取一個臨時地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路徑 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https傳輸cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http傳輸 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否關閉瀏覽器使得Session過時 SESSION_SAVE_EVERY_REQUEST = False # 是否每次請求都保存Session,默認修改以後才保存 b. 使用 同上
四、緩存+數據庫Session
數據庫用於作持久化,緩存用於提升效率 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 b. 使用 同上
五、加密cookie Session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 b. 使用 同上
擴展:Session用戶驗證
def login(func): def wrap(request, *args, **kwargs): # 若是未登錄,跳轉到指定頁面 if request.path == '/test/': return redirect('http://www.baidu.com') return func(request, *args, **kwargs) return wrap
python的模板:HTML代碼+邏輯控制代碼
def current_time(req): now=datetime.datetime.now() return render(req, 'current_datetime.html', {'current_date':now})
咱們經過 context 傳遞的簡單參數值主要是字符串,然而,模板系統可以很是簡潔地處理更加複雜的數據結構,例如list、dictionary和自定義的對象。在 Django 模板中遍歷複雜數據結構的關鍵是句點字符 (.)。
首先,句點可用於訪問列表索引
-----------views.py fruit=['apples', 'bananas', 'carrots'] ----------------templates <h2>{{ fruit.0 }}</h2>
訪問字典:
-----------views.py
dic={"name":"kaylee","age":18}
----------------templates
{% for i,v in dic.items %}
{{ i }} {{ v }}
{% endfor %}
{{ dic.name }}
{{ dic.age }}
#一樣,也能夠經過句點來訪問對象的屬性
比方說, Python 的 datetime.date 對象有 #year 、 month 和 day 幾個屬性,你一樣能夠在模板中使用句點來訪問這些屬性:
-----------views.py
d = datetime.date(1993, 5, 2) ----------------templates {{ d.year }} {{ d.month }} {{ d.day }}
使用了一個自定義的類,經過實例變量加一點(dots)來訪問它的屬性,這個方法適 # 用於任意的對象。
-----------views.py
>>> class Person(object): ... def __init__(self, first_name, last_name): ... self.first_name, self.last_name = first_name, last_name ----------------templates {{ person.first_name }} {{ person.last_name }}
# 點語法也能夠用來引用對象的方法。 例如,每一個 Python 字符串都有 upper() 和 isdigit() # 方法,你在模板中可使用一樣的句點語法來調用它們:
-----------views.py
Context={'var': '123'}
----------------templates
{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}
# 1 add : 給變量加上相應的值 # # 2 addslashes : 給變量中的引號前加上斜線 # # 3 capfirst : 首字母大寫 # # 4 cut : 從字符串中移除指定的字符 # # 5 date : 格式化日期字符串 # # 6 default : 若是值是False,就替換成設置的默認值,不然就是用原本的值 # # 7 default_if_none: 若是值是None,就替換成設置的默認值,不然就使用原本的值
#實例: #value1="aBcDe" {{ value1|upper }}<br> #value2=5 {{ value2|add:3 }}<br> #value3='he llo wo r ld' {{ value3|cut:' ' }}<br> #import datetime #value4=datetime.datetime.now() {{ value4|date:'Y-m-d' }}<br> #value5=[] {{ value5|default:'空的' }}<br> #value6='<a href="#">跳轉</a>' {{ value6 }} {% autoescape off %} {{ value6 }} {% endautoescape %} {{ value6|safe }}<br> {{ value6|striptags }} #value7='1234' {{ value7|filesizeformat }}<br> {{ value7|first }}<br> {{ value7|length }}<br> {{ value7|slice:":-1" }}<br> #value8='http://www.baidu.com/?a=1&b=3' {{ value8|urlencode }}<br> value9='hello I am yuan'
語法格式: {% tags %}
{% if %}標籤計算一個變量值,若是是「true」,即它存在、不爲空而且不是false的boolean值,系統則會顯示{% if %}和{% endif %}間的全部內容
{% if num >= 100 and 8 %} {% if num > 200 %} <p>num大於200</p> {% else %} <p>num大於100小於200</p> {% endif %} {% elif num < 100%} <p>num小於100</p> {% else %} <p>num等於100</p> {% endif %}
{% if %} 標籤接受and,or或者not來測試多個變量值或者否認一個給定的變量 {% if %} 標籤不容許同一標籤裏同時出現and和or,不然邏輯容易產生歧義,例以下面的標籤是不合法的: {% if obj1 and obj2 or obj3 %}
{% for %}標籤容許你按順序遍歷一個序列中的各個元素,每次循環模板系統都會渲染{% for %}和{% endfor %}之間的全部內容
-----------views.py
class Person(object): def __init__(self,name): self.name=name p1=Person("egon") p2=Person("阿毛") p3=Person("ago") querySet=[p1,p2,p3] ----------------templates {% for person in querySet %} <p>{{ person.name }}</p> {% endfor %}
#在標籤裏添加reversed來反序循環列表: {% for obj in list reversed %} ... {% endfor %}
#{% for %}標籤能夠嵌套: {% for country in countries %} <h1>{{ country.name }}</h1> <ul> {% for city in country.city_list %} <li>{{ city }}</li> {% endfor %} </ul> {% endfor %}
#系統不支持中斷循環,系統也不支持continue語句,{% for %}標籤內置了一個forloop模板變量, #這個變量含有一些屬性能夠提供給你一些關於循環的信息 1,forloop.counter表示循環的次數,它從1開始計數,第一次循環設爲1: {% for item in todo_list %} <p>{{ forloop.counter }}: {{ item }}</p> {% endfor %} 2,forloop.counter0 相似於forloop.counter,但它是從0開始計數,第一次循環設爲0 3,forloop.revcounter 4,forloop.revcounter0 5,forloop.first當第一次循環時值爲True,在特別狀況下頗有用: {% for object in objects %} {% if forloop.first %}<li class="first">{% else %}<li>{% endif %} {{ object }} </li> {% endfor %} # 富有魔力的forloop變量只能在循環中獲得,當模板解析器到達{% endfor %}時forloop就消失了 # 若是你的模板context已經包含一個叫forloop的變量,Django會用{% for %}標籤替代它 # Django會在for標籤的塊中覆蓋你定義的forloop變量的值 # 在其餘非循環的地方,你的forloop變量仍然可用
#{% empty %} {{li }} {% for i in li %} <li>{{ forloop.counter0 }}----{{ i }}</li> {% empty %} <li>this is empty!</li> {% endfor %} # [11, 22, 33, 44, 55] # 0----11 # 1----22 # 2----33 # 3----44 # 4----55
用於生成csrf_token的標籤,用於防治跨站攻擊驗證。 其實,這裏是會生成一個input標籤,和其餘表單標籤一塊兒提交給後臺的。
引用路由配置的地址 <form action="{% url "bieming"%}" > <input type="text"> <input type="submit"value="提交"> {%csrf_token%} </form>
用更簡單的變量名替代複雜的變量名 {% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
禁止render {% verbatim %} {{ hello }} {% endverbatim %}
a、在app中建立templatetags模塊(必須的)
b、建立任意 .py 文件,如:my_tags.py
from django import template from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改變 @register.filter def filter_multi(v1,v2): return v1 * v2 @register.simple_tag def simple_tag_multi(v1,v2): return v1 * v2 @register.simple_tag def my_input(id,arg): result = "<input type='text' id='%s' class='%s' />" %(id,arg,) return mark_safe(result)
c、在使用自定義simple_tag和filter的html文件中導入以前建立的 my_tags.py :{% load my_tags %}
d、使用simple_tag和filter(如何調用)
-------------------------------.html {% load xxx %} #首行 # num=12 {{ num|filter_multi:2 }} #24 {{ num|filter_multi:"[22,333,4444]" }} {% simple_tag_multi 2 5 %} 參數不限,但不能放在if for語句中 {% simple_tag_multi num 5 %}
e、在settings中的INSTALLED_APPS配置當前app,否則django沒法找到自定義的simple_tag.
注意:
filter能夠用在if等語句後,simple_tag不能夠
{% if num|filter_multi:30 > 100 %} {{ num|filter_multi:30 }} {% endif %}
解決問題:減小共用頁面區域(好比站點導航)所引發的重複和冗餘代碼
本質上來講,模板繼承就是先構造一個基礎框架模板,然後在其子模板中對它所包含站點公用部分和定義塊進行重載。
你能夠對那些不一樣 的代碼段進行定義,而不是 共同 代碼段。
第一步是定義 基礎模板,該框架以後將由子模板所繼承。
示例:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>{% block title %}{% endblock %}</title> </head> <body> <h1>My helpful timestamp site</h1> {% block content %}{% endblock %} {% block footer %} <hr> <p>Thanks for visiting my site.</p> {% endblock %} </body> </html>
子模板的做用就是重載、添加或保留那些塊的內容。
咱們使用模板標籤: {% block %} 。 全部的 {% block %} 標籤告訴模板引擎,子模板能夠重載這些部分。 每一個{% block %}標籤所要作的是告訴模板引擎,該模板下的這一塊內容將有可能被子模板覆蓋。
子模版一:
{% extends "base.html" %} {% block title %}The current time{% endblock %} {% block content %} <p>It is now {{ current_date }}.</p> {% endblock %}
子模版二:
{% extends "base.html" %} {% block title %}Future time{% endblock %} {% block content %} <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> {% endblock %}
注意因爲子模板並無定義 footer 塊,模板系統將使用在父模板中定義的值。
父模板 {% block %} 標籤中的內容老是被看成一條退路。繼承並不會影響到模板的上下文。
換句話說,任何處在繼承樹上的模板均可以訪問到你傳到模板中的每個模板變量。
你能夠根據須要使用任意多的繼承次數。
如下是使用模板繼承的一些訣竅:
<1>若是在模板中使用 {% extends %} ,必須保證其爲模板中的第一個模板標記。 不然,模板繼承將不起做用。 <2>通常來講,基礎模板中的 {% block %} 標籤越多越好。 記住,子模板沒必要定義父模板中全部的代碼塊,所以 你能夠用合理的缺省值對一些代碼塊進行填充,而後只對子模板所需的代碼塊進行(重)定義。 俗話說,鉤子越 多越好。 <3>若是發覺本身在多個模板之間拷貝代碼,你應該考慮將該代碼段放置到父模板的某個 {% block %} 中。 若是你須要訪問父模板中的塊的內容,使用 {{ block.super }}這個標籤吧,這一個魔法變量將會表現出父模 板中的內容。 若是隻想在上級代碼塊基礎上添加內容,而不是所有重載,該變量就顯得很是有用了。 <4>不容許在同一個模板中定義多個同名的 {% block %} 。 存在這樣的限制是由於block 標籤的工做方式是雙向的。 也就是說,block 標籤不只挖了一個要填的坑,也定義了在父模板中這個坑所填充的內容。若是模板中出現了兩個 相同名稱的 {% block %} 標籤,父模板將無從得知要使用哪一個塊的內容。
有一個數據表,就有一個模型類與之對應;
打開models.py文件,定義模型類;
模型類繼承自models.Model類;
說明:不須要定義主鍵列,在生成時會自動添加,而且值爲自動增加
當輸出對象時,會調用對象的str方法
例如咱們設計兩張表:圖書表和英雄表,二者關係爲一對多
class BookInfo(models.Model): btitle = models.CharField(max_length=20) bpub_date = models.DateTimeField() def _ _str_ _(self): return "%d" % self.pk class HeroInfo(models.Model): hname = models.CharField(max_length=20) hgender = models.BooleanField() hcontent = models.CharField(max_length=100) hBook = models.ForeignKey('BookInfo') def _ _str_ _(self): return "%d" % self.pk
數據庫與ORM(對象關係映射) 目的: 經過python代碼實現對數據庫的增刪改查 在ORM中, 表名-----------類名 字段-----------類屬性 表中的一條記錄-------------類實例對象
注意:因爲Django版本不一樣,因此實現上述的方式也不一樣,例如本人用的Django版本爲1.11.4版本,系統會自動添加,無需本身再添加
python manage.py makemigrations
python manage.py migrate
用一訪問多:對象.模型類小寫_set 用一訪問一:對象.模型類小寫 訪問id:對象.屬性_id
以建立四張表爲例,模擬建立模型類的過程:
四張表:書表、做者表、做者詳細信息表、出版社表
其中書表與出版社表是一對多(one-to-many)關係,也被稱做外鍵。
書表與做者表是多對多(many-to-many)關係。
此外,做者表、做者詳細信息表二者是一對一關係,咱們知道:
一對一:實質就是在主外鍵(author_id就是foreign key)的關係基礎上,給外鍵加了一個UNIQUE=True的屬性;
一對多:就是主外鍵關係;(foreign key)
多對多:(ManyToManyField) 自動建立第三張表;(固然咱們也能夠本身建立第三張表:兩個foreign key)
注意在models文件中定義模型類時的前後順序:
from django.db import models
class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=30) def __str__(self): return self.name class AuthorDetail(models.Model): sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),)) email = models.EmailField() address = models.CharField(max_length=50) birthday = models.DateField() author = models.OneToOneField(Author) class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2,default=10) def __str__(self): return self.title
注意:
字段類型 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, 這個字段在表中必須有惟一值
tips:
批量導入數據: 例如類名爲:Book Book.objects.bulk_create(book_list)
Book.objects.filter(id=1).delete()
ORM之增(create,save):
from app01.models import *
#create方式一: Author.objects.create(name='Alvin') #create方式二: Author.objects.create(**{"name":"alex"}) #save方式一: author=Author(name="alvin") author.save() #save方式二: author=Author() author.name="alvin" author.save()
重點:如何處理外鍵關係的字段如一對多的publisher和
多對多的authors?
#一對多(ForeignKey): #方式一: 因爲綁定一對多的字段,好比publish,存到數據庫中的字段名叫publish_id,因此咱們能夠直接給這個 # 字段設定對應值: Book.objects.create(title='php', publisher_id=2, #這裏的2是指爲該book對象綁定了Publisher表中id=2的行對象 publication_date='2017-7-7', price=99) #方式二: # <1> 先獲取要綁定的Publisher對象: pub_obj=Publisher(name='河大出版社',address='保定',city='保定', state_province='河北',country='China',website='http://www.hbu.com') OR pub_obj=Publisher.objects.get(id=1) # <2>將 publisher_id=2 改成 publisher=pub_obj #多對多(ManyToManyField()):
#多對多關係第三張表經過ManyToManyField()自動建立方式,綁定關係僅此一種 author1=Author.objects.get(id=1) author2=Author.objects.filter(name='alvin')[0] book=Book.objects.get(id=1) book.authors.add(author1,author2) #等同於: book.authors.add(*[author1,author2]) book.authors.remove(*[author1,author2]) #------------------- book=models.Book.objects.filter(id__gt=1) authors=models.Author.objects.filter(id=1)[0] authors.book_set.add(*book) authors.book_set.remove(*book) #------------------- book.authors.add(1) book.authors.remove(1) authors.book_set.add(1) authors.book_set.remove(1) #多對多關係第三張表手動建立方式: class Book2Author(models.Model): author=models.ForeignKey("Author") Book= models.ForeignKey("Book") # 那麼就還有一種方式: author_obj=models.Author.objects.filter(id=2)[0] book_obj =models.Book.objects.filter(id=3)[0] s=models.Book2Author.objects.create(author_id=1,Book_id=2) s.save() s=models.Book2Author(author=author_obj,Book_id=1) s.save()
>>> Book.objects.filter(id=1).delete()
有時咱們刪除一條信息,實際卻會刪除了多條,好比某本書由兩名做者共同創做完成,那麼刪除該本書,會連同對多關係表中和該本書相關的做者那行記錄刪除。
這種刪除方式就是django默認的級聯刪除。
例如,如下幾張表:
書表:
-------------------------------------------------------------------------------------------------
做者表:
-------------------------------------------------------------------------------------------------
出版社表:
-------------------------------------------------------------------------------------------------
書_做者表:
bookW=Book.objects.get(id=5) bookW.bookName ="我是一本書" bookW.save()
############################
Publisher.objects.filter(id=3).update(name="亞太出版社")
# 注意:不能用get(id=3),由於update是QuerySet對象的方法,filter返回的就是一個QuerySet對象,而get返回的是一個model對象
(filter裏面的條件可能有多個條件符合)
注意:模型的save()方法,這個方法會更新一行裏的全部列。 而某些狀況下,咱們只須要更新行裏的某幾列。
如上:
#--------------------save方法工做機制----------------------------------
###(0.003) SELECT `app01_book`.`id`, `app01_book`.`bookName`, `app01_book`.`price`,
`app01_book`.`publisher_id` FROM `app01_book` WHERE `app01_book`.`id` = 5; args=(5,)
###(0.059) UPDATE `app01_book` SET `bookName` = '我是一本書', `price` = '35.78',
`publisher_id` = 1 WHERE `app01_book`.`id` = 5; args=('我是一本書', '35.78', 1, 5)
即save方法會將全部屬性從新設定一遍,效率低
#-------------------update方法工做機制---------------------------------------
(0.042) UPDATE `app01_publisher` SET `name` = '亞太出版社' WHERE `app01_publisher`.`id` = 3; args=('亞太出版社', 3)
即update方法直接設定對應屬性
此外,update()方法對於任何結果集(QuerySet)均有效,這意味着你能夠同時更新多條記錄。
update()方法會返回一個整型數值,表示受影響的記錄條數。
# 查詢相關API: # <1>filter(**kwargs): 它包含了與所給篩選條件相匹配的對象 # <2>all(): 查詢全部結果 # <3>get(**kwargs): 返回與所給篩選條件相匹配的對象,返回結果有且只有一個,若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。
若是未找到會引起"模型類.DoesNotExist"異常;
若是多條被返回,會引起"模型類.MultipleObjectsReturned"異常。
#-----------下面的方法都是對查詢的結果再進行處理:好比 objects.filter.values()-------- # <4>values(*field): 返回一個ValueQuerySet——一個特殊的QuerySet,運行後獲得的並非一系列 model的實例化對象,而是一個可迭代的字典序列 # <5>exclude(**kwargs): 它包含了與所給篩選條件不匹配的對象 # <6>order_by(*field): 對查詢結果排序 # <7>reverse(): 對查詢結果反向排序 # <8>distinct(): 從返回結果中剔除重複紀錄 # <9>values_list(*field): 它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列 # <10>count(): 返回數據庫中匹配查詢(QuerySet)的對象數量。 # <11>first(): 返回第一條記錄 # <12>last(): 返回最後一條記錄 # <13>exists(): 若是QuerySet包含數據,就返回True,不然返回False
---------------了不得的雙下劃線(__)之單表條件查詢---------------- # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 獲取id大於1 且 小於10的值 # # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 獲取id等於十一、2二、33的數據 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # # models.Tb1.objects.filter(name__contains="ven") # 獲取name字段包含「ven」的值 # models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感 # # models.Tb1.objects.filter(id__range=[1, 2]) # 範圍bettwen and # # startswith,istartswith,
# endswith, iendswith,
所謂惰性機制:Publisher.objects.all()或者.filter()等都只是返回了一個QuerySet(查詢結果集對象),它並不會立刻執行sql,而是當調用QuerySet的時候才執行。
QuerySet特色:
<1> 可迭代的
<2> 可切片
#objs=models.Book.objects.all()#[obj1,obj2,ob3...] #QuerySet: 可迭代 # for obj in objs:#每一obj就是一個行對象 # print("obj:",obj) # QuerySet: 可切片 # print(objs[1]) # print(objs[1:4]) # print(objs[::-1])
QuerySet的高效使用:
<1>Django的queryset是惰性的 Django的queryset對應於數據庫的若干記錄(row),經過可選的查詢來過濾。例如,下面的代碼會得 到數據庫中名字爲‘Dave’的全部的人:person_set = Person.objects.filter(first_name="Dave") 上面的代碼並無運行任何的數據庫查詢。你可使用person_set,給它加上一些過濾條件,或者將它傳給某個函數, 這些操做都不會發送給數據庫。這是對的,由於數據庫查詢是顯著影響web應用性能的因素之一。 <2>要真正從數據庫得到數據,你能夠遍歷queryset或者使用if queryset,總之你用到數據時就會執行sql. 爲了驗證這些,須要在settings里加入 LOGGING(驗證方式) obj=models.Book.objects.filter(id=3) # for i in obj: # print(i) # if obj: # print("ok") <3>queryset是具備cache的 當你遍歷queryset時,全部匹配的記錄會從數據庫獲取,而後轉換成Django的model。這被稱爲執行 (evaluation).這些model會保存在queryset內置的cache中,這樣若是你再次遍歷這個queryset, 你不須要重複運行通用的查詢。 obj=models.Book.objects.filter(id=3) # for i in obj: # print(i) ## models.Book.objects.filter(id=3).update(title="GO") ## obj_new=models.Book.objects.filter(id=3) # for i in obj: # print(i) #LOGGING只會打印一次 <4> 簡單的使用if語句進行判斷也會徹底執行整個queryset而且把數據放入cache,雖然你並不須要這些 數據!爲了不這個,能夠用exists()方法來檢查是否有數據: obj = Book.objects.filter(id=4) # exists()的檢查能夠避免數據放入queryset的cache。 if obj.exists(): print("hello world!") <5>當queryset很是巨大時,cache會成爲問題 處理成千上萬的記錄時,將它們一次裝入內存是很浪費的。更糟糕的是,巨大的queryset可能會鎖住系統 進程,讓你的程序瀕臨崩潰。要避免在遍歷數據的同時產生queryset cache,可使用iterator()方法 來獲取數據,處理完數據就將其丟棄。 objs = Book.objects.all().iterator() # iterator()能夠一次只從數據庫獲取少許數據,這樣能夠節省內存 for obj in objs: print(obj.name) #BUT,再次遍歷沒有打印,由於迭代器已經在上一次遍歷(next)到最後一次了,沒得遍歷了 for obj in objs: print(obj.name) #固然,使用iterator()方法來防止生成cache,意味着遍歷同一個queryset時會重複執行查詢。因此使 #用iterator()的時候要小心,確保你的代碼在操做一個大的queryset時沒有重複執行查詢 總結: queryset的cache是用於減小程序對數據庫的查詢,在一般的使用下會保證只有在須要的時候纔會查詢數據庫。 使用exists()和iterator()方法能夠優化程序對內存的使用。不過,因爲它們並不會生成queryset cache,可能 會形成額外的數據庫查詢。
# --------------對象查詢----------------------------------
# 正向查找
book1=Book.objects.first()
print(book1.bookName) #python
print(book1.price) #100.25
print(book1.publisher) #人民出版社 南京(由於一對多的關係因此(book1.publisher是一個對象,而不是一個queryset集合) print(book1.publisher.name) #人民出版社
# 反向查找
pubL=Publisher.objects.last()
print(pubL.name) #亞太出版社
print(pubL.addr) #上海
#如何拿到與某一個出版社綁定的Book對象呢?
pub1=Publisher.objects.first()
print(pub1.book_set.all()) # <QuerySet [<Book: go>, <Book: 我是一本書>, <Book: 從你的全世界路過>]>
注意:條件查詢與對象查詢對應,是指在filter,values等方法中的經過__來明確查詢條件。
#----------------了不得的雙下劃線(__)之多表條件關聯查詢--------------- # 正向查找(條件) # ret3=models.Book.objects.filter(title='Python').values('id') # print(ret3)#[{'id': 1}] #正向查找(條件)之一對多 ret4=models.Book.objects.filter(title='Python').values('publisher__city') print(ret4) #[{'publisher__city': '北京'}] #正向查找(條件)之多對多 ret5=models.Book.objects.filter(title='Python').values('author__name') print(ret5) ret6=models.Book.objects.filter(author__name="alex").values('title') print(ret6) #注意 #正向查找的publisher__city或者author__name中的publisher,author是book表中綁定的字段 #一對多和多對多在這裏用法沒區別 # 反向查找(條件) #反向查找之一對多: ret8=models.Publisher.objects.filter(book__title='Python').values('name') print(ret8)#[{'name': '人大出版社'}] 注意,book__title中的book就是Publisher的關聯表名 ret9=models.Publisher.objects.filter(book__title='Python').values('book__authors') print(ret9)#[{'book__authors': 1}, {'book__authors': 2}] #反向查找之多對多: ret10=models.Author.objects.filter(book__title='Python').values('name') print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}] #注意 #正向查找的book__title中的book是表名Book #一對多和多對多在這裏用法沒區別
字段查詢
實現where子名,做爲方法filter()、exclude()、get()的參數 語法:屬性名稱__比較運算符=值 表示兩個下劃線,左側是屬性名稱,右側是比較類型 對於外鍵,使用「屬性名_id」表示外鍵的原始值
比較運算符 exact:表示判等,大小寫敏感;若是沒有寫「 比較運算符」,表示判等
contains:是否包含,大小寫敏感
startswith、endswith:以value開頭或結尾,大小寫敏感 isnull、isnotnull:是否爲null
在前面加個i表示不區分大小寫,如iexact、icontains、istarswith、iendswith
in:是否包含在範圍內
例如:
gt、gte、lt、lte:大於、大於等於、小於、小於等於 year、month、day、week_day、hour、minute、second:對日期間類型的屬性進行運算: filter(bpub_date__year=1980) filter(bpub_date__gt=date(1980, 12, 31)) 查詢的快捷方式:pk,pk表示primary key,默認的主鍵是id filter(pk__lt=6) filter(pk__in=[1, 2, 3, 4, 5])
示例:
求全部書籍的平均價格:
from django.db.models import Avg,Max,Min,
Book.objects.all().aggregate(Avgprice=Avg("price"))
鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。若是你想要爲聚合值指定
一個名稱,能夠向聚合子句提供它:
>>> Book.objects.all().aggregate(average_price=Avg('price'))
若是你也想知道全部圖書價格的最大值和最小值,能夠這樣查詢:
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
求全部書籍的最高價格
Book.objects.all().aggregate(Maxprice=Max("price"))
求老男孩出版過得書中的最高價
Book.objeacts.filter(authorName="老男孩").aggregate(Maxprice=Max("price"))
求每個做者出版過得書中的最低價
Book.objects.values("authorName").annotate(Minprice=Min("price"))
查詢alex出的書總價格
Book.objeacts.filter(authorName="alex").aggregate(Sumprice=Sum("price"))
count的通常用法:
count = list.count()
示例:將全部書籍的價格增加20元
Book.objects.all().update(price=F("price")+20) from django.db.models import F
示例:
查詢書籍名字或者以老開頭,或者價格大於200的書籍
from django.db.models import Q
寫法一:Book.objects.filter(bookName__startswith="老",price__gt=200) 寫法二:Book.objects.filter(Q(bookName__startswith="老") | Q(price__gt=200))
查詢書籍名字或者以老開頭,或者價格大於200,或者編號爲5的書籍
Book.objects.filter(Q(price__gt=100) | Q(id=5),bookName__startswith="老")
查詢編號不小於6的書籍
Book.objects.filter(~Q(id__lt=6))
filter(鍵1=值1,鍵2=值2) 等價於 filter(鍵1=值1).filter(鍵2=值2)
admin是django強大功能之一,它能共從數據庫中讀取數據,呈如今頁面中,進行管理。
默認狀況下,它的功能已經很是強大,若是你不須要複雜的功能,它已經夠用,可是有時候,一些特殊的功能還須要定製,好比搜索功能,下面就逐步深刻介紹如何定製適合本身的admin應用。
python manage.py createsuperuser,按提示輸入用戶名、郵箱、密碼
LANGUAGE_CODE = 'zh-Hans' TIME_ZONE = 'Asia/Shanghai'
方式一:使用register的方法
打開booktest/admin.py文件,註冊模型
from django.contrib import admin from models import BookInfo admin.site.register(BookInfo)
方式二:使用register的裝飾器
@admin.register(Book)
class QuestionAdmin(admin.ModelAdmin): ... admin.site.register(Question, QuestionAdmin)
list_display = ['pk', 'btitle', 'bpub_date']
list_filter = ['btitle']
search_fields = ['btitle']
list_per_page = 10
ordering: 指定排序字段
from django.contrib import admin from app01.models import * # Register your models here. # @admin.register(Book)#----->單給某個表加一個定製 class MyAdmin(admin.ModelAdmin): list_display = ("title","price","publisher") search_fields = ("title","publisher") list_filter = ("publisher",) ordering = ("price",) fieldsets =[ (None, {'fields': ['title']}), ('price information', {'fields': ['price',"publisher"], 'classes': ['collapse']}), ] admin.site.register(Book,MyAdmin) admin.site.register(Publish) admin.site.register(Author)
fields = ['bpub_date', 'btitle']
fieldsets = [ ('basic',{'fields': ['btitle']}), ('more', {'fields': ['bpub_date']}), ]
def gender(self): if self.hgender: return '男' else: return '女' gender.short_description = '性別'
class HeroInfoAdmin(admin.ModelAdmin): list_display = ['id', 'hname', 'gender', 'hcontent']