1. 程式人生 > 實用技巧 >django 知識點擴充套件

django 知識點擴充套件

一、模型部分

1 關於ForeignKey

1.1 級聯

在django2版本以上,外來鍵關聯的資料需要設定級聯更新

xxx = models.ForeignKey(關聯的表,on_delete=model.CASCADE)
# 級聯操作需要注意,如果是一對一的關聯,那沒問題應該級聯刪除
# 但如果是一對多,刪除了一個出版社,就把這個出版社的所有書都刪了,顯然不合理,因為書和出版社只是邏輯聯絡,不是真的物理關聯
# 所以通常我們會斷開一對多的關聯表
 publish = models.ForeignKey(to='Publish',on_delete=models.DO_NOTHING,db_constraint=False)
# db_constraint=False 表示這個外來鍵只有邏輯關聯,沒有實質的關聯
# 級聯也應當修改
on_delete = models.DO_NOTHING # 刪除出版社的時候,書表什麼都不做
on_delete = models.CASCADE # 級聯刪除
on_delete = models.SET_NULL # 前提是這個欄位可以為空
on_delete = models.SET_DEFAULT # 前提是有預設值

1.2 引數

# to 設定要關聯的表
# to_field 設定要關聯表的欄位
# related_name 反向查詢的時候替代原來的‘表名_set’
# db_constraint 是否在資料庫中建立外來鍵約束,預設為True

2 關於內部類

內部類class Meta提供模型的元資料,元資料不屬於任何欄位的東西,是對整張表的描述

具體擁有的引數

  • ordering 排序選項

    • ordering = ('pk',)
      # 需要注意排序的根據是一個元組,所以如果只有一個根據就要加逗號,排序如果出現重複就會根據第二個元素的排序依據進行排序
      
  • db_table 資料庫表名

    • db_table = '資料庫表名'
      # 注意此處修改表名是真實的在資料庫中的表名被修改了,所以需要重新進行資料遷移
      
  • verbose_name_plural/verbose_name 單複數名稱

    • 這是在admin後臺管理的時候顯示的名稱,複數字尾會在中文後加s通常用中文的話就用單數

其他相關欄位

class UserInfo(models.Model):
        nid = models.AutoField(primary_key=True)
        username = models.CharField(max_length=32)

        class Meta:
            # 設定成虛擬表,通常用於通用表,新增一些每個表必有的欄位,讓其他表繼承
            abstract = True
            # 資料庫中生成的表名稱 預設 app名稱 + 下劃線 + 類名
            db_table = "table_name"

            # 聯合索引
            index_together = [
                ("pub_date", "deadline"),
            ]

            # 聯合唯一索引
            unique_together = (("driver", "restaurant"),)
            
            ordering = ('name',)
            
            # admin中顯示的表名稱
            verbose_name='哈哈'

            # verbose_name加s
            verbose_name_plural=verbose_name


二、檢視函式部分

1 關於markdown使用

import markdown

# 將markdown語法渲染成html樣式
    article.body = markdown.markdown(article.body,
        extensions=[
        # 包含 縮寫、表格等常用擴充套件
        'markdown.extensions.extra',
        # 語法高亮擴充套件
        'markdown.extensions.codehilite',
        ])
# 這裡需要注意,原本的文字格式的文章現在轉化成了html程式碼,如果要展示到前端,就要對資料進行轉義
# 複習:轉義的兩種方式

1 直接在前端引數後面加safe過濾器
<p>{{ article.body|safe }}</p>

2 在後端給html程式碼做標記
from django.utils.safestring import mark_safe
res = mark_safe(article.body)

2、queryset物件

2.1 可切片

用python的切片語法去限制查詢的資料條數

Entry.objects.all()[:5]      # (LIMIT 5)
Entry.objects.all()[5:10]    # (OFFSET 5 LIMIT 5)

不支援負的索引,切片返回的是一個新的查詢集,是由原來的查詢集篩選得到的。

2.2 可迭代

取出的是每一個數據物件

2.3 惰性查詢

簡單來說就是如果只是把查詢結果賦值給了一個變數,而沒使用這個變數的話,查詢語句是不會執行的,只有真正對資料進行操作了才會回過頭來執行查詢語句。

