django學習(一)

1.django版本的選擇問題

在學習django之前,我們先做一個基本問題的討論,這個問題是關於django版本的問題。我們進入官網,可以查看django版本的情況。

關於django的版本的問題,現在公司一般用的是1.11.X和2.2.X兩種,3.X是2020年8月出來的,所以比較的新,所以我們現在可以忽略3.X的版本學習。等之後過兩年左右我們可以轉為3.X版本的學習。

1.x 2.x 3.x(目前直接忽略) 我們這次學習使用的是2.2.15的長期支援穩定的版本
1.x和2.x本身差距也不大,我們學習可以建立在之前的1.11.X版本,然後再講解於2.2.X的區別
公司之前用到的是1.8.X,然後轉到1.11.x,然後現在目前的一些項目用到了2.2.X版本。

2.django的安裝

pip3 install django==2.2.15

pip3 install django==2.2.15,如果已經安裝了其他的版本,無需自己卸載,直接重新的安裝,會自動的卸載安裝新的。如果是報錯了,看看是不是timeout,如果是那麼只是網速的波動,重新安裝即可,那麼就可以了。

驗證是否安裝成功的方式:

  • 終端輸入django-admin看看有沒有反應,在終端輸入django-admin,查看是否有反應,有反應表示的是django已經安裝成功了。
  • 在終端運行python,然後導入django,就可以查看django的版本。

3.django的基本操作

命令行的操作

你可以先切換到對應的位置(盤),然後在創建項目。

  • 創建django項目:django-admin startproject 項目名字

  • 創建好了項目之後,我們看一下項目的結構

啟動django項目,在啟動django項目之前,我們一定要先切換到項目的目錄下,然後輸入命令python manager.py runserver,默認是本機的ip和埠默認是8000。然後我們在瀏覽器去訪問這個ip和埠。就會出現django的頁面。

  • python manager.py runserver運行整個項目

創建django應用

  • python manager.py startapp 應用名字

django是一款專門用來開發app應用的web框架。但是這裡的app和我們手機上的app是不一樣的,是有差別的。django框架就類似於一所大學,django的app應用類似於大學裡面的各個學院。比如開發淘寶,有訂單相關,用戶相關,投訴相關等等。創建不同的app應用對應不同的功能。一個app就是一個獨立的功能模組。應用名應該做到見名知意,例如user、order、web

創建的應用一定要去配置文件中註冊

# Application definition
# 註冊的app應用(功能模組)
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',  # 全寫
    'app02',  # 簡寫
]

ps:你在pycharm創建項目的時候,pycharm可以幫你創建一個app應用並且自動註冊,但是我們需要注意的是,pycharm最多只是幫你創建和註冊一個應用,剩下的應用都需要自己去創建和註冊。

整個項目的結構分析:

pycharm操作

創建項目

  • new project 選擇左側第二個django即可

啟動項目

  • 還是用命令行啟動。

  • 用pycharm的運行的鍵來啟動,點擊綠色小箭頭即可。

創建應用

  • 在終端直接輸入python manager.py startapp 應用的名稱即可

  • pycharm的tools run manager.py task提示【前期不要用,不介意使用,我們寫完整的命令】。ps:你在pycharm創建項目的時候,pycharm可以幫你創建一個app應用並且自動註冊,但是我們需要注意的是,pycharm最多只是幫你創建和註冊一個應用,剩下的應用都需要自己去創建和註冊。

修改埠號

我們先點擊如下第一張圖的edit configurations,然後我們可以修改一下host的主機ip和埠號port。

創建server

我們先點擊如下第一張圖的edit configurations,然後我們可以點擊『+』號,添加我們需要的服務,選擇django server。如果我們要刪除所創建的server,選中所要刪除的server,然後再點擊『-』號。

4.命令行與pycharm創建項目的區別

1)命令行創建不會自動有templates文件夾,需要你自己手動的創建templates文件夾。而pycharm會自動幫你創建,並且還會自動在配置文件中配置對應的路徑。所以總結來說,如果我們使用命令來創建的時候,我們既要手動的創建templates文件夾並且要在setting文件中配置templates文件夾的路徑。[os.path.join(BASE_DIR,’templates’)]。

pycharm創建的

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

