天天看點

Django基礎--2

一、路由系統 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,      
Django基礎--2
Django基礎--2
Django基礎--2
Django基礎--2
Django基礎--2
{% 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() 方法需要注意:

    1. 要捕獲一段url中的值,需要使用尖括号,而不是之前的圓括号;
    2. 可以轉換捕獲到的值為指定類型,比如int。預設情況下,捕獲到的結果儲存為字元串類型,不包含 '/' 這個特殊字元;
    3. 比對模式的最開頭不需要添加 '/' ,因為預設情況下,每個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

Django基礎--2

注釋部分第一節已經寫過,都是通過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      
Django基礎--2

上面看了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  複制到檔案路徑裡面,給連接配接取個名字。使用者名和密碼不用填,點選連接配接

打開後,效果如下面截圖

Django基礎--2

 連接配接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 進行遷移的時候的出錯了,報錯如下:

Django基礎--2

經過篩查,在建立多對一的關系的,需要在Foreign的第二參數中加入on_delete=models.CASCADE  主外關系鍵中,級聯删除,也就是當删除主表的資料時候從表中的資料也随着一起删除

default=1  設定外鍵預設值      

 外鍵關系建立完後,在user表裡面會生成一列usergroup_id,并且生成了一個usergroup類

Django基礎--2

此時要查詢usergroup表中的内容,在html中可以使用 {{row.usergroup.dept}},row為views傳過來的user表中所有對象

通過對象擷取部門

轉載于:https://www.cnblogs.com/Aline2/p/10081674.html