2.4 快取機制?

2.5 exists()與iterator()方法

exists

# 由於簡單的if判斷也會把整個資料物件集放入cache中,但是我們不需要判斷這麼多就可以用到exists

if 查詢集.exists():
	# 相當於只從查詢集中拿出一條資料進行判斷
	...

iterator

# 查詢得到的資料集可能會非常大,一次性放入記憶體就會影響效能,我們可以通過iterator把資料集做成一個迭代器
# 注意做成迭代器的特點,取完資料後資料需要重新查詢,無法回頭
# 每次在記憶體中只會存在一個數據
objs = Book.objects.all().iterator()

上面兩種方法都是為了防止出現cache,所以他可能會增多了我們對資料庫的查詢,沒有完美的方法只有合適的方法。

2.6 orm額外方法

model.Student.object.update_or_create(aa=aa,defaults={'bb=bb'})
# 拿第一個引數作為查詢依據,如果存在則修改,如果不存在則新增

3 extra

由於orm對mysql的封裝程度太高,有些情況下我們需要用一些複雜的查詢就可以通過extra來對查詢注入新的sql語句

extra可以指定一個或多個 引數,例如 select, where or tables. 這些引數都不是必須的,但是你至少要使用一個!要注意這些額外的方式對不同的資料庫引擎可能存在移植性問題.(因為你在顯式的書寫SQL語句),除非萬不得已,儘量避免這樣做

4.1引數之select

The select 引數可以讓你在 SELECT 從句中新增其他欄位資訊,它應該是一個字典,存放著屬性名到 SQL 從句的對映。

queryResult=models.Article
           .objects.extra(select={'is_recent': "create_time > '2017-09-05'"})

結果集中每個 Entry 物件都有一個額外的屬性is_recent, 它是一個布林值,表示 Article物件的create_time 是否晚於2017-09-05.

練習:

# in sqlite:
    article_obj=models.Article.objects
              .filter(nid=1)
              .extra(select={"standard_time":"strftime('%%Y-%%m-%%d',create_time)"})
              .values("standard_time","nid","title")
    print(article_obj)
    # <QuerySet [{'title': 'MongoDb 入門教程', 'standard_time': '2017-09-03', 'nid': 1}]>

4.2引數之where / tables

您可以使用where定義顯式SQL WHERE子句 - 也許執行非顯式連線。您可以使用tables手動將表新增到SQL FROM子句。

wheretables都接受字串列表。所有where引數均為“與”任何其他搜尋條件。

舉例來講:

queryResult=models.Article
           .objects.extra(where=['nid in (1,3) OR title like "py%" ','nid>2'])
extra, 額外查詢條件以及相關表,排序
            
                models.UserInfo.objects.filter(id__gt=1)
                models.UserInfo.objects.all() 
                # id name age ut_id
            
            
                models.UserInfo.objects.extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
                # a. 對映
                    # select 
                    # select_params=None
                    # select 此處 from 表
                
                # b. 條件
                    # where=None
                    # params=None,
                    # select * from 表 where 此處
                
                # c. 表
                    # tables
                    # select * from 表,此處
                    
                # c. 排序
                    # order_by=None
                    # select * from 表 order by 此處
                
                
                models.UserInfo.objects.extra(
                    select={'newid':'select count(1) from app01_usertype where id>%s'},
                    select_params=[1,],
                    where = ['age>%s'],
                    params=[18,],
                    order_by=['-age'],
                    tables=['app01_usertype']
                )
                """
                select 
                    app01_userinfo.id,
                    (select count(1) from app01_usertype where id>1) as newid
                from app01_userinfo,app01_usertype
                where 
                    app01_userinfo.age > 18
                order by 
                    app01_userinfo.age desc
                """
                
                result = models.UserInfo.objects.filter(id__gt=1).extra(
                    where=['app01_userinfo.id < %s'],
                    params=[100,],
                    tables=['app01_usertype'],
                    order_by=['-app01_userinfo.id'],
                    select={'uid':1,'sw':"select count(1) from app01_userinfo"}
                )
                print(result.query)
                # SELECT (1) AS "uid", (select count(1) from app01_userinfo) AS "sw", "app01_userinfo"."id", "app01_userinfo"."name", "app01_userinfo"."age", "app01_userinfo"."ut_id" FROM "app01_userinfo" , "app01_usertype" WHERE ("app01_userinfo"."id" > 1 AND (app01_userinfo.id < 100)) ORDER BY ("app01_userinfo".id) DESC
            