命令行創建的

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [] # 命令行創建的沒有templates文件夾的路徑。沒有加入os.path裡面。需要我們自己寫
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

上面的兩點我們可以得出來,也就是說用命令創建項目的時候,不單單需要創建templates文件夾,而且還需要我們要手動的添加templates的文件的路徑。還需要去配置文件中的配置路徑。’DIRS’: [os.path.join(BASE_DIR, ‘templates’)]

2)創建的應用一定要去配置文件中註冊

創建出來的應用第一步先去配置文件中註冊,其他的先不要做,什麼都不要干。因為應用不註冊,django是無法識別的,所以我們寫的應用就會沒有用。

5.django小白必會三板斧

1)HttpResponse返回字元串

def user_test(request):
    return HttpResponse('hello')

2)render 返回html頁面

def register(request):
	return render(request, 'register.html')

3)redirect重定向鏈接地址,表示可以重定向到任何一個路由鏈接上。

可以重定向到別人的地址上,也可以重定向到自己的視圖上。

return redirect('//www.baidu.com') 重定向到別人的地址上,需要說明ip和埠號。

return redirect('/home/')重定向到自己的地址上,不需要說明ip和埠號。

django框架不難,但是django 的學習前期記憶的東西很多,所以我們在學習django的時候,我們應該記憶一部分的東西。當我們記憶熟悉了之後,我們應該學會如何去用django框架。

6.靜態文件的配置

我們將html文件默認都放在templates文件夾下。

我們將網站所使用的靜態文件默認都放在static文件夾下。靜態文件—前端已經寫好了的,能夠直接調用使用的文件都可以叫做是靜態文件。例如網站寫好的js文件和css文件,網站用到的圖片文件,以及第三方前端框架。拿來就可以直接使用的我們都可以稱為靜態文件。

django默認是不會為你創建static文件夾的,需要你自己手動的創建。

一般情況下,我們在static文件夾內還會進一步的劃分處理—解耦合處理。這樣我們可以將我們前端的靜態文件放到我們static文件夾下面所對應的目錄下面。這樣的處理可以進一步的解耦合。

在瀏覽器中輸入url能夠看到對應的資源,是因為後端提前開設了該資源的介面。如果瀏覽器中輸入url,無法訪問資源,沒有看到對應的資源,表示的是後端沒有提前開設該資源對應的介面。

static靜態文件的配置

static靜態文件的配置是我們在setting文件中,添加下面的程式碼即可:

# 訪問靜態文件的介面,令牌
STATIC_URL = '/static/'  # 類似於訪問靜態文件的令牌,門牌。我們這裡不一定是寫的是'/static/',可以寫成別的。
"""
如果你想要訪問靜態文件,你就必須已static開頭
"""

"""
靜態文件夾的路徑的配置,為什麼這裡是列表呢,因為路徑不只是一個,可以有多個靜態文件的路徑
/static/  令牌

拿到令牌後,開始去列表裡面從上往下依次的查找,如果第一個有,那麼就會去第一個查找,
如果第一個沒有,那麼就開始去第二個查找,
如果第二個有,那麼就從第二個查找。
如果第二個沒有,那麼就開始從第三個查找,依次類推
如果查找到最後一個都沒有查找到,那麼就會顯示報錯,無法找到文件404的錯誤。

"""
# 靜態文件的配置
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
    os.path.join(BASE_DIR, 'static1'),
    os.path.join(BASE_DIR, 'static2')
]

我們知道,我們配置好了靜態文件以後,那麼我們在我們的前端的頁面就可以使用這個static靜態文件中的資源。例如:

<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css">
<script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>

假如我們的項目中有幾百個甚至更多個html文件,這個時候我們的項目經理提出了一個請求將令牌改為/statics/,而不是/static/,這個時候我們需要更改的是幾百個甚至更多個頁面,這樣的話我們就會很麻煩。當我們辛辛苦苦的花了很多的時間把每一個文件給更改好了,但是我們的項目經理這個時候又來說,不好意思,我們應該將我們的靜態文件的請求的令牌的改為/staticstatic/。這個時候我們有要辛苦的去改。那麼有沒有一種方法使得我們的結果變得更加的簡單,畢竟每次的改動真的太麻煩了,耗時耗力。

