Django学习~1
一、什么是web框架?
框架,即framework,特指為解決一個開放性問題而設計的具有一定約束性的支撐結構,使用框架可以幫你快速開發特定的系統,簡單地說,就是你用別人搭建好的舞臺來做表演。
對于所有的Web應用,本質上其實就是一個socket服務端,用戶的瀏覽器其實就是一個socket客戶端。
import socketdef handle_request(client):buf = client.recv(1024)client.send("HTTP/1.1 200 OK\r\n\r\n".encode("utf8"))client.send("<h1 style='color:red'>Hello, yuan</h1>".encode("utf8"))def main():sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.bind(('localhost',8001))sock.listen(5)while True:connection, address = sock.accept()handle_request(connection)connection.close()if __name__ == '__main__':main()最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。
如果要動態生成HTML,就需要把上述步驟自己來實現。
不過,接受HTTP請求、解析HTTP請求、發送HTTP響應都是苦力活,如果我們自己來寫這些底層代碼,還沒開始寫動態HTML呢,就得花個把月去讀HTTP規范。
正確的做法是底層代碼由專門的服務器軟件實現,我們用Python專注于生成HTML文檔。
因為我們不希望接觸到TCP連接、HTTP原始請求和響應格式,所以,需要一個統一的接口,讓我們專心用Python編寫Web業務。
這個接口就是WSGI:Web Server Gateway Interface。
------------------Do a web framework ourselves-----------------
step 1:
from wsgiref.simple_server import make_serverdef application(environ, start_response):start_response('200 OK', [('Content-Type', 'text/html')])return [b'<h1>Hello, web!</h1>']httpd = make_server('', 8080, application)print('Serving HTTP on port 8000...') # 開始監聽HTTP請求: httpd.serve_forever()注意:
整個application()函數本身沒有涉及到任何解析HTTP的部分,也就是說,底層代碼不需要我們自己編寫,我們只負責在更高層次上考慮如何響應請求就可以了。
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。
step 2:
print(environ['PATH_INFO'])path=environ['PATH_INFO']start_response('200 OK', [('Content-Type', 'text/html')])f1=open("index1.html","rb")data1=f1.read()f2=open("index2.html","rb")data2=f2.read()if path=="/yuan":return [data1]elif path=="/alex":return [data2]else:return ["<h1>404</h1>".encode('utf8')]step3:
from wsgiref.simple_server import make_serverdef f1():f1=open("index1.html","rb")data1=f1.read()return [data1]def f2():f2=open("index2.html","rb")data2=f2.read()return [data2]def application(environ, start_response):print(environ['PATH_INFO'])path=environ['PATH_INFO']start_response('200 OK', [('Content-Type', 'text/html')])if path=="/yuan":return f1()elif path=="/alex":return f2()else:return ["<h1>404</h1>".encode("utf8")]httpd = make_server('', 8502, application)print('Serving HTTP on port 8084...')# 開始監聽HTTP請求: httpd.serve_forever()step4:
from wsgiref.simple_server import make_serverdef f1(req):print(req)print(req["QUERY_STRING"])f1=open("index1.html","rb")data1=f1.read()return [data1]def f2(req):f2=open("index2.html","rb")data2=f2.read()return [data2]import timedef f3(req): #模版以及數據庫f3=open("index3.html","rb")data3=f3.read()times=time.strftime("%Y-%m-%d %X", time.localtime())data3=str(data3,"utf8").replace("!time!",str(times))return [data3.encode("utf8")]def routers():urlpatterns = (('/yuan',f1),('/alex',f2),("/cur_time",f3))return urlpatternsdef application(environ, start_response):print(environ['PATH_INFO'])path=environ['PATH_INFO']start_response('200 OK', [('Content-Type', 'text/html')])urlpatterns = routers()func = Nonefor item in urlpatterns:if item[0] == path:func = item[1]breakif func:return func(environ)else:return ["<h1>404</h1>".encode("utf8")]httpd = make_server('', 8518, application)print('Serving HTTP on port 8084...')# 開始監聽HTTP請求:httpd.serve_forever()伙計們,不知不覺我們自己已經寫出一個web框架啦!
二、MVC和MTV模式
著名的MVC模式:所謂MVC就是把web應用分為模型(M),控制器?,視圖(V)三層;他們之間以一種插件似的,松耦合的方式連接在一起。
模型負責業務對象與數據庫的對象(ORM),視圖負責與用戶的交互(頁面),控制器?接受用戶的輸入調用模型和視圖完成用戶的請求。
Django的MTV模式本質上與MVC模式沒有什么差別,也是各組件之間為了保持松耦合關系,只是定義上有些許不同,Django的MTV分別代表:
Model(模型):負責業務對象與數據庫的對象(ORM)
Template(模版):負責如何把頁面展示給用戶
View(視圖):負責業務邏輯,并在適當的時候調用Model和Template
此外,Django還有一個url分發器,它的作用是將一個個URL的頁面請求分發給不同的view處理,view再調用相應的Model和Template。
三、django的流程和命令行工具
django實現流程
django#安裝: pip3 install django添加環境變量#1 創建projectdjango-admin startproject mysite---mysite---settings.py---url.py---wsgi.py---- manage.py(啟動文件) #2 創建APP python manage.py startapp app01#3 settings配置TEMPLATESSTATICFILES_DIRS=(os.path.join(BASE_DIR,"statics"),)STATIC_URL = '/static/' # 我們只能用 STATIC_URL,但STATIC_URL會按著你的STATICFILES_DIRS去找#4 根據需求設計代碼url.pyview.py#5 使用模版render(req,"index.html") #6 啟動項目python manage.py runserver 127.0.0.1:8090#7 連接數據庫,操作數據model.pydjango的命令行工具
django-admin.py 是Django的一個用于管理任務的命令行工具,manage.py是對django-admin.py的簡單包裝,每一個Django Project里都會有一個mannage.py。
<1> 創建一個django工程 : django-admin.py startproject mysite
當前目錄下會生成mysite的工程,目錄結構如下:
manage.py ----- Django項目里面的工具,通過它可以調用django shell和數據庫等。
settings.py ---- 包含了項目的默認設置,包括數據庫信息,調試標志以及其他一些工作的變量。
urls.py ----- 負責把URL模式映射到應用程序。
<2>在mysite目錄下創建blog應用: python manage.py startapp blog
<3>啟動django項目:python manage.py runserver 8080
這樣我們的django就啟動起來了!當我們訪問:http://127.0.0.1:8080/ 時就可以看到:
<4>生成同步數據庫的腳本:
同步數據庫:
python manage.py migrate注意:在開發過程中,數據庫同步誤操作之后,難免會遇到后面不能同步成功的情況,解決這個問題的一個簡單粗暴方法是把migrations目錄下的腳本(除__init__.py之外)全部刪掉,再把數據庫刪掉之后創建一個新的數據庫,數據庫同步操作再重新做一遍。
<5>當我們訪問http://127.0.0.1:8080/admin/時,會出現:
所以我們需要為進入這個項目的后臺創建超級管理員:
設置好用戶名和密碼后便可登錄啦!
<6>清空數據庫:
python manage.py flush<7>查詢某個命令的詳細信息:
django-admin.py help startappadmin 是Django 自帶的一個后臺數據庫管理系統。
<8>啟動交互界面 :
python manage.py shell這個命令和直接運行 python 進入 shell 的區別是:你可以在這個 shell 里面調用當前項目的 models.py 中的 API,對于操作數據,還有一些小測試非常方便。
<9> 終端上輸入
python manage.py可以看到詳細的列表,在忘記子名稱的時候特別有用。
實例練習1-提交數據并展示
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>創建個人信息</h1><form action="/userInfor/" 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><hr><h1>信息展示</h1><table border="1"><tr><td>姓名</td><td>性別</td><td>郵箱</td></tr>{% for i in info_list %}<tr><td>{{ i.username }}</td><td>{{ i.sex }}</td><td>{{ i.email }}</td></tr>{% endfor %}</table></body> </html> -----------------------url.py--------------------------------------- url(r'^userInfor/', views.userInfor)-----------------------views.py--------------------------------------info_list=[]def userInfor(req):if req.method=="POST":username=req.POST.get("username",None)sex=req.POST.get("sex",None)email=req.POST.get("email",None)info={"username":username,"sex":sex,"email":email}info_list.append(info)return render(req,"userInfor.html",{"info_list":info_list})實例練習2-提交數據并展示(數據庫)
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>創建個人信息</h1><form action="/userInfor/" 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><hr><h1>信息展示</h1><table border="1"><tr><td>姓名</td><td>性別</td><td>郵箱</td></tr>{% for i in info_list %}<tr><td>{{ i.username }}</td><td>{{ i.sex }}</td><td>{{ i.email }}</td></tr>{% endfor %}</table></body> </html> ----------------------------------------------models.py from django.db import models# Create your models here.class UserInfor(models.Model):username=models.CharField(max_length=64)sex=models.CharField(max_length=64)email=models.CharField(max_length=64)----------------------------------------------views.pyfrom django.shortcuts import renderfrom app01 import models # Create your views here.def userInfor(req):if req.method=="POST":u=req.POST.get("username",None)s=req.POST.get("sex",None)e=req.POST.get("email",None)#---------表中插入數據方式一# info={"username":u,"sex":e,"email":e}# models.UserInfor.objects.create(**info)#---------表中插入數據方式二models.UserInfor.objects.create(username=u,sex=s,email=e)info_list=models.UserInfor.objects.all()return render(req,"userInfor.html",{"info_list":info_list})return render(req,"userInfor.html")四、Django的配置文件(settings)
靜態文件設置:
一、概述:
靜態文件交由Web服務器處理,Django本身不處理靜態文件。
簡單的處理邏輯如下(以nginx為例):
URI請求-----> 按照Web服務器里面的配置規則先處理,以nginx為例,主要求配置在nginx.
#conf里的location
|---------->如果是靜態文件,則由nginx直接處理
|---------->如果不是則交由Django處理,Django根據urls.py里面的規則進行匹配
以上是部署到Web服務器后的處理方式,為了便于開發,Django提供了在開發環境的對靜態文件的處理機制,方法是這樣:
1、在INSTALLED_APPS里面加入:
django.contrib.staticfiles2、在urls.py里面加入
if settings.DEBUG: urlpatterns += patterns('', url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT }), url(r'^static/(?P<path>.*)$', 'django.views.static.serve',{'document_root':settings.STATIC_ROOT}), )3、這樣就可以在開發階段直接使用靜態文件了。
二、MEDIA_ROOT和MEDIA_URL
而靜態文件的處理又包括STATIC和MEDIA兩類,這往往容易混淆,在Django里面是這樣定義的:
MEDIA:指用戶上傳的文件,比如在Model里面的FileFIeld,ImageField上傳的文件。
如果你定義MEDIA_ROOT=c:\temp\media,那么File=models.FileField(upload_to=“abc/”)#,上傳的文件就會被保存到c:\temp\media\abc
#eg:
class blog(models.Model):
Title=models.charField(max_length=64)
Photo=models.ImageField(upload_to=“photo”)
上傳的圖片就上傳到c:\temp\media\photo,而在模板中要顯示該文件,則在這樣寫:
在settings里面設置的MEDIA_ROOT必須是本地路徑的絕對路徑,一般是這樣寫:
BASE_DIR= os.path.abspath(os.path.dirname(file))
MEDIA_ROOT=os.path.join(BASE_DIR,‘media/’).replace(’\’,’/’)
#MEDIA_URL是指從瀏覽器訪問時的地址前綴,舉個例子:
MEDIA_ROOT=c:\temp\media\photo
MEDIA_URL="/data/"
#在開發階段,media的處理由django處理:
訪問http://localhost/data/abc/a.png就是訪問c:\temp\media\photo\abc\a.png
在模板里面這樣寫
在部署階段最大的不同在于你必須讓web服務器來處理media文件,因此你必須在web服務器中配置,以便能讓web服務器能訪問media文件
以nginx為例,可以在nginx.conf里面這樣:
location ~/media/{
root/temp/
break;
}
具體可以參考如何在nginx部署django的資料。
三、STATIC_ROOT和STATIC_URL、
STATIC主要指的是如css,js,images這樣文件,在settings里面可以配置STATIC_ROOT和STATIC_URL,配置方式與MEDIA_ROOT是一樣的,但是要注意
STATIC文件一般保存在以下位置:
1、STATIC_ROOT:在settings里面設置,一般用來放一些公共的js,css,images等。
2、app的static文件夾,在每個app所在文夾均可以建立一個static文件夾,然后當運行collectstatic時,
Django會遍歷INSTALL_APPS里面所有app的static文件夾,將里面所有的文件復制到STATIC_ROOT。
因此,如果你要建立可復用的app,那么你要將該app所需要的靜態文件放在static文件夾中。
也就是說一個項目引用了很多app,那么這個項目所需要的css,images等靜態文件是分散在各個app的static文件的,比較典型的是admin應用。
當你要發布時,需要將這些分散的static文件收集到一個地方就是STATIC_ROOT。
3、STATIC文件還可以配置STATICFILES_DIRS,指定額外的靜態文件存儲位置。
STATIC_URL的含義與MEDIA_URL類似。
注意1: #為了后端的更改不會影響前端的引入,避免造成前端大量修改 STATIC_URL = '/static/' #引用名 STATICFILES_DIRS = ( os.path.join(BASE_DIR,"statics") #實際名 ,即實際文件夾的名字 )
django對引用名和實際名進行映射,引用時,只能按照引用名來,不能按實際名去找
#<script src="/statics/jquery-3.1.1.js"></script> #------error-----不能直接用,必須用STATIC_URL = '/static/': #<script src="/static/jquery-3.1.1.js"></script>#注意2(statics文件夾寫在不同的app下,靜態文件的調用):
STATIC_URL = '/static/'STATICFILES_DIRS=( ('hello',os.path.join(BASE_DIR,"app01","statics")) , ) #<script src="/static/hello/jquery-1.8.2.min.js"></script>#注意3:
STATIC_URL = '/static/' {% load staticfiles %} <script src={% static "jquery-1.8.2.min.js" %}></script>其它重要參數設置:
APPEND_SLASH
Default: True
When set to True, if the request URL does not match any of the patterns in the URLconf and it
doesn’t end in a slash, an HTTP redirect is issued to the same URL with a slash appended. Note
that the redirect may cause any data submitted in a POST request to be lost.
五、Django URL (路由系統)
URL配置(URLconf)就像Django 所支撐網站的目錄。
它的本質是URL模式以及要為該URL模式調用的視圖函數之間的映射表;你就是以這種方式告訴Django,對于這個URL調用這段代碼,對于那個URL調用那段代碼。
urlpatterns = [url(正則表達式, views視圖函數,參數,別名), ]參數說明:
一個正則表達式字符串
一個可調用對象,通常為一個視圖函數或一個指定視圖函數路徑的字符串
可選的要傳遞給視圖函數的默認參數(字典形式)
一個可選的name參數
5.1 Here’s a sample URLconf:
from django.conf.urls import url from django.contrib import adminfrom app01 import viewsurlpatterns = [url(r'^articles/2003/$', views.special_case_2003),#url(r'^articles/[0-9]{4}/$', views.year_archive),url(r'^articles/([0-9]{4})/$', views.year_archive), #no_named groupurl(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),]Note:
#1 There’s no need to add a leading slash, because every URL has that. For # example, it’s ^articles, not ^/articles.#2 A request to /articles/2005/03/ would match the third entry in the list. # Django would call the function views.month_archive(request, '2005', '03').#3 /articles/2005/3/ would not match any URL patterns#4 /articles/2003/ would match the first pattern in the list, not the second one#5 /articles/2003/03/03/ would match the final pattern. Django would call the # functionviews.article_detail(request, '2003', '03', '03').5.2 Named groups
The above example used simple, non-named regular-expression groups (via parenthesis) to capture bits of the URL and pass them as positional arguments to a view. In more advanced usage, it’s possible to use named regular-expression groups to capture URL bits and pass them as keyword arguments to a view.
In Python regular expressions, the syntax for named regular-expression groups is
(?P< name >pattern), where name is the name of the group and pattern is some pattern to match.
Here’s the above example URLconf, rewritten to use named groups:
import reret=re.search('(?P<id>\d{3})/(?P<name>\w{3})','weeew34ttt123/ooo')print(ret.group()) print(ret.group('id')) print(ret.group('name')) from django.conf.urls import urlfrom . import viewsurlpatterns = [url(r'^articles/2003/$', views.special_case_2003),url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail), ]This accomplishes exactly the same thing as the previous example, with one subtle difference: The captured values are passed to view functions as keyword arguments rather than positional arguments.
5.3 Passing extra options to view functions
URLconfs have a hook that lets you pass extra arguments to your view functions, as a Python dictionary.
The django.conf.urls.url() function can take an optional third argument which should be a dictionary of extra keyword arguments to pass to the view function.
For example:
from django.conf.urls import url from . import viewsurlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), ]In this example, for a request to /blog/2005/, Django will call views.year_archive(request, year=‘2005’,foo=‘bar’).
This technique is used in the syndication framework to pass metadata and options to views.
Dealing with conflicts
It’s possible to have a URL pattern which captures named keyword arguments, and also passes arguments with the same names in its dictionary of extra arguments. When this happens, the arguments in the dictionary will be used instead of the arguments captured in the URL.
5.4 name param
urlpatterns = [url(r'^index',views.index,name='bieming'),url(r'^admin/', admin.site.urls),# 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),] ###################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 'bieming' %}" method="post">用戶名:<input type="text" name="username">密碼:<input type="password" name="password"><input type="submit" value="submit"></form> </body> </html>#######################5.5 Including other URLconfs
#At any point, your urlpatterns can “include” other URLconf modules. This #essentially “roots” a set of URLs below other ones.#For example, here’s an excerpt of the URLconf for the Django website itself. #It includes a number of other URLconfs:from django.conf.urls import include, urlurlpatterns = [url(r'^admin/', admin.site.urls),url(r'^blog/', include('blog.urls')), ]六、Django Views(視圖函數)
http請求中產生兩個核心對象:
http請求:HttpRequest對象
http響應:HttpResponse對象
所在位置:django.http
之前我們用到的參數request就是HttpRequest 檢測方法:isinstance(request,HttpRequest)
1 HttpRequest對象的屬性和方法:
path: 請求頁面的全路徑,不包括域名
method: 請求中使用的HTTP方法的字符串表示。全大寫表示。例如
if req.method=="GET":do_something()elseif req.method=="POST":do_something_else()GET: 包含所有HTTP GET參數的類字典對象
POST: 包含所有HTTP POST參數的類字典對象
服務器收到空的POST請求的情況也是可能發生的,也就是說,表單form通過HTTP POST方法提交請求,但是表單中可能沒有數據,因此不能使用if req.POST來判斷是否使用了HTTP POST 方法;應該使用 if req.method==“POST”
COOKIES: 包含所有cookies的標準Python字典對象;keys和values都是字符串。
FILES: 包含所有上傳文件的類字典對象;FILES中的每一個Key都是
< input type=“file” name="" />標簽中,name屬性的值,FILES中的每一個value同時也是一個標準的python字典對象,包含下面三個Keys:
filename: 上傳文件名,用字符串表示
content_type: 上傳文件的Content Type
content: 上傳文件的原始內容
user: 是一個django.contrib.auth.models.User對象,代表當前登陸的用戶。
如果訪問用戶當前沒有登陸,user將被初始化為django.contrib.auth.models.AnonymousUser的實例。
你可以通過user的is_authenticated()方法來辨別用戶是否登陸:
if req.user.is_authenticated();
只有激活Django中的AuthenticationMiddleware時該屬性才可用
session: 唯一可讀寫的屬性,代表當前會話的字典對象;自己有激活Django中的session支持時該屬性才可用。
方法
get_full_path(), 比如:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()得到的結果就是/index33/?name=123
req.path:/index33
注意一個常用方法:request.POST.getlist(’’)
2 HttpResponse對象:
對于HttpRequest對象來說,是由django自動創建的,但是,HttpResponse對象就必須我們自己創建。
每個view請求處理方法必須返回一個HttpResponse對象。
HttpResponse類在django.http.HttpResponse
在HttpResponse對象上擴展的常用方法:
頁面渲染: render()(推薦)< br > render_to_response(),
頁面跳轉: redirect(“路徑”)
locals(): 可以直接將函數中所有的變量傳給模板
-----------------------------------url.pyurl(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 if render的頁面需要模板語言渲染,需要的將數據庫的數據加載到html,那么所有的這一部分 # 除了寫在yuan_back的視圖函數中,必須還要寫在login中,代碼重復,沒有解耦.# 2 the most important: url沒有跳轉到/yuan_back/,而是還在/login/,所以當刷新后 # 又得重新登錄.七、Template基礎
模板系統的介紹
你可能已經注意到我們在例子視圖中返回文本的方式有點特別。 也
就是說,HTML被直接硬編碼在 Python代碼之中。
def current_datetime(request):now = datetime.datetime.now()html = "<html><body>It is now %s.</body></html>" % nowreturn HttpResponse(html)盡管這種技術便于解釋視圖是如何工作的,但直接將HTML硬編碼到你的視圖里卻并不是一個好主意。
讓我們來看一下為什么:
對頁面設計進行的任何改變都必須對 Python 代碼進行相應的修改。
站點設計的修改往往比底層 Python 代碼的修改要頻繁得多,因此如果可以在不進行 Python 代碼修改的情況下變更設計,那將會方便得多。
Python 代碼編寫和 HTML 設計是兩項不同的工作,大多數專業的網站開發環境都將他們分配給不同的人員(甚至不同部門)來完成。
設計者和HTML/CSS的編碼人員不應該被要求去編輯Python的代碼來完成他們的工作。
程序員編寫 Python代碼和設計人員制作模板兩項工作同時進行的效率是最高的,遠勝于讓一個人等待另一個人完成對某個既包含 Python又包含 HTML 的文件的編輯工作。
基于這些原因,將頁面的設計和Python的代碼分離開會更干凈簡潔更容易維護。
我們可以使用 Django的 模板系統 (Template System)來實現這種模式,這就是本章要具體討論的問題。
----------------------------------模板語法----------------------------------
一、模版的組成
組成:HTML代碼+邏輯控制代碼
二、邏輯控制代碼的組成
1 變量(使用雙大括號來引用變量):
語法格式: {{var_name}}
------Template和Context對象
>>> python manange.py shell (進入該django項目的環境) >>> from django.template import Context, Template >>> t = Template('My name is {{ name }}.') >>> c = Context({'name': 'Stephane'}) >>> t.render(c) 'My name is Stephane.'# 同一模板,多個上下文,一旦有了模板對象,你就可以通過它渲染多個context,無論何時我們都可以 # 像這樣使用同一模板源渲染多個context,只進行 一次模板創建然后多次調用render()方法渲染會 # 更為高效: # Low for name in ('John', 'Julie', 'Pat'):t = Template('Hello, {{ name }}')print t.render(Context({'name': name}))# Good t = Template('Hello, {{ name }}') for name in ('John', 'Julie', 'Pat'):print t.render(Context({'name': name}))Django 模板解析非常快捷。
大部分的解析工作都是在后臺通過對簡短正則表達式一次性調用來完成。
這和基于 XML 的模板引擎形成鮮明對比,那些引擎承擔了 XML 解析器的開銷,且往往比 Django 模板渲染引擎要慢上幾個數量級。
from django.shortcuts import render,HttpResponse from django.template.loader import get_template #記得導入 # Create your views here.import datetime from django.template import Template,Context# def current_time(req):#原始的視圖函數# now=datetime.datetime.now()# html="<html><body>現在時刻:<h1>%s.</h1></body></html>" %now# return HttpResponse(html)# def current_time(req):#django模板修改的視圖函數 # now=datetime.datetime.now() # t=Template('<html><body>現在時刻是:<h1 style="color:red">{{current_date}}</h1></body></html>')#t=get_template('current_datetime.html') # c=Context({'current_date':now}) # html=t.render(c) # return HttpResponse(html)#另一種寫法(推薦)def current_time(req):now=datetime.datetime.now()return render(req, 'current_datetime.html', {'current_date':now})------深度變量的查找(萬能的句點號)
在到目前為止的例子中,我們通過 context 傳遞的簡單參數值主要是字符串,然而,模板系統能夠非常簡潔地處理更加復雜的數據結構,例如list、dictionary和自定義的對象。
在 Django 模板中遍歷復雜數據結構的關鍵是句點字符 (.)。
#最好是用幾個例子來說明一下。 # 首先,句點可用于訪問列表索引,例如:>>> from django.template import Template, Context >>> t = Template('Item 2 is {{ items.2 }}.') >>> c = Context({'items': ['apples', 'bananas', 'carrots']}) >>> t.render(c) 'Item 2 is carrots.'#假設你要向模板傳遞一個 Python 字典。 要通過字典鍵訪問該字典的值,可使用一個句點: >>> from django.template import Template, Context >>> person = {'name': 'Sally', 'age': '43'} >>> t = Template('{{ person.name }} is {{ person.age }} years old.') >>> c = Context({'person': person}) >>> t.render(c) 'Sally is 43 years old.'#同樣,也可以通過句點來訪問對象的屬性。 比方說, Python 的 datetime.date 對象有 #year 、 month 和 day 幾個屬性,你同樣可以在模板中使用句點來訪問這些屬性:>>> from django.template import Template, Context >>> import datetime >>> d = datetime.date(1993, 5, 2) >>> d.year >>> d.month >>> d.day >>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.') >>> c = Context({'date': d}) >>> t.render(c) 'The month is 5 and the year is 1993.'# 這個例子使用了一個自定義的類,演示了通過實例變量加一點(dots)來訪問它的屬性,這個方法適 # 用于任意的對象。 >>> from django.template import Template, Context >>> class Person(object): ... def __init__(self, first_name, last_name): ... self.first_name, self.last_name = first_name, last_name >>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.') >>> c = Context({'person': Person('John', 'Smith')}) >>> t.render(c) 'Hello, John Smith.'# 點語法也可以用來引用對象的方法。 例如,每個 Python 字符串都有 upper() 和 isdigit() # 方法,你在模板中可以使用同樣的句點語法來調用它們: >>> from django.template import Template, Context >>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}') >>> t.render(Context({'var': 'hello'})) 'hello -- HELLO -- False' >>> t.render(Context({'var': '123'})) '123 -- 123 -- True'# 注意這里調用方法時并* 沒有* 使用圓括號 而且也無法給該方法傳遞參數;你只能調用不需參數的 # 方法。------變量的過濾器(filter)的使用
語法格式: {{obj|filter:param}}
# 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'2 標簽(tag)的使用(使用大括號和百分比的組合來表示使用tag)
{% tags %}
------{% if %} 的使用
{% 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 %}標簽允許你按順序遍歷一個序列中的各個元素,每次循環模板系統都會渲染{% for %}和{% endfor %}之間的所有內容。
<ul> {% for obj in list %}<li>{{ obj.name }}</li> {% endfor %} </ul>#在標簽里添加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%}:csrf_token標簽
用于生成csrf_token的標簽,用于防治跨站攻擊驗證。
注意如果你在view的index里用的是render_to_response方法,不會生效。
其實,這里是會生成一個input標簽,和其他表單標簽一起提交給后臺的。
------{% url %}: 引用路由配置的地址
<form action="{% url "bieming"%}" ><input type="text"><input type="submit"value="提交">{%csrf_token%} </form>------{% with %}:用更簡單的變量名替代復雜的變量名
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
------{% verbatim %}: 禁止render
{% verbatim %}
{{ hello }}
{% endverbatim %}
------{% load %}: 加載標簽庫
3 自定義filter和simple_tag
------a、在app中創建templatetags模塊(必須的)
------b、創建任意 .py 文件,如:my_tags.py
from django import template from django.utils.safestring import mark_saferegister = 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 %}4 extend模板繼承
------include 模板標簽
在講解了模板加載機制之后,我們再介紹一個利用該機制的內建模板標簽: {% include %} 。
該標簽允許在(模板中)包含其它的模板的內容。
標簽的參數是所要包含的模板名稱,可以是一個變量,也可以是用單/雙引號硬編碼的字符串。
每當在多個模板中出現相同的代碼時,就應該考慮是否要使用 {% include %} 來減少重復。
------extend(繼承)模板標簽
到目前為止,我們的模板范例都只是些零星的 HTML 片段,但在實際應用中,你將用 Django 模板系統來創建整個 HTML 頁面。
這就帶來一個常見的 Web 開發問題: 在整個網站中,如何減少共用頁面區域(比如站點導航)所引起的重復和冗余代碼?
解決該問題的傳統做法是使用服務器端的 includes ,你可以在 HTML 頁面中使用該指令將一個網頁嵌入到另一個中。
事實上, Django 通過剛才講述的 {% include %} 支持了這種方法。
但是用 Django 解決此類問題的首選方法是使用更加優雅的策略—— 模板繼承 。
本質上來說,模板繼承就是先構造一個基礎框架模板,而后在其子模板中對它所包含站點公用部分和定義塊進行重載。
讓我們通過修改 current_datetime.html 文件,為 current_datetime 創建一個更加完整的模板來體會一下這種做法:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head><title>The current time</title> </head> <body><h1>My helpful timestamp site</h1><p>It is now {{ current_date }}.</p><hr><p>Thanks for visiting my site.</p> </body> </html>這看起來很棒,但如果我們要為 hours_ahead 視圖創建另一個模板會發生什么事情呢?
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head><title>Future time</title> </head> <body><h1>My helpful timestamp site</h1><p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p><hr><p>Thanks for visiting my site.</p> </body> </html>很明顯,我們剛才重復了大量的 HTML 代碼。
想象一下,如果有一個更典型的網站,它有導航條、樣式表,可能還有一些 JavaScript 代碼,事情必將以向每個模板填充各種冗余的 HTML 而告終。
解決這個問題的服務器端 include 方案是找出兩個模板中的共同部分,將其保存為不同的模板片段,然后在每個模板中進行 include。
也許你會把模板頭部的一些代碼保存為 header.html 文件:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head>你可能會把底部保存到文件 footer.html :
<hr><p>Thanks for visiting my site.</p> </body> </html>對基于 include 的策略,頭部和底部的包含很簡單。
麻煩的是中間部分。
在此范例中,每個頁面都有一個< h1 >My helpful timestamp site</ h1 > 標題,但是這個標題不能放在 header.html 中,因為每個頁面的 < title > 是不同的。
如果我們將 < h1 > 包含在頭部,我們就不得不包含 < title > ,但這樣又不允許在每個頁面對它進行定制。
何去何從呢?
Django 的模板繼承系統解決了這些問題。
你可以將其視為服務器端 include 的逆向思維版本。
你可以對那些不同的代碼段進行定義,而不是共同代碼段。
第一步是定義基礎模板,該框架之后將由子模板所繼承。
以下是我們目前所講述范例的基礎模板:
<!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>這個叫做 base.html 的模板定義了一個簡單的 HTML 框架文檔,我們將在本站點的所有頁面中使用。
子模板的作用就是重載、添加或保留那些塊的內容。
(如果你一直按順序學習到這里,保存這個文件到你的template目錄下,命名為 base.html .)
我們使用模板標簽: {% block %} 。
所有的 {% block %} 標簽告訴模板引擎,子模板可以重載這些部分。
每個{% block %}標簽所要做的是告訴模板引擎,該模板下的這一塊內容將有可能被子模板覆蓋。
現在我們已經有了一個基本模板,我們可以修改 current_datetime.html 模板來 使用它:
{% extends "base.html" %}{% block title %}The current time{% endblock %}{% block content %} <p>It is now {{ current_date }}.</p> {% endblock %}再為 hours_ahead 視圖創建一個模板,看起來是這樣的:
{% extends "base.html" %}{% block title %}Future time{% endblock %}{% block content %} <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> {% endblock %}看起來很漂亮是不是?
每個模板只包含對自己而言 獨一無二 的代碼。
無需多余的部分。
如果想進行站點級的設計修改,僅需修改 base.html ,所有其它模板會立即反映出所作修改。
以下是其工作方式:
在加載 current_datetime.html 模板時,模板引擎發現了 {% extends %} 標簽, 注意到該模板是一個子模板。
模板引擎立即裝載其父模板,即本例中的 base.html 。
此時,模板引擎注意到 base.html 中的三個 {% block %} 標簽,并用子模板的內容替換這些 block 。
因此,引擎將會使用我們在 { block title %} 中定義的標題,對 {% block content %} 也是如此。 所以,網頁標題一塊將由{% block title %}替換,同樣地,網頁的內容一塊將由 {% block content %}替換。
注意由于子模板并沒有定義 footer 塊,模板系統將使用在父模板中定義的值。
父模板 {% block %} 標簽中的內容總是被當作一條退路。
繼承并不會影響到模板的上下文。
換句話說,任何處在繼承樹上的模板都可以訪問到你傳到模板中的每一個模板變量。
你可以根據需要使用任意多的繼承次數。
使用繼承的一種常見方式是下面的三層法:
<1> 創建 base.html 模板,在其中定義站點的主要外觀感受。 這些都是不常修改甚至從不修改的部分。
<2> 為網站的每個區域創建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。這些模板對base.html 進行拓展,并包含區域特定的風格與設計。
<3> 為每種類型的頁面創建獨立的模板,例如論壇頁面或者圖片庫。 這些模板拓展相應的區域模板。
這個方法可最大限度地重用代碼,并使得向公共區域(如區域級的導航)添加內容成為一件輕松的工作。
以下是使用模板繼承的一些訣竅:
<1>如果在模板中使用 {% extends %} ,必須保證其為模板中的第一個模板標記。
否則,模板繼承將不起作用。
<2>一般來說,基礎模板中的 {% block %} 標簽越多越好。
記住,子模板不必定義父模板中所有的代碼塊,因此你可以用合理的缺省值對一些代碼塊進行填充,然后只對子模板所需的代碼塊進行(重)定義。
俗話說,鉤子越多越好。
<3>如果發覺自己在多個模板之間拷貝代碼,你應該考慮將該代碼段放置到父模板的某個 {% block %} 中。
如果你需要訪問父模板中的塊的內容,使用 {{ block.super }}這個標簽吧,這一個魔法變量將會表現出父模板中的內容。
如果只想在上級代碼塊基礎上添加內容,而不是全部重載,該變量就顯得非常有用了。
<4>不允許在同一個模板中定義多個同名的 {% block %} 。
存在這樣的限制是因為block 標簽的工作方式是雙向的。
也就是說,block 標簽不僅挖了一個要填的坑,也定義了在父模板中這個坑所填充的內容。
如果模板中出現了兩個相同名稱的 {% block %} 標簽,父模板將無從得知要使用哪個塊的內容。
總結
以上是生活随笔為你收集整理的Django学习~1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows10 安装 Twisted
- 下一篇: 树莓派搭建Django服务器通过远程访问