一、路由系統 URL
1.模闆語言循環字典
1.簡單的字典循環
View Code
<ul>
{% for i in user_dict %}
<li>{{ i }}</li>
{% endfor %} </ul> 擷取字典中的key <ul> {% for i in user_dict.keys %} <li>{{ i }}</li> {% endfor %} </ul> 擷取字典中的key,通過keys關鍵字,不要括号 <ul> {% for i in user_dict.values %} <li>{{ i }}</li> {% endfor %} </ul> 擷取字典中的value,通過values關鍵字,不要括号 <ul> {% for i in user_dict.items %} <li>{{ i }}</li> {% endfor %} </ul> 擷取字典中的key和value,得到的是個元組 <ul> {% for i,j in user_dict.items %} <li>{{ i }}---{{ j }}</li> {% endfor %} </ul> 分别擷取字典中的key,value,

{% for i in user_dict %} {% for i in user_dict.keys %} {% for i in user_dict.values %} {% for i in user_dict.items %} {% for i,j in user_dict.items %}
循環字典,和python裡是差不多的,就是後面沒有括号():
- 直接dict :循環的是key
- dict.keys :循環key
- dict.values :循環values
- dict.items :循環key和values
2.一條對應關系對應多個頁面
這裡通過嵌套字典來實作
場景:一個頁面會顯示很多使用者的某個資訊,然後我們點選檢視詳情,會出來使用者的多條資訊,但是這些使用者資訊不是在同一個頁面上,例如我們看部落格園裡面的部落格,每次檢視如,
https://www.cnblogs.com/mlq2017/p/10054661.html /10054661這個值,換一篇部落格,這個nid就會變,
1.通過GET方式實作:
#index.html
<body>
<ul>
{% for i,j in user_dict.items %}
<li><a target="_blank" href="/detail/?nid={{ i }}">{{ j.name}}</a></li>
{% endfor %}
</ul>
</body>
#detail,html
<!DOCTYPE html>
<html en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>詳細資訊</h1>
<h3>使用者名:{{ user_info.name }}</h3>
<h3>年齡:{{ user_info.age }}</h3>
<h3>郵箱:{{ user_info.email }}</h3>
</body>
</html>
#urls.py
path('index/', views.index),
path('detail/', views.detail),
#wiews.py
from django.shortcuts import render
USER_DICT = {
"1":{"name":"root1","age":"12","email":"[email protected]"},
"2":{"name":"root2","age":"13","email":"[email protected]"},
"3":{"name":"root3","age":"14","email":"[email protected]"},
"4":{"name":"root4","age":"15","email":"[email protected]"},
}
def detail(request): #通過GET,html裡面用 /?+{{key}}
a = request.GET.get("nid")
user_info = USER_DICT[a]
print(user_info)
return render(request,"detail.html",{"user_info":user_info})
def index(request):
return render(request,"index.html",{"user_dict":USER_DICT})
View Code
這裡用到了如下方法:
1.html裡面使用:href="/detail/?nid={{ i }}" target="_blank" rel="external nofollow" 通過?加上字典的key跳轉連結
2.views裡面的detail裡面通過GET擷取nid: 然後通過nid擷取字典的值,将值傳給rengder(),再後在頁面擷取每個值中的資訊
2.通過正則方式實作:
上面的HTML代碼隻有一個地方需要修改,做成動态比對
{% for i,j in user_dict.items %}
<li><a target="_blank" href="/detail-{{ i }}.html">{{ j.name }}</a></li>
{% endfor %}
相應的urls.py裡面用到正則比對
from django.contrib import admin
from django.urls import path,re_path
from app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
re_path('detail-(\d+).html', views.detail),
]
既然我們用到正則,那這裡就要導入正則,使用正則比對
from django.urls import re_path
下面的比對為:
re_path('detail-(\d+).html', views.detail),
這裡要注意,之前的視訊是很老的,方法不一樣,按照視訊上說的做,會一直報錯
然後是views.py裡面的修改了
def detail(request,nid): #正則比對
#return HttpResponse(nid)
user_info = USER_DICT[nid]
print(nid)
return render(request,"detail.html",{"user_info":user_info})
這裡我們需要在detail裡面再給一個形參,接收傳過來的動态參數:這裡傳回的是字典的key,
然後通過nid直接擷取字典的第一層值
建議使用正則的時候用下面的方法
re_path('detail-(?P<nid>\d+).html', views.detail),
将形參進行綁定,就不用考慮傳入的位置
1.當html後面接很多-(\d+)時,如,re_path('detail-(\d+)-(\d+).html', views.detail),在detail裡面傳多個形式參數的時候def detail(request,nid,uid): 位置就要一一對應,
如果用上面的方式,我們就可以任意寫形參的位置
2.當我們寫正則的時候綁定了多個參數,在detail裡面傳多個形式參數的時候def detail(request,nid,uid,……):,我們就要穿多個參數,對于這樣的,我們可以使用
def detail(request,*args,**kwargs):
這樣,我們通過re_path('detail-(\d+)-(\d+).html', views.detail) 這樣的就會把值傳給*args
我們通過re_path('detail-(?P<nid>\d+).html', views.detail),這樣的就會吧值傳給**kwargs
總結實戰:
a.
re_path('detail-(\d+)-(\d+).html', views.detail),
def func(request,nid,uid):
def func(request,*args):
args = (2,9)
def func(request,*args,**kwargs):
b.
re_path('detail-(?P<nid>\d+)-(?P<nid>\d+).html', views.detail),
def func(request,nid,uid):
def func(request,*args,**kwargs):
*args為空,**kwargs為字典接受上面的值
三.關于url中的name
1.當頻繁修改url,就要修改urls.py,又要修改html頁面,
我們可以在urls裡面給url進行綁定,之後無論怎麼修改,隻需要修改url裡面的值就行
在綁定是添加name進行綁定
path('index/', views.index,name = "indexx"),
在detail.html裡面,送出表單時,這麼改
<form action="{% url "indexx" %}">
<input type="text" />
</form>
2.當我們還要在綁定的url後面繼續使用比對,如:path('index/(\d+)/', views.index,name = "indexx"),
此時我們無法通過action="{% url "indexx" %}" 進行送出,可以用
1.跳轉到固定的頁面
<form action="{% url "indexx" 11 %}">
後面這個11,表示隻能跳轉到這個頁面,并且這種模闆語言,後面隻能寫固定值
2.跳轉到目前頁面
這裡要用到request中的path_info,擷取目前頁面的url
在detail函數裡面,我們試着列印request.path_info
print(request.path_info) # 輸出結果為:/detail-1.html
distail裡面不需要改,是以我們可以在action裡面這麼寫
<form action="{{ request.path_info }}">
用 path() 方法實作捕獲參數
課上講的是舊版本,現在Django已經2.0了,url()方法被path()方法替代,用法也有差別。
re_path() 可以看做是2.0裡向下相容的一個方法,也就是舊的1.0的 url() 方法。在2.0裡用 path() 方法也可以實作捕獲組的對應關系。使用 path() 方法需要注意:
-
- 要捕獲一段url中的值,需要使用尖括号,而不是之前的圓括号;
- 可以轉換捕獲到的值為指定類型,比如int。預設情況下,捕獲到的結果儲存為字元串類型,不包含 '/' 這個特殊字元;
- 比對模式的最開頭不需要添加 '/' ,因為預設情況下,每個url都帶一個最前面的 '/' ,既然大家都有的部分,就不用浪費時間特别寫一個了。
path('detail2-<int:nid>-<int:uid>.html', views.detail2),
上面的例子,就是捕獲一個0或正整數,并且傳回一個int類型,再用冒号把命名也完成了。除了int,還有下面這些。
預設情況下,Django内置下面的路徑轉換器:
-
- str:比對任何非空字元串,但不含斜杠/,如果你沒有專門指定轉換器,那麼這個是預設使用的;
- int:比對0和正整數,傳回一個int類型;
- slug:可了解為注釋、字尾、附屬等概念,是url拖在最後的一部分解釋性字元。該轉換器比對任何ASCII字元以及連接配接符和下劃線,比如’ building-your-1st-django-site‘ ;
- uuid:比對一個uuid格式的對象。為了防止沖突,規定必須使用破折号,所有字母必須小寫,例如’075194d3-6885-417e-a8a8-6c931e272f00‘ 。傳回一個UUID對象;
- path:比對任何非空字元串,重點是可以包含路徑分隔符’/‘。這個轉換器可以幫助你比對整個url而不是一段一段的url字元串。
小結
上面各種實作的方法由淺入深,并且一個比一個好,推薦用最後面的實作方式:
- 基于正則的url比使用get方式擷取參數的好
- 命名捕獲組比普通捕獲組好
- 推薦還是用最後的 path() 方法來實作,如果是1.x的版本,那麼就是推薦基于正則的命名捕獲組的方法。
另外,在定義函數的時候也可以寫成這種萬能的模式:
def detail2(request, *args, **kwargs):
,這樣的話,要使用
args[0]
(普通捕獲組)或
kwargs['nid']
(命名捕獲組)來取值。
路由分發
之前所有的url對應關系都寫在了項目名下的urls裡面,當我們的項目有多個app的時候,所有的頁面都寫在一起也不好。應該是每個app各自管理自己的那部分url。這就需要路由分發。
1.首先我們在項目名下的urls裡面導入include,
from django.contrib import admin
from django.urls import path,re_path
from django.urls import include #導入include
urlpatterns = [
path('admin/', admin.site.urls),
path('app/',include('app.urls')) #添加app的綁定
]
如果有多個app,就繼續在下面添加綁定 path('app1/',include('app1.urls')) 這樣的
2.然後在app裡面建立一個同樣的urls.py
3.導入views,py
from django.urls import path,re_path
from app import views
urlpatterns = [
path('index/', views.index),
]
預設值
命名空間
二、視圖
送出資料不僅有
POST和GET,還有其他的送出方法,比如:PUT、DELETE、HEAD、OPTION
form表單送出資料
1,request.POST.get()
2,request.GET.get()
3,request.POST.getlist()
4,request.FILES.get()
先上代碼:
#login。html
<!DOCTYPE html>
<html en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<p>
<input type="text" name="user" placeholder="使用者名">
</p>
<p>
<input type="password" name="pwd" placeholder="密碼">
</p>
<p>
男:<input type="radio" name="gender" value="1">
女:<input type="radio" name="gender" value="2">
人妖:<input type="radio" name="gender" value="3">
</p>
<p>
男:<input type="checkbox" name="favl" value="11">
女:<input type="checkbox" name="favl" value="22">
人妖:<input type="checkbox" name="favl" value="33">
</p>
<p>
<select name="city" multiple="multiple">
<option value="sz">深圳</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
</select>
</p>
<p>
<input name="file" type="file">
</p>
<p>
<input type="submit" value="送出">
<span style="color: red">{{ error_msg }}</span>
</p>
</form>
</body>
</html>
views.py
from django.shortcuts import render,redirect,HttpResponse
# Create your views here.
def login(request):
if request.method == "POST":
obj = request.POST.get("gender")
print("obj",obj) #輸出為1
obj1 = request.POST.get("favl")
obj2 = request.POST.getlist("favl")
print("obj1",obj1) #這裡checkbox是多選框,但是隻擷取到一個值,實際要擷取多個,這裡不能用get
print("obj2", obj2) #obj2 ['11', '33'] 這裡擷取到了每個選中的值
obj3 = request.POST.getlist("city")
print("obj3", obj3) #obj3 ['sz', 'bj', 'sh']
obj4 = request.POST.get("file") #obj4 None <class 'NoneType'> 這裡沒擷取到
print("obj4", obj4,type(obj4))
obj5 = request.FILES.get("file")
print("obj5", obj5, type(obj5)) #這裡擷取到的是檔案名和檔案,
#from django.core.files.uploadedfile import InMemoryUploadedFile
f = open(obj5.name,"wb")
for data in obj5.chunks():
f.write(data)
f.close()
elif request.method == "GET":
pass
else:
return HttpResponse("index")
return render(request,"login.html",)
#error_msg = ""
# def login(request):
# if request.method == "POST":
# u = request.POST.get("user")
# p = request.POST.get("pwd")
# if u == 'aaa' and p == '123':
# return HttpResponse("home")
# else:
# error_msg = "使用者名或密碼錯誤"
# return render(request,"login.html",{"error_msg":error_msg})
# elif request.method == "GET":
# pass
# else:
# return HttpResponse("index")
#
# return render(request,"login.html",)
#urls.py
from app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
]
View Code
注釋部分第一節已經寫過,都是通過get擷取
對于從網頁隻擷取一個值的我們可以用get(text,password,radio),
obj = request.POST.get("gender")
print("obj",obj) #輸出為1
但是如果是多選,就需要用getlist(checkbox,select),
obj1 = request.POST.get("favl")
obj2 = request.POST.getlist("favl")
print("obj1",obj1) #這裡checkbox是多選框,但是隻擷取到一個值,實際要擷取多個,這裡不能用get
print("obj2", obj2) #obj2 ['11', '33'] 這裡擷取到了每個選中的值
obj3 = request.POST.getlist("city")
print("obj3", obj3) #obj3 ['sz', 'bj', 'sh']
普通表單無法上傳檔案,要上傳檔案,還需要在form标簽中加 enctype="multipart/form-data"
另檔案我們需要用request.FILES.get(),這樣擷取到的不是檔案的名稱,而是一個類,既能擷取到檔案名,又能擷取到檔案,檔案名通過obj5.name擷取,檔案内容用 obj5.chunks()
obj4 = request.POST.get("file") #obj4 None <class 'NoneType'> 這裡沒擷取到
print("obj4", obj4,type(obj4))
obj5 = request.FILES.get("file")
print("obj5", obj5, type(obj5)) #obj5 作業.png <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>這裡擷取到的是檔案名和檔案,
#from django.core.files.uploadedfile import InMemoryUploadedFile
f = open(obj5.name,"wb")
for data in obj5.chunks():
f.write(data)
f.close()
CBV 和 FBV
- FBV(function base views) 就是在視圖裡使用函數處理請求。
- CBV(class base views) 就是在視圖裡使用類處理請求。
到目前為止,所有的處理都是寫在一個函數裡的。Django還提供另外一個方式,我們也可以通過類來處理。
之前用FBV,這裡也可以把之前的函數改成類處理請求,
#home.html
<!DOCTYPE html>
<html en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/home/" method="post">
<input type="text" name="user" />
<input type="submit" value="送出" />
</form>
</body>
</html>
#views.py
from django.shortcuts import render,redirect,HttpResponse
#FBV和CBV
from django.views import View
class Home(View):
def get(self,request): #當輸入http://127.0.0.1:8000/home/位址時,是get請求,是以下面的列印的結果為GET
print(request.method)
return render(request,"home.html")
def post(self,request):
print(request.method) #當輸入内容時,是post請求,執行了這裡,列印結果為POST
return render(request,"home.html")
#urls.py
from app import views
urlpatterns = [
path('admin/', admin.site.urls),
#path('login/', views.login), 送出表單的
path('home/', views.Home.as_view()),
]
View Code
以上代碼執行和之前使用FBV效果是一樣的,我們在類裡寫上對應的送出方式,當執行是,會自動到類裡面去找對應的函數。
CBV的寫法為
1.先導入View,在視圖裡面建立類
from django.views import View
class Home(View):
2.修改urls裡面,這裡添加url對應關系和FBV不同,應此需要特别注意:中間寫上類名,後面固定跟一個
.as_view()
3.在類中定義相應的方法,
先去看看繼承的View類裡有什麼,在源碼的base.py這個檔案裡。首先裡面定義了一個公有屬性:
點選View會看到
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
是以除了GET,POST,還可以處理這麼多的請求方法,用起來,在類裡照着别的一樣定義一個同名方法就可以了。
3.在類中可以引入父類,就是Django内部定義的父類,這樣我們既能定義自己的類,還可以繼承父類,修改父類(裝飾器)
繼續看源碼的View,調用了as_view()方法裡面會再調用一個dispatch()方法。這個dispatch()方法裡是通過映射擷取我們的 request.method 即送出的方法來調用我們的處理方法的。dispatch()的源碼如下:
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
View Code
結論就是,根據不同的請求類型送出到不同的處理方法,是用過dispatch()方法裡通過映射來實作的。先執行dispatch()方法然後再調用對應的送出類型的處理方法。
class Home(View):
def dispatch(self, request, *args, **kwargs):
#調用父類中的dispatch
result = super(Home,self).dispatch(request, *args, **kwargs)
return result
def get(self,request): #當輸入http://127.0.0.1:8000/home/位址時,是get請求,是以下面的列印的結果為GET
print(request.method)
return render(request,"home.html")
def post(self,request):
print(request.method) #當輸入内容時,是post請求,執行了這裡,列印結果為POST
return render(request,"home.html")
View Code
在類中添加dispatch()方法後,執行效果和不添加它的效果是一樣的
是以通過繼承和重構dispatch()方法,可以在處理方法執行前和執行後自定義一些操作。如果需要的話就在我們的類裡繼承并重構,
def dispatch(self, request, *args, **kwargs):
#調用父類中的dispatch
print("before") //處理前執行的操作
result = super(Home,self).dispatch(request, *args, **kwargs)
print("after") //處理後執行的操作
return result
上面看了View代碼,我們看到了,當送出方式都不比對的時候是執行下面的代碼,頁面是傳回405
def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
這裡我們就可以重構自己的方法了:
def http_method_not_allowed(self, request, *args, **kwargs):
return redirect('/home/')
給views.py分類
預設所有的處理函數都是寫在views.py這個檔案裡的。如果處理函數很多,全部寫在一個檔案裡也會很亂。這是可以考慮建立一個views包來替代原來的views.py檔案。然後在views包裡建立多個py檔案來寫我們的處理函數。比如:
- views/account.py 是使用者相關的操作,登入認證之類的
- views/test.py 是用來測試的處理函數
- views/order.py 訂單相關的操作
三、模闆
四、ORM操作
django資料庫錯誤相關問題
連接配接sqlite資料庫
預設使用的是sqlite3作為資料庫,使用資料庫需要一下步驟
一、建立你的資料庫表結構
在app01裡面的models.py添加類
from django.db import models
# Create your models here.
class Userinfo(models.Model):
username = models.CharField(max_length=32);
password = models.CharField(max_length=64);
上面的類等到去資料庫建立表的時候,表名是 “app01_userinfo” ,也就是 [app名]_[類名] 。
二、設定settings.py檔案
在 INSTALLED_APPS 注冊你的app,把你的app追加到這個清單裡:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.AppConfig',
]
View Code
注:如果是在pycharm裡面建立項目的時候建立的app,預設是已經加上了的;通過指令添加的,需要上面的操作:'app02.apps.AppConfig',
配置資料庫連接配接,預設已經配置好了一個sqlite3,是以不需要修改:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
View Code
三、去終端執行2條指令
python manage.py makemigrations
python manage.py migrate
第一條指令會在 app 目錄下的 migrations 目錄下建立一個檔案(0001_initial.py),記錄我們對 models.py 所做的變動。
第二條指令是真正去操作資料庫了,除了建立我們自己寫的表以外,還建立了很多 django 自己的表。
上面兩條指令都是作用于全局的,如果要限定作用于隻在某個app,可以在最後加上app的名稱:
python manage.py makemigrations app01
python manage.py migrate app01
關于SQLite:
SQLite是一種嵌入式資料庫,它的資料庫就是一個檔案。由于SQLite本身是C寫的,而且體積很小,是以,經常被內建到各種應用程式中,甚至在iOS和Android的App中都可以內建。
Python就内置了SQLite3,是以,在Python中使用SQLite,不需要安裝任何東西,直接使用。
使用SQLite
安裝Navicat Premium 可以連接配接上SQLite
1.在pycharm裡面右鍵點選SQLite,然後點選 copy path
2.打開Navicat Premium ,點選連接配接,選擇SQlite,将C:\Users\ZYP\PycharmProjects\day19\db.sqlite3 複制到檔案路徑裡面,給連接配接取個名字。使用者名和密碼不用填,點選連接配接
打開後,效果如下面截圖
連接配接mysql
在settings裡面設定如下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'dbname',
'USER': 'root',
'PASSWORD': 'xxx',
'HOST': '',
'PORT': '',
}
}
1
2
3
4
5
6
# 由于Django内部連接配接MySQL時使用的是MySQLdb子產品,而python3中還無此子產品,是以需要使用pymysql來代替
# 如下設定放置的與project同名的配置的 __init__.py檔案中
import pymysql
pymysql.install_as_MySQLdb()
View Code
注:上面的資料庫名,使用mysql時,需要自己手動建立
資料的增删改查
ORM操作
先添加如下代碼:
#在APP01裡面的urls.py裡面
path("orm/", views.orm),
#views.py
from app01 import models
def orm(request):
return HttpResponse("orm")
既然要操作資料庫,這裡肯定要導入models子產品,表就用之前講的userinfo
1.1添加資料,網頁上輸入http://127.0.0.1:8000/app01/orm/後,使用Navicat Premium 看下表裡面的内容,此時已經添加了三條資料
#添加資料
for row in list:
list = [
{"username": "root", "password": "admin"},
{"username": "admin", "password": "admin"},
{"username": "user", "password": "123456"},
]
models.Userinfo.objects.create(username=row['username'],password=row["password"])
return HttpResponse("ORM")
1.2添加資料
# 第二種方法
obj = models.Userinfo(username="root",password="123456")
obj.save()
1.3添加資料
# 第三種方法
dic = {"username": "root", "password": "admin"}
models.Userinfo.objects.create(**dic)
2.查詢資料
#查詢資料all.filter
result = models.Userinfo.objects.all() #查詢所有
result = models.Userinfo.objects.filter(username="root").all() #while條件,查詢指定的資料
for i in result:
print(i.id,i.username,i.password)
filter()裡面還可以傳入多個參數,就是多個條件,他們之間的關系是邏輯與(and)。
還有一個first()方法,取出第一個值,這樣傳回就不是清單而直接就是對象了。可以直接用,也可以用在filter()方法後面
models.UserInfo.objects.first()
models.UserInfo.objects.filter(username='root', password='123456').first()
# 上面就是一個驗證使用者登入的邏輯了,傳回結果是None或者是找到的第一個對象
QuerySet 對象,分别列印出查詢結果和這個對象的query屬性:
res = models.UserInfo.objects.all()
print(res) # 結果在下面
# <QuerySet [<UserInfo: UserInfo object (1)>, <UserInfo: UserInfo object (2)>, <UserInfo: UserInfo object (3)>]> print(res.query) # 結果寫下面 # SELECT "cmdb_userinfo"."id", "cmdb_userinfo"."username", "cmdb_userinfo"."password" FROM "cmdb_userinfo
是以要看到對象的值,需要用for循環每個元素
3.删除資料
#删資料 delete
models.Userinfo.objects.filter(username="admin").delete()
4.修改資料
#改資料update
models.Userinfo.objects.filter(username="root").update(password = "123456")
背景管理執行個體
修改settings
#settings中的設定
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',
]
去掉注釋的部分
綁定mysql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'testdb',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1',
'PORT': '',
}
}
View Code
在總urls裡面導入include
# 總urls.py
from django.contrib import admin
from django.urls import path
from django.urls import include
urlpatterns = [
path('admin/', admin.site.urls),
path('ORM/', include("ORM.urls")),
]
#orm中的urls
from django.urls import path,re_path
from ORM import views
urlpatterns = [
path('login/', views.login),
path('home/', views.home),
path('user_info/', views.user_info),
re_path('detail-(?P<nid>\d+)', views.detail),
]
View Code
mysql需要在項目名下的項目裡面修改__init__
import pymysql
pymysql.install_as_MySQLdb()
View Code
添加三個頁面
#login
<!DOCTYPE html>
<html en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/ORM/login/" method="post" enctype="multipart/form-data">
<p>
<input type="text" name="user" placeholder="使用者名">
</p>
<p>
<input type="password" name="pwd" placeholder="密碼">
</p>
<p>
<input type="submit" value="送出">
<span style="color: red">{{ error_msg }}</span>
</p>
</form>
</body>
</html>
#home
<!DOCTYPE html>
<html en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
margin: 0 auto;
}
.head{
height: 48px;
background-color: #66c9f3;
}
.left{
width: 200px;
position: absolute;
left: 0;
top: 48px;
bottom: 0;
background-color: #dddddd;;
}
.menu{
display: block;
padding: 10px;
}
.right{
background-color: #bce8f1;
position: absolute;
top: 48px;
left: 200px;
right: 0px;
bottom: 0;
}
</style>
</head>
<body>
<div class="head">背景管理系統</div>
<div class="left">
<a class="menu" href="/ORM/user_info/">使用者管理</a>
<a class="menu" href="/ORM/user_group/">使用者組管理</a>
</div>
<div class="right"></div>
</body>
</html>
#user_info
<!DOCTYPE html>
<html en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
margin: 0 auto;
}
.head{
height: 48px;
background-color: #66c9f3;
}
.left{
width: 200px;
position: absolute;
left: 0;
top: 48px;
bottom: 0;
background-color: #dddddd;;
}
.menu{
display: block;
padding: 10px;
}
.right{
background-color: #bce8f1;
position: absolute;
top: 48px;
left: 200px;
right: 0px;
bottom: 0;
}
</style>
</head>
<body>
<div class="head">背景管理系統</div>
<div class="left">
<a class="menu" href="/ORM/user_info/">使用者管理</a>
<a class="menu" href="/ORM/user_group/">使用者組管理</a>
</div>
<div class="right">
<h3>使用者清單</h3>
<table 1px">
<thead>
<tr>
<th>ID</th>
<th>使用者名</th>
</tr>
{% for row in userlist%}
<tr>
<td><a href="/ORM/detail-{{row.id}}">{{row.id}}</a></td>
<td>{{row.username}}</td>
</tr>
{% endfor %}
</thead>
</table>
</div>
</body>
</html>
#detail
<!DOCTYPE html>
<html en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
margin: 0 auto;
}
.head{
height: 48px;
background-color: #66c9f3;
}
.left{
width: 200px;
position: absolute;
left: 0;
top: 48px;
bottom: 0;
background-color: #dddddd;;
}
.menu{
display: block;
padding: 10px;
}
.right{
background-color: #bce8f1;
position: absolute;
top: 48px;
left: 200px;
right: 0px;
bottom: 0;
}
</style>
</head>
<body>
<div class="head">背景管理系統</div>
<div class="left">
<a class="menu" href="/ORM/user_info/">使用者管理</a>
<a class="menu" href="/ORM/user_group/">使用者組管理</a>
</div>
<div class="right">
<h3>詳細資訊</h3>
<h5>id:{{ user.id }}</h5>
<h5>使用者名:{{ user.username }}</h5>
<h5>密碼:{{ user.password }}</h5>
</div>
</body>
</html>
View Code
models建立表
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(max_length=16)
password = models.CharField(max_length=32)
去終端執行2條指令
python manage.py makemigrations
python manage.py migrate
views主要代碼:
from django.shortcuts import render,HttpResponse,redirect
from ORM import models
def initialized_db():
USER = [
{"username": "aaa", "password": "1234"},
{"username": "bbb", "password": "1234"},
{"username": "ccc", "password": "1234"},
{"username": "ddd", "password": "1234"},
]
find = models.User.objects.first()
print("find",find)
if find == None:
for row in USER:
models.User.objects.create(username =row["username"],password = row["password"])
initialized_db()
def login(request):
error_msg = ""
if request.method == "GET":
return render(request,"login.html")
elif request.method == "POST":
user = request.POST.get("user",None)
pwd = request.POST.get("pwd",None)
result = models.User.objects.filter(username = user,password = pwd).first()
#print(result)
if result:
return redirect("/ORM/home/")
else:
error_msg = "使用者名或密碼錯誤"
return render(request,"login.html",{"error_msg":error_msg})
else:
return redirect("/login/")
def home(request):
return render(request,"home.html")
def user_info(request):
userlist = models.User.objects.all()
#print(userlist.query) #列印出來的是select語句
return render(request,"user_info.html",{"userlist":userlist})
def detail(request,nid):
a = models.User.objects.filter(id=nid)
user = models.User.objects.filter(id = nid).first() #這裡要用first,否則就要用循環
#print(type(a),type(user)) #<class 'django.db.models.query.QuerySet'> <class 'ORM.models.User'>
return render(request,"detail.html",{"user":user})
增加使用者資訊
在頁面上添加單條使用者資訊,并立即顯示在頁面上
#user_info 添加如下:
<h3>添加使用者</h3>
<form method="post" action="/ORM/user_info/">
<input type="text" name = "user" placeholder="使用者名" />
<input type="password" name = "password" placeholder="密碼" />
<input type="submit" value="添加" />
</form>
views.py 添加并修改如下:
def user_info(request):
if request.method == "GET":
userlist = models.User.objects.all()
#print(userlist.query) #列印出來的是select語句
return render(request,"user_info.html",{"userlist":userlist})
else:
if request.method == "POST":
username = request.POST.get("user")
password = request.POST.get("password")
models.User.objects.create(username = username,password = password)
return redirect("/ORM/user_info/")
删除使用者資訊
修改user_info.html,
#直接修改表格,也可以再添加一列來放删除
<tr>
<td><a href="/ORM/detail-{{row.id}}">{{row.id}}</a></td>
<td>{{row.username}} |
<a href="/ORM/dele-{{row.id}}">删除</a>
</td>
</tr>
urls.py 添加如下:
re_path('dele-(?P<nid>\d+)', views.dele),
#views.py 添加如下
def dele(request,nid):
models.User.objects.filter(id = nid).delete()
return redirect("/ORM/user_info/")
修改使用者資訊
#繼承home頁面後,并修改右側内容為
<div class="right">
<h3>編輯使用者資訊</h3>
<form action="user_edit-{{obj.id}}.html" method="post">
<input style="display: none" type="text" name="id" value="{{obj.id}}"/>
<input type="text" name="username" value="{{obj.username}}"/>
<input type="text" name="password" value="{{obj.password}}"/>
<input type="submit" value="送出"/>
</form>
</div>
#urls.py
re_path('user_edit-(?P<nid>\d+)', views.user_edit),
#views.py
def user_edit(request,nid):
if request.method == "GET":
obj = models.User.objects.filter(id = nid).first()
return render(request,"user_edit.html",{"obj":obj})
else:
if request.method == "POST":
nid = request.POST.get("id")
username = request.POST.get("username")
password = request.POST.get("password")
models.User.objects.filter(id = nid).update(username = username,password = password)
return redirect("/ORM/user_info/")
注意:<form action="user_edit-{{obj.id}}.html" method="post"> 這裡的寫法
ORM表結構
修改表結構
1.修改過表結構之後,需要再執行一下下面的2行指令,把新的表結構應用到資料庫。
python manage.py makemigrations
python manage.py migrate
2.修改資料長度:當修改資料長度比之前長的沒什麼影響,但是修改後的資料長度比之前短,就會造成資料丢失
3.删除一列,重新執行指令就後自動删掉了,沒什麼特别的
4.增加一列:當增加一列
# Create your models here.
class User(models.Model):
username = models.CharField(max_length=16)
password = models.CharField(max_length=32)
email = models.CharField(max_length=32)
在終端執行第一條指令時,會提示,這是因為Django預設内容不能為空,這時你可以根據提示操作
1.在終端輸入字元串作為預設值,然後再輸入第二條指令
2.在新添加的一列後面添加null = True,此時表格新添加的一列就都可以為空了
email = models.CharField(max_length=32,null=True)
5.資料類型
基本資料類型:字元串,數字,時間,二進制
Django的ORM提供了非常多的字段類型,比如:EmailField、URLField、GenericIPAddressField。這些其實都是字元串類型而已,并且确實對我們沒任何用(并不能幫我們做資料驗證)。這些字段類型的隻有在用Django的背景管理頁面 admin 的時候才能發揮資料驗證的效果。隻有通過admin送出資料的時候才會驗證你的資料格式是否正确。後面寫怎麼登入及哪裡會顯示格式錯誤
models對象的資料類型
AutoField(Field)
- int自增列,必須填入參數 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必須填入參數 primary_key=True
注:當model中如果沒有自增列,則自動會建立一個列名為id的列
from django.db import models
class UserInfo(models.Model):
# 自動建立一個列名為id的且為自增的整數列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定義自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整數 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整數 0 ~ 32767
IntegerField(Field)
- 整數列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整數 0 ~ 2147483647
BigIntegerField(IntegerField):
- 長整型(有符号的) -9223372036854775808 ~ 9223372036854775807
自定義無符号整數字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
PS: 傳回值為字段在資料庫中的屬性,Django字段預設的值為:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
BooleanField(Field)
- 布爾值類型
NullBooleanField(Field):
- 可以為空的布爾值
CharField(Field)
- 字元類型
- 必須提供max_length參數, max_length表示字元長度
TextField(Field)
- 文本類型
EmailField(CharField):
- 字元串類型,Django Admin以及ModelForm中提供驗證機制
GenericIPAddressField(Field)
- 字元串類型,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6
- 參數:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定為True,則輸入::ffff:192.0.2.1時候,可解析為192.0.2.1,開啟刺功能,需要protocol="both"
URLField(CharField)
- 字元串類型,Django Admin以及ModelForm中提供驗證 URL
SlugField(CharField)
- 字元串類型,Django Admin以及ModelForm中提供驗證支援 字母、數字、下劃線、連接配接符(減号)
CommaSeparatedIntegerField(CharField)
- 字元串類型,格式必須為逗号分割的數字
UUIDField(Field)
- 字元串類型,Django Admin以及ModelForm中提供對UUID格式的驗證
FilePathField(Field)
- 字元串,Django Admin以及ModelForm中提供讀取檔案夾下檔案的功能
- 參數:
path, 檔案夾路徑
match=None, 正則比對
recursive=False, 遞歸下面的檔案夾
allow_files=True, 允許檔案
allow_folders=False, 允許檔案夾
FileField(Field)
- 字元串,路徑儲存在資料庫,檔案上傳到指定目錄
- 參數:
upload_to = "" 上傳檔案的儲存路徑
storage = None 存儲元件,預設django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字元串,路徑儲存在資料庫,檔案上傳到指定目錄
- 參數:
upload_to = "" 上傳檔案的儲存路徑
storage = None 存儲元件,預設django.core.files.storage.FileSystemStorage
width_field=None, 上傳圖檔的高度儲存的資料庫字段名(字元串)
height_field=None 上傳圖檔的寬度儲存的資料庫字段名(字元串)
DateTimeField(DateField)
- 日期+時間格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 時間格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 長整數,時間間隔,資料庫中按照bigint存儲,ORM中擷取的值為datetime.timedelta類型
FloatField(Field)
- 浮點型
DecimalField(Field)
- 10進制小數
- 參數:
max_digits,小數總長度
decimal_places,小數位長度
BinaryField(Field)
- 二進制類型
View Code
把主鍵拿出來說下
自增id,之前定義表結構的時候,省略了主鍵,讓Django幫我建立了自增id。也可以自己定義主鍵和自增id:
class User(models.Model):
id = models.AutoField(primary_key=True) # 資料類型是自增,并且設為主鍵
g = models.CharField(max_length=32)
6.字段的參數
null 資料庫中字段是否可以為空
db_column 資料庫中字段的列名
db_tablespace
default 資料庫中字段的預設值
primary_key 資料庫中字段是否為主鍵
db_index 資料庫中字段是否可以建立索引
unique 資料庫中字段是否可以建立唯一索引
unique_for_date 資料庫中字段【日期】部分是否可以建立唯一索引
unique_for_month 資料庫中字段【月】部分是否可以建立唯一索引
unique_for_year 資料庫中字段【年】部分是否可以建立唯一索引
verbose_name Admin中顯示的字段名稱
blank Admin中是否允許使用者輸入為空
editable Admin中是否可以編輯
help_text Admin中該字段的提示資訊
choices Admin中顯示選擇框的内容,用不變動的資料放在記憶體中進而避免跨表操作
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
error_messages 自定義錯誤資訊(字典類型),進而定制想要顯示的錯誤資訊;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能為空.", 'invalid': '格式錯誤'}
validators 自定義錯誤驗證(清單類型),進而定制想要的驗證規則
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '優先錯資訊1',
'c2': '優先錯資訊2',
'c3': '優先錯資訊3',
},
validators=[
RegexValidator(regex='root_\d+', message='錯誤了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又錯誤了', code='c2'),
EmailValidator(message='又錯誤了', code='c3'), ]
)
參數
View Code
db_column :資料庫中字段的列名。預設列明就是我們的變量名,可以通過這個參數設定成不一樣的
class UserInfo(models.Model):
username = models.CharField(max_length=32) # 列名就是變量名 username
password = models.CharField(max_length=64, db_column='pwd') # 資料庫中的列名是 pwd
auto_now :自動生成一個目前時間,資料更新時(包括建立),注意更新資料的寫法,和前面的不同
auto_now_add :自動生成一個目前時間,資料建立時
class UserInfo(models.Model):
# 使用者注冊時會生成使用者名和密碼
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
# 建立記錄時會生成目前時間存放在ctime裡,這個就是使用者的注冊時間
ctime = models.DateTimeField(auto_now_add=True)
# 使用者修改密碼會更新uptime的時間,這個就是上次修改密碼的時間
uptime = models.DateTimeField(auto_now=True)
#建立資料
# models.UserInfo.objects.filter(id=nid).update(password='1234') 這種方法更新是不會重新整理 auto_now 的時間的
# 用save()方法更新可以重新整理 auto_now 的時間
# obj = models.UserInfo.objects.filter(id=nid).first()
# obj.password = '4321'
# obj.save()
choices :Admin中顯示選擇框的内容。(用不變動的資料放在記憶體中進而避免連表操作,連表操作會涉及到性能問題)
class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
# 使用者有各種類型
user_type_choices = (
(1, '管理者')
(2, '普通使用者')
(3, '訪客')
)
# 定義一個使用者類型的字段
user_type_id = models.IntegerField(choices=user_type_choices, default=2)
# 這樣資料庫裡是一個整數類型,值是1、2、3。使用字段名取值 obj.user_type_id 擷取到的是數值
# 如果要擷取後面的内容,使用 get_FOO_display() 方法, 即 obj.get_user_type_id_display()
# 但是我們在admin裡看選擇框的時候看到的是“管理者”、“普通使用者”、“訪客”,這就是因為把選項所對應的内容放到了記憶體中了
# 有了Django這個功能就不用再搞一張表,存放各個數值對應的内容了,還要做外鍵關聯,用的時候還要連表查詢
# 即使不用admin,我們也可以在自己的代碼裡讀取這個屬性擷取到内容,避免連表查詢
error_messages :自定義錯誤資訊(字典類型)。字典key:null、blank、invalid、invalid_choice、unique、unique_for_date
class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64, error_messages={'null': "不能為空", 'invalid': '格式錯誤'})
help_text :Admin中該字段的提示資訊。預設沒有提示資訊,設定後會顯示在input框的下方
外鍵操作
一對多
建立外鍵關聯-修改表結構
usergroup = models.ForeignKey(User_Group,on_delete=models.CASCADE,default=1)
ForeignKey建立外鍵
第一次建立的時候,我沒有添加on_delete=models.CASCADE
在使用python manage.py makeigrations 進行遷移的時候的出錯了,報錯如下:
經過篩查,在建立多對一的關系的,需要在Foreign的第二參數中加入on_delete=models.CASCADE 主外關系鍵中,級聯删除,也就是當删除主表的資料時候從表中的資料也随着一起删除
default=1 設定外鍵預設值
外鍵關系建立完後,在user表裡面會生成一列usergroup_id,并且生成了一個usergroup類
此時要查詢usergroup表中的内容,在html中可以使用 {{row.usergroup.dept}},row為views傳過來的user表中所有對象
通過對象擷取部門
轉載于:https://www.cnblogs.com/Aline2/p/10081674.html