這個時候我們提出了一個解決辦法就是靜態文件的動態解析。例如下面所示:

靜態文件的動態解析

{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>

通過靜態文件的動態解析,我們項目經理提出來的上面的請求,我們就會很好的改變,每次就要相應的改變static令牌就可以,省時省力。

當你在寫django項目的時候,可能會出現後端程式碼修改了,但是前端的頁面沒有變化的情況。

  • 你在同一個埠開了好幾個django項目,但是一直在跑的其實是第一個django項目。

  • 是瀏覽器的快取問題。清楚瀏覽器的快取setting–network–disable cache 勾選上

  • 或者在瀏覽器的設置選項中,然後選擇清除瀏覽數據,這樣的話,我們瀏覽器的快取就會撤銷。

    通過上面的措施,我們基本上就可以實現,後端程式碼修改了,實現前端頁面的變化和修改,我們遇見這個問題的時候,我們就可以通過一下幾個方式做就可以了。

7.request對象方法(最基本的三個方法)

form表單默認的提交方式是get方式提交數據,所以我們在寫項目登陸頁面的時候,我們應該說明請求的方式,只有說明請求的方式,那麼form表單才會去這樣的請求,否則都認為是get的方式。

//127.0.0.1:8000/login/?username=zhoquian&password=zhoquian

但是我們在寫登陸頁面應該改為post請求方式,因為用戶名和密碼都是隱私的數據,如果用get請求,那麼就會將用戶名和密碼都暴露在get請求的後面。from表單action參數:1)不寫,默認朝當前所在的url提交數據。2)全寫,指名道姓。3)只寫後綴 /login/

當我們用post請求去點擊按鈕提交的時候,這個時候我們顯示forbidden,csrf verification failed,request aborted。

在前期我們使用django提交post請求的時候,需要取配置文件中注釋掉一行程式碼。後期我們在講解csrf機制的應用。

# django中間件
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',
]

我們這裡講解一下request對象,由下圖所示,我們知道request對象有下面這麼多的屬性和方法。

1)print(type(request.method)) # 返回請求方式 並且是全大寫的字元串的形式 <class 'str'>,這裡我們返回的是『POST』和『GET』兩種請求方式。

def login(request):
    """
    get請求和post請求應該有不同的處理機制
    :param request: 請求相關的數據對象 裡面有很多簡易的方法
    :return:
    """
     if request.method == 'POST':
        username = request.get('username')
        password = request.get('password')
        if not all((username, password)):
            return '請輸入用戶名和密碼!'
    return render(request, 'login.html')

2.獲取POSt請求方式攜帶的參數,print(request.POST),列印出來的是: <QueryDict: {'username': ['zhoquian'], 'password': ['1'],'hobbies':['游泳','健身','跑步']}>。這裡是字典的情況。鍵和值:鍵是我們使用的關鍵字。值是列表的形式。

request.POST:獲取用戶post請求提交的普通數據不包含文件。

request.POST.get():只獲取列表的最後一個元素,返回的是字元串的類型。獲取的是鍵所對應值中的列表的最後一個元素。

request.POST.getlist():獲取列表中的所有的元素,返回的是列表的類型,直接列表取出來。獲取的是鍵所對應值中列表的全部元素。

 if request.method == 'POST':
        # 獲取用戶數據
        print(request.POST)  # 獲取用戶提交的POST請求數據(不包含文件)
        username = request.POST.get('username')
        password = request.POST.get('password')
        print(username,type(username)) # zhouqian <class 'str'>
        hobby = request.POST.get('hobby')
        print(hobby,type(hobby)) # 跑步 <class 'str'>   健身 <class 'str'>
        '''
        我們必須知道一個東西是:get只會獲取到列表中的是最後一個元素,並且返回的是字元串
        '''
        return HttpResponse('收到了,寶貝')
    
        
        # 如果我們想獲取的是全部的元素,那麼我們就不可以用get了,我們使用的是getlist的方法,並且返回的是列表。
        username = request.POST.getlist('username')
        password = request.POST.getlist('password')
        print(username,type(username)) # ['zhouqian'] <class 'list'>
        hobby = request.POST.getlist('hobby') # ['111','222','333'] <class 'list'>
        print(hobby,type(hobby))
         '''
        我們必須知道一個東西是:getlist獲取到列表中的是全部元素,並且返回的是列表
        '''

