Python編程(二十九):Django基礎(views請求信息、模板繼承和導入、自定義方法使用)
本節知識點
URL:路由映射,兩個Views:在views中目前只會接收 get 請求, post 請求等,當然前端發過來的不止這些數據,還有請求頭,會話和cookie在views中:請求的其他信息,裝飾器Models操作:資料庫操作。還有正向操作、反向操作(本節不涉及)Templates:模板的使用,如xxx.html,還可以自定義函數。
cookie和session:用戶認證(本節不涉及session)分頁(自定義的分頁):可用於任何的 Python Web框架Form驗證:輸入內容不合法,不能進行下一步操作(本節不涉及)一、 上節內容回顧
1、 請求周期url > 路由 > 函數或類 > 返回字元串或者模板語言給用戶
Form 表單提交時:
提交 -> url -> 函數或類中的方法
HttpResponse(......)
render(request, index.html) # 經過處理後的字元串
redirect(/index/) #
用戶 < < 返回字元串
(當接收到redirect時) 自動發起另外一個請求
--> url .....
form 表單提交舉例:
假設 render(request, , {name: 123}) 這樣返回,在 html 中:
<html>
<body>
{{ name }}
<script>alert("{{name}}")</script>
</body>
</html>
在這個 html 中,兩處的 {{name}} 都會被替換為 123 返回給用戶。
Ajax 提交,方法如下:
$.ajax({
url: /index/,
type: POST,
data: {k: v}, // $(form對象).serialize(), 可通過這種方式打包獲取。當有多選框,獲取到的字典值是列表
traditional: true, // 加上這一句,data 字典的值可以是列表,這樣後台才能接收到
dataType: JSON,
success: function(d){
location.reload(), // 刷新當前頁面
location.href="要跳轉的URL" // 跳轉到指定 URL
}
})
Ajax 提交的請求周期:
提交 -> url -> 函數或類中的方法,返回給用戶時:
HttpResponse("......") # 字元串
render(request, index.html, {name: michael}) # 使用 render 方法時會對模板進行替換,例如:
<h1>{{ name }}</h1> --> <h1>michael</h1>
不能使用 redirect 方法,所以 Ajax 提交不能跳轉
用戶 <<< 字元串
如果要提交,在 Ajax 中的 success 的 function 方法中做跳轉:
location.reload(), // 刷新當前頁面
location.href="要跳轉的URL" // 跳轉到指定 URL
2、 路由系統URL
/index/ -> 函數或類
/index/(d+) -> 函數或類
/index/(?P<nid>d+) -> 函數或類,nid=d+
/index/(?P<nid>d+) name=root -> 函數或類,nid=d+
reverse(),後台用這個方法可生成URL
{% url root 1 %},前端用這個方法可生成URL
/crm/ include(app01.urls) 路由分發
默認值:下面這個URL後面的字典就是默認值,當有默認值的時候,對應的函數要去接收這個默認值
path(index/, views.index, {name: root}),
def index(request, name):
print(name)
return HttpResponse(Python)
命名空間:
假設在項目文件夾下的urls.py文件內容如下:
from django.urls import path, include
urlpatterns = [
path(a/, include("app01.urls", namespace=author)),
]
在app01文件夾下的urls.py文件內容如下:
from django.urls import path
from app01 import views
app_name = app01 # 注意這裡要指定 app_name 才行
urlpatterns = [
path(index/, views.index, name=index)
]
此時 app01 文件夾下的 views.py文件的 index 函數代碼如下所示:
def index(request):
return HttpResponse(Python)
此時訪問 http://127.0.0.1:8000/a/index/ 頁面顯示 Python。
繼續修改 app01 文件夾下的 views.py文件的 index 函數,使用 reverse 生成 URL,代碼如下所示:from django.urls import reverse
def index(request):
v = reverse(author:index)
print(v)
return HttpResponse(Python)
運行代碼後地址欄輸入 http://127.0.0.1:8000/a/index/ 頁面顯示 Python,後台輸出 /a/index/ ,這就是命名空間的作用。在前端要獲取 URL 的方式也是 author:index 方式。關於命名空間現總結如下:
/admin/ include(app01.urls, namespace=m1) # 項目文件夾下的 urls 指向同一個app 的 URL
/crm/ include(app01.urls, namespace=m1)
app01.urls
/index/ name = n1 # 在 app 的urls 中指定 name 值
reverse("m1:n1") # 在函數中通過命名空間名字與 app 中的 urls 的 name 值獲取 URL
3、 FBV 和 CBV
FBV:URL對應views 的函數,FBV獲取前端數據方法:def func(request):
# 獲取請求
request.POST
request.GET
request.FILES
request.getlist
request.method
request.path_info
# 返回數據
return render, HttpResponse, redirect
4、 模板中的常用方法
假設 render 方法返回數據到前端,前端可用的方法有下面這些:render(request, index.html)
# for循環
# if 條件判斷
# 字典通過索引點(.)取,有 keys, values, items等方法,都不加括弧
5、 操作資料庫
首先要在 models.py 中定義類:class User(models.Model):
username = models.CharField(max_length=32)
email = models.EmailField()
在資料庫中插入數據,更新數據,無驗證功能的用法:
User.objects.create(username=root, email=root123456)
User.objects.filter(id=1).update(email=michael)
有驗證功能:
Django Admin
接下來對數據進行增、刪、改、查等操作
資料庫一對多操作(外鍵),現假設有 UserType 表和 User 表的代碼如下所示:class UserType(models.Model):
name = models.CharField(max_length=32)
class User(models.Model):
username = models.CharField(max_length=32)
email = models.EmailField()
user_type = models.ForeignKey("UserType") # 資料庫存儲時的實際名稱是 user_type_id
user_list = User.objects.all() # 查詢所有數據,QuerySet對象類型列表
for obj in user_list: # 循環列表,獲取數據方法有下面這些:
obj.username, obj.email, obj.user_type_id, # 獲取 User 表數據
obj.user_type.name, obj.user_type.id # 獲取 UserType 表數據
user = User.objects.get(id=1) # 獲取單個對象
user.某個欄位 # 直接用這個對象可獲取數據
使用雙下劃線進行跨表操作
User.objects.all().values("username", "user_type__name",)資料庫多對多操作,現假設有 UserType、User、UserGroup 三張表的代碼如下所示:class UserType(models.Model):
name = models.CharField(max_length=32)
class User(models.Model):
username = models.CharField(max_length=32)
email = models.EmailField()
user_type = models.ForeignKey("UserType")
m = models.ManyToMany(UserGroup) # 自動創建多對多關係
class UserGroup(models.Model):
name = ......
obj = User.objects.get(id=1) # 查詢到的是 User 對象
obj.m.add(2)
obj.m.add(2, 3)
obj.m.add(*[1,2,3])
obj.m.remove(...)
obj.m.clear()
obj.m.set([1,2,3,4,5])
# 多個組, UserGroup 對象
obj.m.all()
obj.m.filter(name=CTO)
二、 views 中的請求信息
當從前端發來請求的時候,後台通常使用 request 參數接收,請求數據中除了常用的 GET、POST、method、FILES等屬性外,還有其它的很多數據可以接收。可以在函數中 print(type(request)),看下還有類型。
首先創建項目文件夾 michael_03 及 app 目錄 app01,創建過程參考 「Python編程(二十八)」。現在有 michael_03michael_03urls.py 文件代碼如下所示:from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path(admin/, admin.site.urls),
path(a/, include("app01.urls", namespace=author)),
]
michael_03app01urls.py 文件代碼如下所示:
from django.urls import path
from app01 import views
app_name = app01
urlpatterns = [
path(index/, views.index, name=index)
]
michael_03app01views.py 文件中的 index 函數代碼如下所示:
def index(request):
print(type(request))
return HttpResponse(Python)
運行 michael_03 項目文件,在地址欄輸入 http://127.0.0.1:8000/a/index/,後台輸出內容如下:
<class django.core.handlers.wsgi.WSGIRequest>從輸出可以看出這是一個類,通過導入這個類,可以進入這個類的源代碼看下,這個類提供了些什麼方法。from django.core.handlers.wsgi import WSGIRequest通過查看 WSGIRequest 類源代碼,可以看到這個類在初始化時有一個參數 environ,這個參數封裝了所有的用戶請求信息。例如將 index 函數的代碼修改如下:def index(request):
from django.core.handlers.wsgi import WSGIRequest
print(type(request))
# 封裝了所有用戶請求信息
print(request.environ)
return HttpResponse(Python)
在地址欄輸入 http://127.0.0.1:8000/a/index/ 後,可以看第二個 print 語句輸出了非常多的信息,信息以字典的形式輸出,並且是原生的數據。可以循環輸出這個字典。另外在函數中除了常用的 request.GET 和 equest.POST 方法外,還有 request.COOKIES 方法。修改後的 index 函數代碼如下:
def index(request):
# request.GET
# request.POST
# request.COOKIES
from django.core.handlers.wsgi import WSGIRequest
print(type(request))
# environ封裝了所有用戶請求信息,字典形式的數據
print(request.environ)
for k, v in request.environ.items():
print(k, v)
return HttpResponse(Python)
在循環輸出這個 request.environ 的時候,可以看到有一個 HTTP_USER_AGENT 請求頭的信息,這個信息表示用戶的終端類型。比如某些網站對手機訪問的時候,返回給用戶的頁面與電腦上訪問時返回給用戶的頁面就會不一樣。獲取這個請求頭的方法是:
request.environ["HTTP_USER_AGENT"]三、 templates(模板繼承)
在項目文件夾下的 urls.py 文件中添加下面這3個 URL:
path(tpl1/, views.tpl1),path(tpl2/, views.tpl2),path(tpl3/, views.tpl3),並且注釋掉這個URL:path(a/, include("app01.urls", namespace=author)),在 app01views.py 中增加下面這3個對應URL的函數:def tpl1(request):
user_list = [1,2,3,4]
return render(request, tpl1.html, {u: user_list})
def tpl2(request):
name = michael
return render(request, tpl2.html, {name: name})
def tpl3(request):
status = "已刪除"
return render(request, tpl3.html, {status: status})
接下來在 templates 目錄下增加4個 html 文件:master.html,tpl1.html,tpl2.html,tpl3.html。通常看到的頁面都有相同的頭部、左側菜單等,假設在每個頁面中寫下重複的 HTML 代碼,會增加工作量,也會增加文件大小。這時可以把具有相同的代碼寫在一個模板文件中,在這裡假設這個模板文件是 master.html,那麼在 tpl1.html,tpl2.html,tpl3.html可以繼承這個模板文件。可以在模板文件中寫下有共用的HTML、CSS、JS等,在子文件中繼承即可,當然在子文件中也可以寫自己的CSS、JS代碼。現假設有模板文件 master.html的代碼如下所示。
templatemaster.html:<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="/static/commons.css" />
<style>
.pg-header{
height: 48px;
background-color: seashell;
color: green;
}
</style>
{% block css %} {% endblock %}
</head>
<body>
<div class="pg-header">Linux系統管理</div>
{% block content %} {% endblock %}
<script src="/static/jquery.js"></script>
{% block js %} {% endblock %}
</body>
</html>
在這段 master.html 文件的代碼中,下面這行代碼可以讓每個頁面定義自己的標題:
<title>{% block title %}{% endblock %}</title>
下面這行代碼在模板文件中引入CSS 文件,可以在子文件中使用:<link rel="stylesheet" href="/static/commons.css" />在模板文件中的 <style></style> 代碼塊是模板文件定義的 CSS,也可以在子文件中使用。接下來的這行代碼說明了可以在子文件中自定義子頁面特有的 CSS 屬性:{% block css %} {% endblock %}下面這行在模板文件中的代碼,則說明了在子文件中可以在這裡寫自己的頁面代碼:{% block content %} {% endblock %}下面這行代碼是在模板文件中引入 JS 文件,這樣在子文件中也可以使用:<script src="/static/jquery.js"></script>下面這行代碼,可以在子文件中引入自己的 JS 代碼,只在子文件中使用:
{% block js %} {% endblock %}這樣當模板文件的代碼有變化時,只需要修改模板文件即可,不用去修改所有的子頁面,節省了很多的時間,也減少出錯的機率。接下來在子文件中繼承模板文件,現在有子文件 tpl1.html 文件,代碼如下所示:templates pl1.html:{% extends "master.html" %} <!--模板可以有多個,extends 為指定要使用的模板名稱-->
{% block title %}用戶管理{% endblock %} <!--使用模板中的title-->
{% block content %} <!--在模板中是 block content,在子頁面中也使用 block content,表示要將子頁面的該位置進行替換-->
<h1>用戶管理</h1>
<ul>
{% for i in u %}
<li>{{ i }}</li>
{% endfor %}
</ul>
{% endblock %}
{% block css %}
<style>
body{
background-color: red;
}
</style>
{% endblock %}
在這個 tpl1.html 文件中,第一行代碼 {% extends "master.html" %} 指定了繼承的模板文件名稱,第二行是子文件的頁面標題。接下來的 {% block content %}{% endblock %} 塊是子文件的代碼塊。後面的 {% block css %}{% endblock %} 是子文件的CSS 代碼,與其它子文件不共用。後面還可以繼續寫子文件的 JS 代碼塊。
通過 tpl1.htm 文件可知,子文件繼承了模板文件,子文件要寫自己特有的 CSS 代碼和 JS 代碼,則在模板文件中要有相應的 {% block css %} {% endblock %},{% block js %} {% endblock %}塊才可以在子文件中自定義自己的 CSS 和 JS,另外引用的順序可以不分先後,但引用的名稱要正確,在每個 block 後面的字元就是要引用的名稱。另外 block 與 endblock 要成對出現。在 tpl2.html 中的代碼如下:{% extends "master.html" %}
{% block content %}
<h1>修改密碼{{ name }}</h1>
{% endblock %}
在 tpl3.html 中的代碼如下:
{% extends "master.html" %}
{% block content %}
<h3>{{ status }}</h3>
{% endblock %}
運行 michael_03 後,在地址欄輸入 http://127.0.0.1:8000/tpl1/,http://127.0.0.1:8000/tpl2/,http://127.0.0.1:8000/tpl3/,都可以正常訪問。
四、 模板導入(include)
在前面繼承模板使用的 extends 命令,每個子文件只能繼承一個模板,不能繼承多個。在寫頁面的時候,同一個功能要在頁面是重複使用,傳統的方法就是要在頁面上多次重複寫相同的代碼,這裡可以把這個功能相同的代碼寫在一個 html 文件中,使用 include 來使用這個功能。例如現在要在頁面上多次使用輸入框和提交按鈕,這時可以新建一個 input.html 文件,代碼如下:
templatesinput.html:
<form>
<input type="text" />
<input type="submit" />
</form>
接下來在 tpl3.html 文件導入這個 input.html 文件,並多次使用文本輸入框,代碼如下所示:
{% extends "master.html" %}
{% block content %}
<h3>{{ status }}</h3>
{% include "input.html" %}
{% include "input.html" %}
{% include "input.html" %}
{% endblock %}
代碼的 {% include "input.html" %} 就表示導入 input.html 這個文件。這行 include 代碼也可以包含在模板中的 for 循環中,進行多次使用,例如下面這樣:
{% for i in u %}
{% include "input.html" %}
{% endfor %}
五、 自定義 simple_tag 方法
在Django的HTML文件中,還有其它的方法可以使用,例如直接使用 Python 中的方法。例如後台傳過來兩個數字,在HTML中讓這兩個數字相加或者相乘之類的,比如傳過來的字元串,讓其只顯示前幾個字元,或者讓字元全大寫、小寫等在頁面上顯示等。在HTML中提供的方法是這樣使用的:
{{ item.event_start|date:"Y-m-d H:i:s"}} # item.event_start 是後台傳過來的字元串,date:"Y-m-d H:i:s"表示轉化成日期後顯示在頁面上
{{ bio|truncatewords:"30" }} # 在頁面上只顯示前 30 個字元
{{ my_list|first|upper }} # 字元串首字母大寫
{{ name|lower }} # 字元串全部轉化為小寫
雖然 Django 能夠提供這些方法,但這些方法還是有限,這時就要考慮創建自己的方法。
在michael_03urls.py文件添加下面這行代碼:path(tpl4/, views.tpl4),在app01views.py文件中寫 tpl4 函數代碼如下:def tpl4(request):
name = MICHAEL
return render(request, "tpl4.html", {name: name})
在 michael_03michael_03settings.py 文件註冊 app01,在 INSTALLED_APPS 變數中添加下面這行代碼:
app01,
在 app01 目錄下創建 templatetags 文件夾,在該文件夾下創建 michael.py 文件。michael.py 文件的代碼如下:
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.simple_tag
def foo():
return "Foo"
在 tempates pl4.html 文件的代碼如下所示:
{% load michael %}
<body>
<div>
{{ name }}
</div>
<div>
{{ name | lower }}
</div>
<div>
{% foo %}
</div>
</body>
此時在地址欄輸入 http://127.0.0.1:8000/tpl4/,頁面上有顯示:MICHAEL, michael, Foo。其中的 MICHAEL 字元串是後台傳過來的字元串,michael 是調用 Python的 lower 方法轉化成小寫的字元串。另外一個 Foo 是在 app01 emplatetagsmichael.py文件自定義的函數 foo 返回的字元串。通過這個過程就使用了自定義Python函數的方法。
在這個自定義的函數 foo 中,還可以傳遞參數,現在給這個 foo 函數傳遞兩個參數,修改後的代碼如下:def foo(x, y):
return x + y
此時在這 tpl4.html 文件的引用位置的代碼修改如下:
<div>
{% foo 5 6 %}
</div>
此時在刷新 http://127.0.0.1:8000/tpl4/ 頁面,就可以看到運算的結果 11。
自定義函數總結:
simple_tag(1)、在 app 目錄下創建 templatetags 目錄,這個目錄名稱必須是這個,不能改。
(2)、在 templatetags 目錄下可以創建任意 py 文件。
(3)、在 py 文件中創建 template 對象,這個對象名稱必須是 register,不能使用其它名稱。
(4)、使用 register 對象去裝飾函數,例如下面這樣:
@register.simple_tag
def function(x,y,z,...)
return "abcdefg..."
(5)、要使用這個自定義函數,需要在 settings 中註冊 APP。
(6)、在要使用這個自定義函數的 HTML 文件的頂部 load這個 py 文件,比如:{% load michael %}。
(7)、使用自定義函數的方法:{% 函數名 arg1 arg2 %},arg 是參數,參數之間用空格分隔,比如:{% foo 5 6 %}
缺點:不能作為 HMTL 中的 if 條件判斷
優點:參數可以傳任意多個
前面自定義函數時使用的裝飾器是:@register.simple_tag,裝飾器還有另外一個:@register.filter。現在在 michael.py文件中新增一個 hello_world 函數,使用 @register.filter 裝飾器,代碼如下所示:
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.filter
def hello_world(x, y):
return x + " ".join(y.split(","))
使用 @register.filter 裝飾的函數可以用在 HTML中 if 判斷語句,而 @register.simple_tag 裝飾的函數不能用在判斷語句中。
自定義函數總結:filter(1)、在 app 目錄下創建 templatetags 目錄,這個目錄名稱必須是這個,不能改。
(2)、在 templatetags 目錄下可以創建任意 py 文件。
(3)、在 py 文件中創建 template 對象,這個對象名稱必須是 register,不能使用其它名稱。
(4)、使用 register 對象去裝飾函數,例如下面這樣:
@register.filter
def function(x,y)
return "abcdefg..."
(5)、要使用這個自定義函數,需要在 settings 中註冊 APP。
(6)、在要使用這個自定義函數的 HTML 文件的頂部 load這個 py 文件,比如:{% load michael %}。
(7)、使用自定義函數的方法:{{ 參數1 | 函數名:"參數2" }},比如:{{ "python" | hello_world:" linux" }}
缺點:參數最多可以傳兩個,不能加空格
優點:能作為 HMTL 中的 if 條件判斷
(下一篇繼續)
推薦閱讀:
※Django的未來在哪裡?
※如何在Django中設置定時任務?
※0基礎掌握Django框架(6)視圖與URL分發器
※DRF 與 React(Django2.1 + 測試 + xadmin + api文檔)-翻譯強化版
※flask-sqlalchemy的類似django-orm的管理器objects擴展
TAG:Django(框架) | Python開發 | Python框架 |