天天看點

Django web架構學習之旅(4)《Django Web 架構》

《Django Web 架構》

目錄

  • Django shell的使用
  • admin 背景資料庫管理
    • 自定義背景管理資料表
    • 修改背景Models的展現形式
    • 模型管理器類
    • 資料庫表管理
  • 資料表關聯關系映射 Relationship Map
    • 一對一映射
    • 一對多映射
    • 多對多映射
  • Cookies 和 Session(會話)
    • Cookies
    • Session(會話)
  • 中間件 Middleware
    • 跨站請求僞造保護 CSRF
  • Django中的forms子產品
    • Field 内置小部件 - widget
    • form表單驗證
  • Django Shell使用

    • 在Django提供了一個互動式的操作項目叫Django Shell 它能夠在互動模式用項目工程的代碼執行相應的操作
    • 利用Django Shell 可以代替編寫View的代碼來進行直接操作
    • 在Django Shell 下隻能進行簡單的操作,不能運作遠端調式
    • 啟動 Django shell 顯示IPython風格的互動界面如下:
      $ python3 manage.py shell
      manage.py shell
      Python 3.6.1 (v3.6.1:69c0db5050, Nov 11 2019, 01:21:04) 
      Type 'copyright', 'credits' or 'license' for more information
      IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.
      
      In [1]: 
      In [2]: from bookstore import models
      In [3]: models.Book.objects.create(title="Python")
      Out[3]: <Book: Book object>
      In [4]: book = models.Book.objects.create(title='C++')
      In [4]: print(book)
      Book object
                 
  • admin 背景資料庫管理

    • django 提供了比較完善的背景管理資料庫的接口,可供開發過程中調用和測試使用
    • django 會搜集所有已注冊的模型類,為這些模型類提拱資料管理界面,供開發者使用
    • 使用步驟:
      • 1、建立背景管理賬号
        背景管理--建立管理者帳号
        python3 manage.py createsuperuser
        
        $ python3 manage.py createsuperuser
        Username (leave blank to use 'tarena'): dayin  # 此處輸入使用者名
        Email address: [email protected]  # 此處輸入郵箱
        Password: # 此處輸入密碼(密碼要複雜些,否則會提示密碼太簡單)
        Password (again): # 再次輸入重複密碼
        Superuser created successfully.
        $ 
                   
      • 2、用注冊的賬号登入背景管理界面
        • 背景管理界面位址: http://127.0.0.1:8000/admin
    • 自定義背景管理資料表
      • 若要自己定義的模型類也能在 /admin 背景管理界中顯示和管理,需要将自己的類注冊到背景管理界面
      • 添加自己定義模型類的背景管理資料表的,需要用admin.site.register(自定義模型類) 方法進行注冊
      • 配置步驟如下:
        1. 在應用app中的admin.py中導入注冊要管理的模型models類, 如:  from . import models
        2. 調用 admin.site.register 方法進行注冊,如: 
                                     			  from django.contrib import admin
        							 			  admin.site.register(自定義模型類)
        3. 示例:
        如: 在 bookstore/admin.py 添加如下代碼對Book類進行管理
        
        # file: bookstore/admin.py
        from django.contrib import admin
        # Register your models here.
        
        from . import models
        ...
        admin.site.register(models.Book)  # 将Book類注冊為可管理頁面
        							 			
                   
    • 修改背景Models的展現形式
      • 在admin背景管理資料庫中對自定義的資料記錄都展示為 XXXX object 類型的記錄,不便于閱讀和判斷
      • 在使用者自定義的模型類中可以重寫 def str(self): 方法解決顯示問題,如:
        classd Bookstore(models.Model):
            ...
            def __str__(self):
                return "書名" + self.title
                   
    • 模型管理器類
      • 作用:
        用背景管理界面添加便于操作的新功能。
                   
      • 說明:
        背景管理器類須繼承自 django.contrib.admin 裡的 ModelAdmin 類
                   
      • 模型管理器使用方法:
        1. 在 <應用app>/admin.py 裡定義模型管理器類
        
        	class XXXX_Manager(admin.ModelAdmin):
        	    ......
        
        2. 注冊管理器與模型類關聯
        
        		from django.contrib import admin
        		from . import models
        		# 注冊models.YYYY 模型類與 管理器類 XXXX_Manager 關聯
        		admin.site.register(models.YYYY, XXXX_Manager) 
        3. 示例:
        
        		# file : bookstore/admin.py
        		from django.contrib import admin
        		from . import models
        		
        		class BookAdmin(admin.ModelAdmin):
        		    list_display = ['id', 'title', 'price', 'market_price']
        		
        		admin.site.register(models.Book, BookAdmin)
        
                   
      • 模型管理器類ModelAdmin中實作的進階管理功能
        1. list_display 去控制哪些字段會顯示在Admin 的修改清單頁面中。
        2. list_display_links 可以控制list_display中的字段是否應該連結到對象的“更改”頁面。
        3. list_filter 設定激活激活Admin 修改清單頁面右側欄中的過濾器
        4. search_fields 設定啟用Admin 更改清單頁面上的搜尋框。 
        5. list_editable 設定為模型上的字段名稱清單,這将允許在更改清單頁面上進行編輯。
        6. 其它參見https://docs.djangoproject.com/en/1.11/ref/contrib/admin/
        
                   
    • 資料庫表管理
      • 修改模型類字段的顯示名字
        • 模型類各字段的第一個參數為verbose_name,此字段顯示的名字會在背景資料庫管理頁面顯示
        • 通過 verbose_name 字段選項,修改顯示名稱示例如下:
          title = models.CharField(
              max_length = 30,
              verbose_name='顯示名稱'
          )
                     
      • 通過Meta内嵌類 定義模型類的屬性及展現形式
        • 模型類可以通過定義内部類class Meta 來重新定義目前模型類和資料表的一些屬性資訊
        • 用法格式如下:
          class Book(models.Model):
              title = CharField(....)
              class Meta:
                  1. db_table = '資料表名'
                      - 該模型所用的資料表的名稱。(設定完成後需要立馬更新同步資料庫)
                  2. verbose_name = '單數名'
                      - 給模型對象的一個易于了解的名稱(單數),用于顯示在/admin管理界面中
                  3. verbose_name_plural = '複數名'
                      - 該對象複數形式的名稱(複數),用于顯示在/admin管理界面中
                     
        • 示例:
          class Meta:
              db_table = 'book_table'  # 将原資料表名"bookstore_book" 換為 "book_table",請檢視資料表
              verbose_name = 'booooook'
              verbose_name_plural = 'booksssssss'  # 去127.0.0.1:8000/admin下看看哪兒變化了?
                     
  • 資料表關聯關系映射 Relationship Map

    • 在關系型資料庫中,通常不會把所有資料都放在同一張表中,這樣做會額外占用記憶體空間
    • 在關系列資料庫中通常用表關聯來解決資料庫
    • 常用的表關聯方式有三種:
      • 一對一映射:如 一張身份證對應一個人
      • 一對多映射:如 一個班級可以有多名學生
      • 多對多映射:如 一個學生可以報多個課程,一個課程可以有多個學生學習
    • 一對一映射
      • 一對一是表示現實事物間存在的一對一的對應關系。
      • 如:一個家庭隻有一個戶主,一個男人有一個妻子,一個人有一個唯一的指紋資訊等
      • 文法:
        在關聯的兩個類中的任何一個類中:
        class A(model.Model):
            ...
        
        class B(model.Model):
            屬性 = models.OneToOneField(A)
                   
      • 用法執行個體:
      • 1、建立作家和妻子類
        # file : xxxxxxxx/models.py
        from django.db import models
        
        class Author(models.Model):
            '''作家模型類'''
            name = models.CharField('作家', max_length=50)
        
        class Wife(models.Model):
            '''作家妻子模型類'''
            name = models.CharField("妻子", max_length=50)
            author = models.OneToOneField(Author)  # 增加一對一屬性
                   
      • 2、查詢
        • 在 Wife 對象中,通過 author 屬性找到對應的author對象
        • 在 Author 對象中,通過 wife 屬性找到對應的wife對象
      • 3、創始一對一的資料記錄
        from . import models
        author1 = models.Author.objects.create(name='陳老師')
        wife1 = models.Wife.objects.create(name='陳夫人', author=author1)  # 關聯王老師
        author2 = models.Author.objects.create(name='小米老師')  # 一對一可以沒有資料對應的資料 
                   
      • 4、一對一資料的互相擷取
        • 正向擷取,直接通過關聯屬性查詢即可
          # 通過 wife 找 author
          from . import models
          wife = models.Wife.objects.get(name='陳夫人')
          print(wife.name, '的老公是', wife.author.name)
                     
        • 反向查詢
          • 通過反向引用屬性查詢
          • 反向引用屬性為執行個體對象.引用類名(小寫),如作家的反向引用為作家對象.wife
          • 當反向引用不存在時,則會觸發異
            # 通過 author.wife 引用屬性 找 wife,如果沒有對應的wife剛觸發異常
            author1 = models.Author.objects.get(name='陳老師')
            print(author1.name, '的妻子是', author1.wife.name)
            author2 = models.Author.objects.get(name='小米老師')
            try:
                print(author2.name, '的妻子是', author2.wife.name)
            except:
                print(author2.name, '還沒有妻子')
                       
      • 作用:

                  主要是解決常用資料不常用資料的存儲問題,把經常加載的一個資料放在主表中,不常用資料放在另一個副表中,這樣在通路主表資料時不需要加載副表中的資料以提高通路速度提高效率和節省記憶體空間,如經常把書的内容和書名建成兩張表,因為在網站上經常通路書名等資訊,但不需要得到書的内容。

    • 一對多映射
      • 一對多是表示現實事物間存在的一對多的對應關系。
      • 如:一個學校有多個班級,一個班級有多個學生, 一本圖書隻能屬于一個出版社,一個出版社允許出版多本圖書
      • 用法:
        • 當一個A類對象可以關聯多個B類對象時
          class A(model.Model):
              ...
          
          class B(model.Model):
              屬性 = models.ForeignKey(多對一中"一"的模型類, ...)
                     
        • 外鍵類 ForeignKey
          • 構造函數:
            ForeignKey(to, on_delete, **options)
                       
          • 常用參數:
            • on_delete
              1. models.CASCADE 級聯删除。 Django模拟SQL限制ON DELETE CASCADE的行為,并删除包含ForeignKey的對象。
              2. models.PROTECT 抛出ProtectedError 以阻止被引用對象的删除;
              3. SET_NULL 設定ForeignKey null;隻有null是True才有可能。
              4. SET_DEFAULT 将ForeignKey設定為其預設值;必須設定ForeignKey的預設值。
              5. 其它參請參考文檔 https://yiyibooks.cn/xx/Django_1.11.6/ref/index.html ForeignKey部分
            • **options 可以是常用的字段選項如:
        • 示例:
          • 有兩個出版社對應五本書的情況:
            • 清華大學出版社有如下書:
              1. C++
              2. Java
              3. Python
            • 北京大學出版社有如下書:
              1. 西遊記
              2. 水浒傳
          • 定義一對多類:
            # file: myorm/models.py
            from django.db import models
            class Publisher(models.Model):
                '''出版社'''
                name = models.CharField('名稱', max_length=50, unique=True)
            
            class Book(models.Model):
                title = models.CharField('書名', max_length=50)
                publisher = models.ForeignKey(Publisher, null=True)
                       
          • 建立一對多的對象:
            # file: xxxxx/views.py
            from . import models
            pub1 = models.Publisher.objects.create(name='清華大學出版社')
            models.Book.objects.create(title='C++', publisher=pub1)
            models.Book.objects.create(title='Java', publisher=pub1)
            models.Book.objects.create(title='Python', publisher=pub1)
            
            pub2 = models.Publisher.objects.create(name='北京大學出版社')
            models.Book.objects.create(title='西遊記', publisher=pub2)
            models.Book.objects.create(title='水浒', publisher=pub2)
                       
          • 查詢:
            • 通過多查一:
              # 通過一本書找到對應的出版社
              abook = models.Book.objects.get(id=1)
              print(abook.title, '的出版社是:', abook.publisher.name)
                         
            • 通過一查多:
              # 通過出版社查詢對應的書
              pub1 = models.Publisher.objects.get(name='清華大學出版社')
              books = pub1.book_set.all()  # 通過book_set 擷取pub1對應的多個Book資料對象
              # books = models.Book.objects.filter(publisher=pub1)  # 也可以采用此方式擷取
              print("清華大學出版社的書有:")
              for book in books:
                  print(book.title)
                         
      • 多對多映射
        • 多對多表達對象之間多對多複雜關系,如: 每個人都有不同的學校(國小,國中,高中,…),每個學校都有不同的學生…
        • 文法:
          在關聯的兩個類中的任意一個類中,增加:
          屬性 = models.ManyToManyField(Entry)
                     
        • 示例:
          一個作者可以出版多本圖書
          一本圖書可以被多名作者同時編寫
          
          class Author(models.Model):
              xxxx xxxx
          
          class Book(models.Model):
              xxxx xxxx
          
              authors = models.ManyToManyField(Author)
                     
        • 資料查詢:
          • 通過 Book 查詢對應的所有的 Authors
            可以通過authors表示對應所有Author的查詢對象
            
            book.authors.all() -> 擷取 book 對應的所有的author的資訊
            
            book.authors.filter(age__gt=80) -> 擷取book對應的作者中年齡大于80歲的作者的資訊
                       
          • 通過 Author 查詢對應的所有的Books
            Django會生成一個屬性 book_set 用于表示對對應的book的查詢對象相關操作
            author.book_set.all()
            author.book_set.filter()
            author.book_set.create(...)  # 建立新書并聯作用author
            author.book_set.add(book)   # 添加已有的書為目前作者author
            author.book_set.clear()  # 删除author所有并聯的書
            author.book_set.remove()  # 删除所author所有并聯的書
                       
          • 示例:
            • 多對多模型:
              class Author(models.Model):
                  '''作家模型類'''
                  name = models.CharField('作家', max_length=50)
                  def __str__(self):
                      return self.name
              class Book(models.Model):
                  title = models.CharField('書名', max_length=50)
                  author = models.ManyToManyField(Author, null=True)
                  def __str__(self):
                      return self.title
                         
            • 多對多視圖操作:
              from django.http import HttpResponse
              
              from . import models
              
              def many2many_init(request):
                  # 建立兩人個作者
                  author1 = models.Author.objects.create(name='dayin')
                  author2 = models.Author.objects.create(name='xiaoyin')
              
                  # dayin和xiaoyin同時寫了一本Python
                  book11 = author1.book_set.create(title="Python")
                  author2.book_set.add(book11)  #
              
                  # xiaoyin還寫了兩本書
                  book21 = author2.book_set.create(title="C")  # 建立一本新書"C"
                  book22 = author2.book_set.create(title="C++")  # 建立一本新書"C++"
              
                  return HttpResponse("初始化成功")
              
              def show_many2many(request):
                  authors = models.Author.objects.all()
                  for auth in authors:
                      print("作者:", auth.name, '發出版了', auth.book_set.count(), '本書: ')
                      for book in books:
                          print('    ', book.title)
                  print("----顯示書和作者的關系----")
                  books = models.Book.objects.all()
                  for book in books:
                      auths = book.author.all()
                      print(book.title, '的作者是:', '、'.join([str(x.name) for x in auths]))
                  return HttpResponse("顯示成功,請檢視伺服器端控制台終端")
                         
            • 多對多最終的SQL結果:
              mysql> select * from myorm2_author;
              +----+-----------+
              | id | name      |
              +----+-----------+
              | 11 | dayin      |
              | 12 | xiaoyin    |
              +----+-----------+
              2 rows in set (0.00 sec)
              
              mysql> select * from myorm2_book;
              +----+--------+
              | id | title  |
              +----+--------+
              | 13 | Python |
              | 14 | C      |
              | 15 | C++    |
              +----+--------+
              3 rows in set (0.00 sec)
              
              mysql> select * from myorm2_book_author;
              +----+---------+-----------+
              | id | book_id | author_id |
              +----+---------+-----------+
              | 17 |      13 |        11 |
              | 20 |      13 |        12 |
              | 18 |      14 |        12 |
              | 19 |      15 |        12 |
              +----+---------+-----------+
              4 rows in set (0.00 sec)
                         
  • Cookies 和 Session(會話)

    • Cookies
      • cookies是儲存在用戶端浏覽器上的存儲空間,通常用來記錄浏覽器端自己的資訊和目前連接配接的确認資訊
      • cookies 在浏覽器上是以鍵-值對的形式進行存儲的,鍵和值都是以ASCII字元串的形存儲(不能是中文字元串)
      • 在Django 伺服器端來設定 設定浏覽器的COOKIE 必須通過 HttpResponse 對象來完成
      • HttpResponse 關于COOKIE的方法
        • 添加、修改COOKIE
          • HttpResponse.set_cookie(key, value=’’, max_age=None, expires=None)
            • key:cookie的名字
            • value:cookie的值
            • expires:儲存時長,以秒為機關(s不寫)
          • 删除Cookie
            • HttpResponse.delete_cookie(key)
            • 删除指定的key 的Cookie。 如果key 不存在則什麼也不發生。
      • Django中的cookies
        • 使用 響應對象HttpResponse 等 将cookie儲存進用戶端
        • 方法1:
          from django.http import HttpResponse
          resp = HttpResponse()
          resp.set_cookie('cookies名', cookies值, 超期時間)
                     
        • 方法二, 使用render對象
          from django.shortcuts import render
          resp = render(request,'xxx.html',locals())
          resp.set_cookie('cookies名', cookies值, 超期時間)
                     
        • 方法三, 使用redirect對象
          from django.shortcuts import redirect
          resp = redirect('/')
          resp.set_cookie('cookies名', cookies值, 超期時間)
                     
      • 擷取cookie:
        • 通過 request.COOKIES 擷取用戶端的 COOKIES資料
          resp = redirect('/')
          resp.set_cookie('cookies名', cookies值, 超期時間)
                     
      • 注:Chrome 浏覽器 可能通過開發者工具的 Application >> Storage >> Cookies 檢視和操作浏覽器端所有的 Cookies 值
      • 示例:
        # file : <項目名>/urls.py
        from . import views
        
        urlpatterns = [
            path(r'^admin/', admin.site.urls),
            # 增删改cookie
            path(r'^add_cookie', views.add_cookie),
            path(r'^mod_cookie/<int:d>', views.mod_cookie),
            path(r'^del_cookie', views.del_cookie),
            path(r'^show_cookie', views.show_cookie),
        ]
            
        # file : <項目名>/views.py
        from . import views
        from django.http import HttpResponse
        def add_cookie(request):
            responds = HttpResponse("已添加mycookie_var1,值為123")
            responds.set_cookie('mycookie_var1', 123, 3600)
            return responds
        
        def mod_cookie(request,d):
            responds = HttpResponse("已修改mycookie_var1,新值為"+d)
            responds.set_cookie('mycookie_var1', d, 3600)
            return responds
        
        def del_cookie(request):
            responds = HttpResponse("已删除mycookie_var1")
            responds.delete_cookie('mycookie_var1')
            return responds
        
        def show_cookie(request):
            value = request.COOKIES.get('mycookie_var1', '沒有值!')
            print("cookie mycookie_var1 = ", value)
            return HttpResponse("mycookie_var1:" + value)
                   
    • Session
      • session是在伺服器上開辟一段空間用于保留浏覽器和伺服器互動時的重要資料
      • 每個用戶端都可以在伺服器端有一個獨立的Session
      • http協定是無狀态的:每次請求都是一次新的請求,不會記得之前通信的狀态
      • 用戶端與伺服器端的一次通信,就是一次會話
      • 實作狀态保持的方式:在用戶端或伺服器端存儲與會話有關的資料
      • 存儲方式包括cookie、session,會話一般指session對象
      • 使用cookie,所有資料存儲在用戶端,注意不要存儲敏感資訊
      • 推薦使用sesison方式,所有資料存儲在伺服器端,在用戶端cookie中存儲session_id
      • 狀态保持的目的是在一段時間内跟蹤請求者的狀态,可以實作跨頁面通路目前請求者的資料
      • 注意:不同的請求者之間不會共享這個資料,與請求者一一對應
      • 什麼是session
        • session - 會話
        • 在伺服器上開辟一段空間用于保留浏覽器和伺服器互動時的重要資料
      • Django啟用Session
        • 在 settings.py 檔案中
        • 項INSTALLED_APPS清單中添加:
          • ‘django.contrib.sessions’,
        • 項MIDDLEWARE_CLASSES清單中添加
          • ‘django.contrib.sessions.middleware.SessionMiddleware’,
      • session的基本操作:
        • Session對于象是一個 QueryDict 字典, 可以用類拟于字典的方式進行操作
        • 儲存 session 的值到伺服器
          • request.session[鍵] = 值
          • 如: request.session[‘KEY’] = VALUE
        • 擷取session的值
          • VALUE = request.session[‘KEY’]
          • VALUE = request.session.get(‘KEY’, 預設值)
        • 删除session的值
          • del request.session[‘KEY’]
        • 在 settings.py 中有關 session 的設定
          • SESSION_COOKIE_AGE 作用:指定sessionid在cookies中的儲存時長 SESSION_COOKIE_AGE = 60*30
          • SESSION_EXPIRE_AT_BROWSER_CLOSE = True 設定隻要浏覽器關閉時,session就失效
        • 注: 當使用session時需要遷移資料庫,否則會出現錯誤
          $ python3 manage.py makemigrations
          $ python3 manage.py migrate
                     
        • session 示例:
          # file : <項目名>/urls.py
          from . import  views
          
          urlpatterns = [
              path(r'^admin/', admin.site.urls),
              # 增删改session
              path(r'^add_session', views.add_session),
              path(r'^mod_session/<int:d>', views.mod_session),
              path(r'^del_session', views.del_session),
              path(r'^show_session', views.show_session),
          ]
              
          # file : <項目名>/views.py
          from . import views
          from django.http import HttpResponse
          def add_session(request):
              request.session['mysession_var'] = 100
              responds = HttpResponse("添加session")
              return responds
          def mod_session(request, d):
              request.session['mysession_var'] = d
              responds = HttpResponse("修改session成功")
              return responds
          def del_session(request):
              try:
                  del request.session['mysession_var']
                  responds = HttpResponse("删除session成功")
              except:
                  responds = HttpResponse("删除session失敗")
              return responds
          def show_session(request):
              mysession_var = request.session.get('mysession_var', '沒有值!')
              print("mysession_var = ", mysession_var)
              return HttpResponse("mysession_var = " + str(mysession_var))
                     
  • 中間件 Middleware

    • 中間件是 Django 請求/響應處理的鈎子架構。它是一個輕量級的、低級的“插件”系統,用于全局改變 Django 的輸入或輸出。
    • 每個中間件元件負責做一些特定的功能。例如,Django 包含一個中間件元件 AuthenticationMiddleware,它使用會話将使用者與請求關聯起來。
    • 他的文檔解釋了中間件是如何工作的,如何激活中間件,以及如何編寫自己的中間件。Django 具有一些内置的中間件,你可以直接使用。它們被記錄在 built-in middleware reference 中。
    • 中間件類:
      • 中間件類須繼承自 django.utils.deprecation.MiddlewareMixin類
      • 中間件類須實作下列五個方法中的一個或多個:
        • def process_request(self, request): 執行視圖之前被調用,在每個請求上調用,傳回None或HttpResponse對象
        • def process_view(self, request, callback, callback_args, callback_kwargs): 調用視圖之前被調用,在每個請求上調用,傳回None或HttpResponse對象
        • def process_response(self, request, response): 所有響應傳回浏覽器之前被調用,在每個請求上調用,傳回HttpResponse對象
        • def process_exception(self, request, exception): 當處理過程中抛出異常時調用,傳回一個HttpResponse對象
        • def process_template_response(self, request, response): 在視圖剛好執行完畢之後被調用,在每個請求上調用,傳回實作了render方法的響應對象
      • 注: 中間件中的大多數方法在傳回None時表示忽略目前操作進入下一項事件,當傳回HttpResponese對象時表示此請求結果,直接傳回給用戶端
    • 編寫中間件類:
      # file : middleware/mymiddleware.py
      from django.http import HttpResponse, Http404
      from django.utils.deprecation import MiddlewareMixin
      
      class MyMiddleWare(MiddlewareMixin):
          def process_request(self, request):
              print("中間件方法 process_request 被調用")
      
          def process_view(self, request, callback, callback_args, callback_kwargs):
              print("中間件方法 process_view 被調用")
      
          def process_response(self, request, response):
              print("中間件方法 process_response 被調用")
              return response
      
          def process_exception(self, request, exception):
              print("中間件方法 process_exception 被調用")
      
          def process_template_response(self, request, response):
              print("中間件方法 process_template_response 被調用")
              return response
                 
    • 注冊中間件:
      # file : settings.py
      MIDDLEWARE = [
          ...
          'middleware.mymiddleware.MyMiddleWare',
      ]
                 
    • 中間件的執行過程
    Django web架構學習之旅(4)《Django Web 架構》
    • 跨站請求僞造保護 CSRF
      • 跨站請求僞造攻擊
        • 某些惡意網站上包含連結、表單按鈕或者JavaScript,它們會利用登入過的使用者在浏覽器中的認證資訊試圖在你的網站上完成某些操作,這就是跨站請求僞造。
      • CSRF
        Cross-Site Request Forgey
        跨     站點   請求    僞裝
                   
      • 說明:
        • CSRF中間件和模闆标簽提供對跨站請求僞造簡單易用的防護。
      • 作用:
        • 不讓其它表單送出到此 Django 伺服器
      • 解決方案:
        • 取消 csrf 驗證(不推薦)
          • 删除 settings.py 中 MIDDLEWARE 中的 django.middleware.csrf.CsrfViewMiddleware 的中間件
        • 開放驗證
          在視圖處理函數增加: @csrf_protect
          @csrf_protect
          def post_views(request):
              pass
                     
        • 通過驗證
          需要在表單中增加一個标簽 
          {% csrf_token %}
                     
  • Django中的forms子產品

    • 在Django中提供了 forms 子產品,用forms 子產品可以自動生成form内部的表單控件,同時在伺服器端可以用對象的形式接收并操作用戶端表單元素,并能對表單的資料進行伺服器端驗證
    • forms子產品的作用:
      • 通過 forms 子產品,允許将表單與class相結合,允許通過 class 生成表單
    • 使用 forms 子產品的步驟
      • 1、在應用中建立 forms.py
      • 2、導入 django 提供的 forms
        • from django import forms
      • 3、建立class,一個class會生成一個表單
        • 定義表單類
        class ClassName(forms.Form):
                ...
                   
      • 4、在 class 中建立類屬性
        • 一個類屬性對應到表單中是一個控件
      • 5、利用Form 類型的對象自動成表單内容
      • 6、讀取form表單并進行驗證資料
    • forms.Form 的文法
      • 屬性 = forms.Field類型(參數)
      • 1、類型
        class XXX(froms.Form):
            forms.CharField() : 文本框 <input type="text">
            forms.ChoiceField() : 下拉選項框 <select>
            forms.DateField() : 日期框 <input type="date">
            ... ...
                   
      • 2、參數
        • label
          • 控件前的文本
        • widget
          • 指定小部件
        • initial
          • 控件的初始值(主要針對文本框類型)
        • required
          • 是否為必填項,值為(True/False)
    • form 表單示例
      • 手動實作Form 表單:
        <form action="/test_form1" method="post">
            <div>
                <label for="id_input_text">請輸入内容:</label> <input type="text" name="input_text" id="id_input_text" />
            </div>
            <button type="submit">送出</button>
        </form>
                   
      • Django Form 實作 Form 表單
        class MySearch(forms.Form):
            input_text = forms.CharField(label = '請輸入内容')
                   
    • 在模闆中解析form對象
      • 方法
        • 需要自定義
        • 表單中的按鈕需要自定義
      • 解析form對象
        在 視圖中建立form對象并發送到模闆中解析.
        ex:
            form = XXXForm()
            return render(request,'xx.html',locals())
                   
      • 手動解析 {% for field in form %} field : 表示的是form對象中的每個屬性(控件) {{field.label}} : 表示的是label參數值 {{field}} : 表示的就是控件 {% endfor %}
      • 自動解析
        • {{form.as_p}} 将 form 中的每個屬性(控件/文本)都使用p标記包裹起來再顯示
        • {{form.as_ul}} 将 form 中的每個屬性(控件/文本)都使用li标記包裹起來再顯示。注意:必須手動提供ol 或 ul 标記
        • {{form.as_table}} 将 form 中的每個屬性(控件/文本)都使用tr标記包裹起來再顯示,注意:必須手動提供table标記
    • Field 内置小部件 - widget
      • 什麼是小部件
        • 表示的是生成到網頁上的控件以及一些其他的html屬性
          message=forms.CharField(widget=forms.Textarea)
          upwd=forms.CharField(widget=forms.PasswordInput)
                     
        • 常用的小部件類型
      Django web架構學習之旅(4)《Django Web 架構》
    • 小部件的使用
      • 繼承自forms.Form
        • 基本版
          • 文法:
            屬性 = forms.CharField() #無預選值使用
                text,password,email,url,textarea,checkbox
            屬性 = forms.ChoiceField() #有預選值使用
                checkbox,radio,select
            
            屬性 = forms.CharField(
                label='xxx',
                widget=forms.小部件類型
            )
                       
          • 示例:
            upwd = forms.CharField(
                label='使用者密碼',
                widget=forms.PasswordInput
            )
            
            message = forms.CharField(
                label='評論内容',	
                widget=forms.Textarea
            )
                       
      • 進階版
        • 特征
          • 在指定控件類型的基礎之上還能指定控件的一些html屬性值
        • 文法
          屬性 = forms.CharField(
                  label='xxx',
                  widget=forms.小部件類型(
                      attrs={
                          'html屬性名':'值',
                          'html屬性名':'值',
                      }
                  )
              )
                     
        • 文檔參見https://yiyibooks.cn/xx/Django_1.11.6/topics/forms/index.html#forms-in-django
  • Django之form表單驗證
    • django form 提供表單和字段驗證
    • 當在建立有不同的多個表單需要送出的網站時,用表單驗證比較友善驗證的封裝
    • 當調用form.is_valid() 傳回True表示目前表單合法,當傳回False說明表單驗證出現問題
    • 驗證步驟:
      • 1、先對form.XXXField() 參數值進行驗證,比如:min_length,max_length, validators=[…],如果不符合form.is_valid()傳回False
      • 2、對各自from.clean_zzz屬性名(self): 方法對相應屬性進行驗證,如果驗證失敗form.is_valid()傳回False
      • 3、調用form.clean(self): 對表單的整體結構進行驗證,如果驗證失敗form.is_valid()傳回False
      • 以上驗證都成功 form.is_valid()傳回True
    • 驗證方法:
      • validators = [驗證函數1, 驗證函數1]
        • 驗證函數驗證失敗抛出forms.ValidationError
        • 驗證成功傳回None
      • def clean_xxx屬性(self):
        • 驗證失敗必須抛出forms.ValidationError
        • 驗證成功必須傳回xxx屬性的值
      • def clean(self):
        • 驗證失敗必須抛出forms.ValidationError
        • 驗證成功必須傳回 self.cleaned_data
    • 文檔參見https://yiyibooks.cn/xx/Django_1.11.6/topics/forms/index.html#forms-in-django
    • 驗證示例
      from django import forms
      import re
      
      mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
      def mobile_validate(value):
          if not mobile_re.match(value):
              raise forms.ValidationError('手機号碼格式錯誤')
      
      class RegisterForm(forms.Form):
          username = forms.CharField(label='使用者名')
          password = forms.CharField(label='請輸入密碼', widget=forms.PasswordInput)
          password2 = forms.CharField(label='再次輸入新密碼', widget=forms.PasswordInput)
          mobile = forms.CharField(label='電話号碼', validators=[mobile_validate])
      
          def clean(self):
              pwd1 = self.cleaned_data['password']
              pwd2 = self.cleaned_data['password2']
              if pwd1 != pwd2:
                  raise forms.ValidationError('兩次密碼不一緻!')
              return self.cleaned_data  # 必須傳回cleaned_data
      
          def clean_username(self):
              username = self.cleaned_data['username']
              if len(username) < 6:
                  raise forms.ValidationError("使用者名太短")
              return username
                 

end…