3.獲取get請求方式攜帶的參數。—-獲取url後面攜帶的參數。//127.0.0.1:8000/login/?username=zhoquian&password=zhoquian

print(request.GET) # 獲取用戶get請求提交的普通數據。<QueryDict: {'username': ['zhoquian'], 'password': ['ada'],'hobby':['游泳','健身','跑步']}>和我們的print(request.post)列印出來的結果是一模一樣。

  	print(request.GET)
    print(request.GET.get('hobby'))
    print(request.GET.getlist('hobby'))
    """
    <QueryDict: {'username': ['zhoquian'], 'password': ['ada'],'hobby':['游泳','健身','跑步']}>
    222
    ['111','222']
    """

在這裡我們得出一個結論就是request.POSTrequest.GET得出來的規律是一模一樣的,列印出來的規律也是一模一樣的。所以我們這裡request.GET.get()方法只獲取列表的最後一個元素,返回的是字元串的類型。request.GET.getlist():獲取列表中的所有的元素,返回的是列表的類型,直接列表取出來。

區別:get請求攜帶的數據是有大小限制的,大概好像只有4KB左右。而post請求方式攜帶的數據是沒有大小限制的。

8.pycharm鏈接mysql資料庫

三個位置查找資料庫相關

  • 右上方databases
  • 左下方databases
  • 配置裡面的plugins插件搜索安裝

如果再沒有,那麼卸載掉pycharm重新安裝。

pycharm可以充當很多款資料庫軟體的客戶端。我們使用pycharm來鏈接mysql資料庫。需要提前創建好庫,並且還要安裝資料庫文件的driver,資料庫文件的驅動。

9.django鏈接mysql資料庫

django默認的資料庫是sqllite3,所以我們在這裡將默認使用的sqlite3改為mysql資料庫。我們去setting配置文件中將我們django默認的sqlite3資料庫改為我們現在要使用的mysql5.7資料庫。django鏈接mysql資料庫的鏈接的地址。

1)第一步在配置文件中配置,這裡我們要說明ENGINE,NAME,USER,PASSWORD,HOST,PORT,CHARSET。

# mysql資料庫的配置,配置的資訊如下所示。
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'imooc',
        'USER': 'root',
        'PASSWORD': '$ZHOU04200926yu',
        'HOST': '192.168.73.129',
        'PORT': 3306,
        'CHARSET': 'utf8'
    }
}

2.程式碼申明

django默認用的是mysqldb模組鏈接MySQL,但是mysqldb模組的兼容性不好,也有很多的bug,所以需要我們手動改為pymysql鏈接MySQL資料庫。你需要告訴django不要用默認的mysqldb,而是要用pymysql。在項目名下的__init__或者任意的應用名下的__init__文件下書寫以下程式碼都可以。

import pymysql
pymysql.install_as_MySQLdb() # 看這一段話就覺得是見名知意。將pymysql作為django默認的mysqldb,不再使用django默認用的mysqldb模組鏈接MySQL資料庫。

10.django的ORM的前戲

我們學會了用pycharm和django鏈接資料庫了,然後我們還需要學會怎麼樣去操作資料庫,只有學會如何去操作資料庫,我們才會從資料庫中得到精髓。我們操作資料庫之前,我們要學習一下django的ORM(對象關係映射object relationship model)的真正的含義。

django的orm框架的簡介

orm:對象關係映射object relationship model

作用:能夠讓一個不會用sql語句的小白也能夠通過python程式碼或者是面向對象的程式碼簡單快捷的去操作資料庫。

不足之處:封裝程度太高,有時候sql語句的效率偏低,需要你自己寫sql語句。

models tables
對象 記錄
對象屬性 記錄中某個欄位對應的值或者方法