三、路由部分

1 url和path的區別

django1.x版本用的是url

基本用法:

from django.conf.urls import url,include
# 引數部分是放一個{}內部鍵值對的key在views函式內要以關鍵字引數接受
# 別名用於反向解析
urlpatterns = [
     url(正則表示式, views檢視函式,引數,別名),
]

# 有名分組 year = 4位數的數字
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
# 無名分組
re_path(r'^articles/([0-9]{4})/$', views.year_archive),

# 路由分發
path('app01/', include(urls))

# 反向解析的應用場景,當我們檢視中或者模版中多次用到了某個url
# 為了防止這個url後期修改導致所有用到的地方都要改,這裡通過一個別名反向解析就行
# 兩種反向解析

# 在檢視函式中
from django.shortcuts import reverse
url = reverse('tag',args=(1,))
>>> /mytag_test/1
# 得到的是url路徑,args是拼接在後面的引數

# 在頁面中
{% url "別名" 引數  引數%}

django2.x用的大部分是path,也可以匯入url去使用,也有結合兩種特性的re_path

# path的第一個是一個寫死的路徑,不支援正則
# 第二個引數是具體的檢視函式
# 也可以反向解析
# 在1.x版本的分組到2中變成了轉換器拼接在url後面
path('article-create/<int:id>/', views.article_create, name='article_create')

# 內建了5中轉化器
str,匹配除了路徑分隔符(/)之外的非空字串,這是預設的形式
int,匹配正整數,包含0。
slug,匹配字母、數字以及橫槓、下劃線組成的字串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字串,包含了路徑分隔符(/)(不能用?)

四、模版部分

1 xss攻擊

當我們服務端提供使用者提交js程式碼的介面,就容易受到xss攻擊,去用一些js程式碼影響我們的伺服器。

如何解決xss攻擊?

python中用了轉義,讓被設計者標記安全的語言才能顯示到頁面,內部原理是,如果使用者給了<a href = 'meizitu.com'> 點選</a>這樣的標籤上傳,如果標記安全,那這個字串就會直接渲染到頁面上變成一個標籤。

如果我們沒有對其轉義,他會原封不動的顯示在html頁面上

展示的就是一些特殊字元,比如&gt表示的就是>,所以展示到頁面的就是在後端顯示的

五、settings

1 靜態資源暴露

網站所用的靜態檔案我們通常都放在static內,比如js,css檔案,如果要讓前端可以用這些靜態檔案渲染頁面,就需要開放介面,關於網站的靜態檔案,django已經給我們開放了令牌(功能類似於反向解析),我們只要配置路徑即可

STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static')
]

而使用者上傳的靜態檔案,也需要專門有一個資料夾來接收,通常我們用media做資料夾的名字,也可以修改

# settings.py

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')  # 使用者上傳的檔案就會儲存到該資料夾下
# media是資料夾的名字,可以自定義,一般使用media作為名字

使用者只要上傳檔案,就會自動建立media目錄,會自動在media內建立相應的上傳目錄

例如models中,avatar欄位

avatar = models.FileField(upload_to='avatar', default='avatar/default.png')

在上傳了頭像後,media內會自動建立一個avatar資料夾來存放頭像

當然我們配置了路徑只能在服務端使用這個路徑,如果前端要訪問後端的資源,就必須要開放相應的介面

在urls配置

固定寫法,複製就能使用

from django.views.static import serve
from bbs import settings
url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})

2 專案修改成中文

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

六、RBAC:基於角色的許可權控制(django內建的auth體系)

# RBAC :是基於角色的訪問控制(Role-Based Access Control ),公司內部系統
# django的auth就是內建了一套基於RBAC的許可權系統