操作應用下面的models.py文件

  • 先去models.py中書寫一個類

    # Create your models here.
    class user(models.Model):
        # id int primary key auto_increment  nou null unique  verbose_name 這個關鍵字參數表示的意思是解釋說明該欄位的含義/名字。
        id = models.AutoField(primary_key=True, verbose_name="用戶主鍵")
        # username varchar(20)
        username = models.CharField(max_length=20, verbose_name="用戶名")
        # password int 
        password = models.IntegerField(verbose_name="密碼")
    
  • 資料庫遷移命令,資料庫的遷移命令是兩條:python3 manage.py makemigrations或者python3 manager.py migrate,這兩條命令必須牢記於心。

    python3 manage.py makemigrations 將操作記錄記錄到小本本上(或者說django框架的ORM框架將操作的記錄在migrations文件夾)

    class Migration(migrations.Migration):
    
        initial = True
    
        dependencies = [
        ]
    
        operations = [
            migrations.CreateModel(
                name='user',
                fields=[
                    ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='用戶主鍵')),
                    ('username', models.CharField(max_length=20, verbose_name='用戶名')),
                    ('password', models.IntegerField(verbose_name='密碼')),
                ],
            ),
        ]
    

    python3 manage.py migrate 將操作真正的同步到資料庫中

class Author(models.Model):
“””
由於一張表中必須要有一個主鍵欄位,並且一般的情況下都叫id欄位。
所以orm當你不再定義主鍵欄位的時候 orm會自動的幫你創建一個名為id的主鍵欄位
也就是意味著後續我們在創建模型類的時候,如果主鍵欄位沒有額外的叫法,那麼主鍵欄位可以省略不寫
“””
# CharField必須要指定max_length參數,不指定會直接報錯。
# verbose_name 該參數是所有欄位都有的,就是用來對欄位的解釋。
# username varchar(32)
username = models.CharField(max_length=32, verbose_name=”用戶名”)
# password int
password = models.IntegerField(verbose_name=”密碼”)


**我們修改了models.py文件,所以我們必須重新的執行上面兩條資料庫的遷移命令。**記住:這裡再次的強調一遍,只要是我們修改了models.py中跟資料庫相關的程式碼,我們就必須重新的執行上述的兩條命令,重新生成資料庫的遷移文件,重新將數據操作同步到資料庫中。

### 11.欄位的更、刪、改、查

我們的models文件已經定義好原來的類,但是我們想在表中增加一個欄位的話,那麼我們必須在類中寫。例如我們增加了`info=mdoels.CharField(max_length=32,verbose_name='個人簡介')`。原來的表中已經有數據了,這個時候我們如果要在頁面增加欄位,那麼我們原來的數據表中增加了一個欄位,那麼這個欄位該怎麼處理呢?

**欄位增加的三種處理方式:**

- 可以在終端內直接給出默認值

- 可以在新的欄位默認設置為空,該欄位可以為空。

  `info=models.CharField(max_length=32,verbose_name='個人簡介',null=True)`

- 可以直接給欄位設置默認值

  `hobby=models.CharField(max_length=32,verbose_name='興趣愛好',default='study')`

**欄位的修改:**

直接修改程式碼然後執行資料庫遷移的兩條命令即可!

**欄位的刪除:**

直接注釋對應的欄位然後執行資料庫遷移的兩條命令即可!執行完畢之後欄位對應的數據也都沒有了。所以欄位的刪除一定一定要謹慎。

**欄位的查詢:**

直接點擊models頁面,查看對應類的欄位即可!這樣就可以查看到自己想查的欄位。

**在操作models.py的時候一定要細心,千萬不要注釋一些欄位的程式碼,執行遷移命令之前最好先檢查一下自己寫的程式碼。**

**個人建議:當你離開你的電腦之後,一定要鎖屏。**

### 12.數據的查詢

1)查詢資料庫數據的操作

`user=models.User.objects.filter(username=username)`這個`user`是一個QuerySet:`<QuerySet [<user: user object (1)>, <user: user object (3)>]>`。QuerySet我們可以理解為是列表裡面套對象。

```python
res=models.User.objects.filter(username=username) # <QuerySet [<user: user object (1)>, <user: user object (3)>]>
"""
返回值你先看成是列表中套數據對象的格式
它也支援索引取值  切片操作  但是不支援負數索引
但是django是不會推薦你使用索引的方式和切片的方式取值
django推薦我們使用的是:user_obj=models.User.objects.filter(username=username).first()
這個類似於資料庫的sql語句是:select * from User where username = username;
"""

我們這裡講解一下filter()的用戶,filter相當於sql語句中的where關鍵字,表示篩選,在什麼條件下。filter括弧內的參數可以攜帶一個或者多個參數,參數和參數之間默認是and關係。

user_obj=models.User.objects.filter(username=username,password=password).first()這個相當於sql語句的操作為:select * from User where username=username and password=password limit 1;

這裡還有一個資料庫的查詢操作:

user=models.User.objects.filter()user=models.User.objects.all()是一樣的,返回的都是一個QuerySet。列表中套數據對象的數據類型。

13.數據的增加

數據的增加有兩種的方式,這兩種方式都可以在資料庫中增加數據:

from app01 import models
# 第一種:調用的是objects.create()的方法
res = models.User.objects.create(username=username,password=password) #返回值就是當前被創建的對象本身
# 第二種:調用的是對象.save()方法
user_obj = models.User(username=username,password=password) # 創建User()對象
user_obj.save() # 保存數據

14.數據展示

我們先講將資料庫中的數據全部展示到前端,然後給每一個數據兩個按鈕,一個編輯按鈕,一個刪除按鈕。

# user_list.html頁面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h3 class="text-center">數據展示</h3>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <table class="table table-striped table-hover">
                <thead>
                    <tr>
                        <td>ID</td>
                        <td>USERNAME</td>
                        <td>PASSWORD</td>
                        <td>ACTION</td>
                    </tr>
                </thead>
                <tbody>
                    {% for foo in user_queryset %}
                        <tr>
                            <td>{{ foo.id }}</td>
                            <td>{{ foo.username }}</td>
                            <td>{{ foo.password }}</td>
                            <td>
                                <a href="/user_edit/?user_id={{ foo.id }}" class="btn btn-primary btn-xs">編輯</a>
                                <a href="/user_delete/?user_id={{ foo.id }}" class="btn btn-danger btn-xs">刪除</a>
                            </td>
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>
def user_list(request):
    # 來到這個頁面我們就把這個頁面給顯示出來
    user_queryset = models.user.objects.all()

    return render(request, 'user_list.html', {'user_queryset': user_queryset})
path('user_list/', views.user_list),

15.數據編輯

點擊編輯按鈕朝後端發送編輯數據的請求。但是我們的編輯按鈕怎麼會知道我們應該編輯的是哪一個用戶的資訊呢?每一個編輯按鈕都是一樣的,編輯按鈕怎麼識別要編輯哪一個用戶的資訊呢?

"""
如何告訴後端用戶想要編輯哪條數據?
將編輯按鈕所在的那一行數據的主鍵值發送給後端。
利用url攜帶參數的方式
"""
後端根據主鍵查詢出用戶想要編輯的數據對象,展示到前端頁面提供給用戶查看和編輯。
def user_edit(request):
    user_id = request.GET.get('user_id')
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user_obj = models.user.objects.filter(id=user_id).update(username=username, password=password)
        # 將filter查詢出來的所有數據對象全部更新,批量更新。
        return redirect('/user_list')
    user_obj = models.user.objects.filter(id=user_id).first()
    return render(request, 'user_edit.html', {'user_obj': user_obj})

或者用下面的方式來實現數據的編輯:

def user_edit(request):
    user_id = request.GET.get('user_id')
    user_obj = models.user.objects.filter(id=user_id).first()
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user_obj.username = username
        user_obj.password = password
        user_obj.save()
        # 這個是單個數據對象的更新
        # 這個方法欄位特別多的時候效率會非常的低
        # 從頭到尾將數據對象的所有欄位更新一遍,無論該欄位是否被修改。
        return redirect('/user_list')
    return render(request, 'user_edit.html', {'user_obj': user_obj})
path('user_edit/', views.user_edit),

16.數據刪除

我們刪除也是一樣的,點擊刪除按鈕,我們必須知道我們刪除的是哪一個用戶。我們刪除的話,可以將刪除按鈕所在的那一行的主鍵值傳遞給後端,可以利用url攜帶參數的方式傳遞給後端。

# 我們後端的刪除功能的程式碼如下
def user_delete(request):
    user_id = request.GET.get('user_id')
    user_obj = models.user.objects.filter(id=user_id).delete()
    # 這裡的刪除也是批量的刪除
    return redirect('/user_list')
path('user_delete/', views.user_delete),

Note:1.真正的刪除功能應該是需要二次確認,因為刪除數據是一個謹慎的操作,所以我們在刪除的時候,我們需要二次確認的過程。

2.刪除數據的內部其實不是真正的刪除,因為數據很值錢,不會輕易的刪除掉數據。我們會給數據添加一個標識欄位用來表示當前數據是否被刪除了,如果是刪除了,我們僅僅只是將欄位修改一個轉態的值。

如下所示的情況一樣,我們將is_delete作為刪除的標誌位:

username     password     is_delete
zhouqian     zhouqian         1
AndreasZhou  AndreasZhou      0

17.orm創建表關係

我們知道表的關係有三種的基本轉態,這三種基本轉態分別是多對一,多對多,一對一三種關係。那麼這三種關係在models.py文件中怎麼寫呢?我們的orm(對象關係映射)該怎麼創建表之間的關係呢?我們以下面的表關係為例子,來實現orm如何創建表之間的關係的。

在創建表關係之前,我們要做的第一步就是先將基表創建出來,然後再添加外鍵的欄位。基表的創建如下所示:

class Book(models.Model):
    # 如果我們的主鍵是id,那麼我們可以自己不用創建id主鍵,讓django默認給我們創建id主鍵
    title = models.CharField(max_length=32,verbose_name='書名')
    # 這個表示的是創建了一個小數,總位數有8位,小數點後面佔兩位。
    price = models.DecimalField(max_digits=8,decimal_places=2,verbose_name='價格')
    # 建立一對多的關係
    '''
    圖書和出版社是一對多的關係,並且書是多的一方  所以外鍵欄位放在書表裡面
    '''
    publish = models.ForeignKey(to='publish',on_delete=models.CASCADE)
    '''
    如果欄位對應的是ForeignKey,那麼orm會自動在欄位的後面加_id,如果你自作聰明在欄位後面加了_id,那麼orm框架還是會在後面加上_id。在定義ForeignKey的時候,我們就不要自己去加_id。
    '''
    
    # 建立多對多的關係
    '''
    圖書和作者是多對多的關係,外鍵欄位放在任意一方均可,但是推薦你建在查詢頻率較高的一方
    '''
    author = models.ManyToManyField(to='Author')
    '''
    多對多的author欄位是是虛擬的欄位,主要是用來告訴orm,書籍表和作者表是多對多的關係
    讓orm自動幫你創建第三章表。
    '''
    
class Publish(models.Model):
    # 這裡的id默認讓django幫我們創建,所以我們在使用的時候,就可以不用自己創建id主鍵欄位
    name = models.CharField(max_length=32,verbose_name='出版社名')
    add = models.CharField(max_length=32,Verbose_name='出版社地址')
    
    
class Author(models.Model):
    name = models.CharField(max_length=32,verbose_name='作者名')
    age = models.IntegerField(verbose_name='作者年齡')
    # 建立一對一的關係
    '''
    作者和作者詳情是一對一的關係,外鍵欄位建在任意一方都是可以的,但是推薦你建在查詢頻率較高的一方中
    '''
    author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)
    '''
    OneToOneField也會自動給欄位加_id後綴,所以你也不要自作聰明的自己加上_id。
    '''

class AuthorDetail(models.Model):
    phone = models.IntegerField(verbose_name='作者手機號碼') # 手機號也可以直接是CharField
    addr = models.CharField(max_length=32,verbose_name='作者地址')
在django2.0後,定義外鍵和一對一關係的時候需要加on_delete選項,此參數為了避免兩個表裡的數據不一致問題,不然會報錯:
TypeError: **init**() missing 1 required positional argument: 『on_delete』
舉例說明:
user=models.OneToOneField(User)
owner=models.ForeignKey(UserProfile)
需要改成:
user=models.OneToOneField(User,on_delete=models.CASCADE) --在老版本這個參數(models.CASCADE)是默認值
owner=models.ForeignKey(UserProfile,on_delete=models.CASCADE) --在老版本這個參數(models.CASCADE)是默認值
參數說明:
on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五個可選擇的值
CASCADE:此值設置,是級聯刪除。
PROTECT:此值設置,是會報完整性錯誤。
SET_NULL:此值設置,會把外鍵設置為null,前提是允許為null。
SET_DEFAULT:此值設置,會把設置為外鍵的默認值。
SET():此值設置,會調用外面的值,可以是一個函數。
一般情況下使用CASCADE就可以了。

我們修改了models文件,那麼這樣子我們必須重新執行以下資料庫遷移文件的命令,python manager.py makemigrations和python manager.py migrate。

18.django請求生命周期流程圖

19.路由匹配

django1.11.11的路由匹配的解釋如下所示:

url(r'test',views.test)
url(r'testadd',views.testadd)
'''
url方法第一個參數是正則表達式
只要第一個參數正則表達式能夠匹配到內容,那麼就會立刻停止往下匹配,直接執行對應的視圖函數。

你在輸入url的時候會默認加斜杠。django內部幫你做到重定向,一次匹配不行,url後面會加斜杠再來一次。
'''

# 取消自動加斜杠 默認情況下下面這個參數是True,默認自動添加斜杠。我們了解一下即可。
# APPEND_SLASH = False

django2.2.15的路由匹配如下所示:

urlpatterns = [
    # django2.2.15 與 django1.11.11在視圖函數上是有差別的
    # django2.2.15的path是默認加了r防止字元轉義,^正則匹配以什麼開始,和$正則匹配以什麼結束。
    # 而django1.11.11版本是沒有的,url是需要自己添加r、^、$符號的。
    # 寫我們自己的路由與視圖函數對應的關係
    path('', views.index),
    path('index/', views.index),
    path('home/', views.home),
    path('login/', views.login),
    path('register/', views.register),
    path('user/', views.user_test),
    path('user_list/', views.user_list),
    path('user_edit/', views.user_edit),
    path('user_delete/', views.user_delete),
    path('admin/', admin.site.urls),
]

20.無名分組和有名分組

1)無名分組

'''
分組:就是給某一段正則表達式用小括弧擴起來
'''
django1.11.11的無名分組怎麼寫?
# 無名分組
url(r'^test/(\d+)/$',views.test)

def test(request,test):
    print(test)
    return HttpResponse('test')


django2.2.15的無名分組怎麼寫呢?
# 無名分組
re_path(r'test/(\d+)/', views.test)

def test(request,test):
    print(test)
    return HttpResponse('test')

無名分組就是將括弧內正則表達式匹配到的內容當做位置參數傳遞給後面的視圖函數。

2)有名分組

django1.11.11有名分組怎麼寫呢?
url(r'^test/(?p<year>\d+)/',views.test)
def test(request, year):
    print(year)
    return HttpResponse('test!')

django2.2.15有名分組怎麼寫呢?
# 有名分組
re_path(r'test/(?P<year>[0-9]{4})/', views.test),
def test(request, year):
    print(year)
    return HttpResponse('test!')

有名分組就是將括弧內正則表達式匹配到的內容當做關鍵字參數傳遞給後面的視圖函數。

3)無名分組和有名分組是否可以混合使用

我們直接給出答案:我們的無名分組和有名分組是不能混合使用的。

django1.11.11
url(r'^index/(\d+)/(?P<year>\d+)/', views.index)
django2.2.15
re_path(r'test2/(\d+)/(?P<year>[0-9]{4})/', views.test2)
那麼有名和無名分組是否可以混合使用呢?在這裡我們就不啰嗦了,直接給出答案,有名和無名分組是不能混合使用的。

4)單個分組是否可以使用多次

我們這裡也直接給出答案,單個分組可以多次的使用。我們看下面的程式碼即可。

# django1.11.11單個無名分組可以使用多次
url(r'^index/(\d+)/(\d+)/(\d+)/', views.index)
# django2.2.15單個無名分組可以使用多次
re_path(r'test3/(\d+)/(\d+)/(\d+)/', views.test3)
# django1.11.11單個有名分組可以使用多次
url(r'^index/(?P<year>\d+)/(?P<year>\d+)/(?P<year>\d+)/',views.index)
# django2.2.15單個有名分組可以使用多次
 re_path(r'test4/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{3})', views.test4),