# django中
	# 後臺的許可權控制(公司內部系統,crm,erp,協同平臺)
	user表  #使用者表
    permssion表 # 許可權表
    group表 # 組別表
    user_groups表是user和group的中間表
    group_permissions表是group和permssion中間表
    user_user_permissions表是user和permission中間表
    # 前臺(主站),需要用三大認證
    
# 使用者和組別是多對多
使用者a可以是銷售組也可以是開發組
銷售組也可以有多個人
# 組別和許可權是多對多
銷售組的人有銷售產品的許可權,也有檢查產品的許可權
檢查產品的許可權可能管理組也有
# 使用者和許可權
使用者可以有多重許可權
一個許可權也可以有多個使用者擁有

七、django的快取

1 快取6中配置位置

  1. 開發除錯(此模式為開發除錯使用,實際上不執行任何操作)

  2. 記憶體快取

    CACHES = {
     'default': {
      'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',  # 指定快取使用的引擎
      'LOCATION': 'unique-snowflake',         # 寫在記憶體中的變數的唯一值 
      'TIMEOUT':300,             # 快取超時時間(預設為300秒,None表示永不過期)
      'OPTIONS':{
       'MAX_ENTRIES': 300,           # 最大快取記錄的數量(預設300)
       'CULL_FREQUENCY': 3,          # 快取到達最大個數之後,剔除快取個數的比例,即:1/CULL_FREQUENCY(預設3)
      }  
     }
    }
    
  3. 檔案快取

  4. 資料庫快取

    CACHES = {
     'default': {
      'BACKEND': 'django.core.cache.backends.db.DatabaseCache',  # 指定快取使用的引擎
      'LOCATION': 'cache_table',          # 資料庫表    
      'OPTIONS':{
       'MAX_ENTRIES': 300,           # 最大快取記錄的數量(預設300)
       'CULL_FREQUENCY': 3,          # 快取到達最大個數之後,剔除快取個數的比例,即:1/CULL_FREQUENCY(預設3)
      }  
     }   
    }
    
  5. Memcache快取(使用python-memcached模組連線memcache)

  6. Memcache快取(使用pylibmc模組連線memcache)

2 快取應用

Django提供了不同粒度的快取,可以快取某個頁面,可以只快取一個頁面的某個部分,甚至可以快取整個網站.

2.1 檢視中使用快取

from django.views.decorators.cache import cache_page
import time
from .models import *

@cache_page(15)          #超時時間為15秒
def index(request):
  t=time.time()      #獲取當前時間
  bookList=Book.objects.all()
  return render(request,"index.html",locals())


# index.html
<body>
<h3>當前時間:-----{{ t }}</h3>

<ul>
    {% for book in bookList %}
       <li>{{ book.name }}--------->{{ book.price }}$</li>
    {% endfor %}
</ul>

</body>
# 我們可以看到的效果是15秒內不管前端重新整理了幾次 t都不會改變

2.2 全域性使用快取

既然是全站快取,當然要使用Django中的中介軟體.

使用者的請求通過中介軟體,經過一系列的認證等操作,如果請求的內容在快取中存在,則使用FetchFromCacheMiddleware獲取內容並返回給使用者

當返回給使用者之前,判斷快取中是否已經存在,如果不存在,則UpdateCacheMiddleware會將快取儲存至Django的快取之中,以實現全站快取

快取整個站點,是最簡單的快取方法

在 MIDDLEWARE_CLASSES 中加入 “update” 和 “fetch” 中介軟體
MIDDLEWARE_CLASSES = (
    ‘django.middleware.cache.UpdateCacheMiddleware’, #第一
    'django.middleware.common.CommonMiddleware',
    ‘django.middleware.cache.FetchFromCacheMiddleware’, #最後
)
“update” 必須配置在第一個
“fetch” 必須配置在最後一個
# 檢視和模版都不需要配置

2.3 區域性檢視快取

# views
from django.views.decorators.cache import cache_page
import time
from .models import *
def index(request):
     t=time.time()      #獲取當前時間
     bookList=Book.objects.all()
     return render(request,"index.html",locals())

# index.html
{% load cache %}
{% cache 2 'name' %}
 <h3>快取:-----:{{ t }}</h3>
{% endcache %}