python繪制風車,路飛學城python全棧開發_python 全棧開發,Day98(路飛學城背景,django ContentType組件,表結構講解)..

 2023-11-19 阅读 30 评论 0

摘要:昨日內容回顧 1. 為什么要做前后端分離?-前后端交給不同的人來編寫,職責劃分明確。-API (IOS,安卓,PC,微信小程序...)-vue.js等框架編寫前端時,會比之前寫jQuery更簡單快捷。2. 對于后端人員,主要為前端提供:API(接口) python繪制風車?以

昨日內容回顧

ContractedBlock.gif

ExpandedBlockStart.gif

1. 為什么要做前后端分離?-前后端交給不同的人來編寫,職責劃分明確。-API (IOS,安卓,PC,微信小程序...)-vue.js等框架編寫前端時,會比之前寫jQuery更簡單快捷。2. 對于后端人員,主要為前端提供:API(接口)

python繪制風車?以前的你的接口:

http://127.0.0.1:8000/index/http://127.0.0.1:8000/users/http://127.0.0.1:8000/add_users/http://127.0.0.1:8000/del_users/http://127.0.0.1:8000/edit_users/restful 規范:

http://127.0.0.1:8000/users/

python 前端、3. 談談你對restful規范的理解?1. 使用https代替http

https://www.luffycity.com/course/detail/web/3http://www.luffycity.com/course/detail/web/3

2. 在URL中體現自己寫的是API

Python java?https://www.luffycity.com/api/https://api.luffycity.com/可能會跨域3. 在URL中體現版本

https://www.luffycity.com/api/v1/users

https://www.luffycity.com/api/v2/users4. 名詞(面向資源編程)

https://www.luffycity.com/api/v1/users

https://www.luffycity.com/api/v1/song5. 行為

https://www.luffycity.com/api/v1/users

method:

get,獲取

post,新建

put,更新

patch,局部更新

delete,刪除6. 條件

https://www.luffycity.com/api/v1/users?page=1https://www.luffycity.com/api/v1/users?page=1&gender=2

7. 狀態碼200

301

302

404

500推薦使用code:defxx(request):

ret= {'code':1000,'data':None}try:

...exceptExptions as e:

ret['status'] = 1001ret['error'] = 'xxxx錯誤'

returnJsonResponse(ret)8. 錯誤信息

{

code:10001,

error:'用戶名或密碼錯誤'}9. 返回結果:

GET:

https://www.luffycity.com/api/v1/users

響應:

{

code:1000,

data: [

{'name':'趙森','age':19},

{'name':'趙云','age':16},

{'name':'趙云','age':16},

{'name':'趙云','age':16},

{'name':'趙云','age':16},

]

}

GET:

https://www.luffycity.com/api/v1/users/1/響應:

{

code:1000,

data:{'name':'趙森','age':19},

}

POST:

https://www.luffycity.com/api/v1/users

請求體:

{'name':'大表哥','age':19}

響應(不要):

{

code:1000,

data:{'id':9, 'name':'大表哥','age':19}

}

PUT/PATCH:

https://www.luffycity.com/api/v1/users

請求體:

{'name':'大表哥','age':19}

響應(不要):

{

code:1000,

data:{'id':9, 'name':'大表哥','age':19}

}

DELETE:

...10. hyper link

訪問:https://www.luffycity.com/api/v1/users

{

code:1000,

data:[

{'id':1,'name':'趙森','age':19, 'depart':https://www.luffycity.com/api/v1/depart/1/},

{'id':1,'name':'趙森','age':19, 'depart':https://www.luffycity.com/api/v1/depart/1/},

{'id':1,'name':'趙森','age':19, 'depart':https://www.luffycity.com/api/v1/depart/1/},

{'id':1,'name':'趙森','age':19, 'depart':https://www.luffycity.com/api/v1/depart/1/},

{'id':1,'name':'趙森','age':19, 'depart':https://www.luffycity.com/api/v1/depart/1/},

]

}

https://www.luffycity.com/api/v1/users

{

code:1000,

data:[

{'id':1,'name':'趙森','age':19, 'depart_title':'公關部'},

{'id':1,'name':'趙森','age':19, 'depart_title':'公關部'},

{'id':1,'name':'趙森','age':19, 'depart_title':'公關部'},

{'id':1,'name':'趙森','age':19, 'depart_title':'公關部'},

{'id':1,'name':'趙森','age':19, 'depart_title':'公關部'},

]

}4. django rest framework框架的作用?

幫助開發者可以快速開發出遵循restful規范的API5. django rest framework框架都有哪些組件(10)?

版本

權限

認證

節流

分頁

解析器****序列化*****視圖****路由

渲染器

View Code

補充:

http傳輸的數據是明文的,https傳輸的數據是加密的

簡單情況下,使用status,1表示成功,0表示失敗

復雜情況下,使用code,查看支付寶api的code說明

一般寫接口的時候,推薦使用code。錯誤信息是服務器返回的!

根據restful規范,POST請求,要返回新增的完整數據。但是,如果前端不需要這些信息,可以不返回!

規定是死的,人是活的。所以要看應用場景!

delete默認是沒有返回信息的,但是,還是得返回一個狀態。誰知道你,刪除有沒有成功呢?

hyper link,返回信息中包含超鏈接。這個也是看需求了

restful規范,不光是python才有。其他語言,也遵循這個規范!

一、路飛學城背景

初期

2012年,有2個人講python。其中一個是alex,主要是周末班。有7個人,當時沒有固定場所,在7個人所在公司的辦公室講課。

一周換一家公司。

2014年,alex進入汽車之家。2014下半年,和佩奇。講Python,也是講周末班。有了固定場所--沙河。

線下班,復制難。不好拿投資,規模擴大難。

第一版:給視頻+拉群

第一版,在線教育。弄一個QQ群,將視頻放到網上。給錢買視頻,比如1000塊,拉群進去討論。

學成率比較低,學員自己堅持不下去。10%以下的成功率!

第二版:51cto合作

第二版,和51cto合作,將視頻交由51cto管理。每周三,周五做直播,直播2小時。主要做在線答疑!

前期看直播的人,比較多。做了3次直播之后,指數急劇下滑,因為學員技術參差不齊,后面都聽不懂了!

最終以失敗告終,51cto下架

第三版:51cto合作-->微職位

由alex和51cto談判,推動51cto的微職位,就是一對一輔導。現今,51cto官網,還保留者微職位。

還是給視頻,并且分配一個導師,協助學員學習。并不是真正的一對一,找兼容人員。比如優秀的畢業生,1000~1500的費用。

導師主動跟進學員學習!

好的導師,一個人,可以對接50個人。有些人,遇到問題,會先自己搞定,搞不定,才會找導師。提升自己的解決問題能力,導師就會很輕松。

學成率也不高,相對于以前的看視頻,學成率有提升!

第一個問題,導師問題。導師每周跟進一次。有些不問問題的學生,可能忽略跟進了

第二個問題,學生問題。周末在家一天,大多數學不學下的,出去玩,或者打游戲了。學生不積極,沒有明確目標

第三個問題,視頻問題。針對的學員的狀態,是不同的!還有一些,不錄制的視頻。跟線上不一樣!

第四個問題,擴張問題。光靠線下,無法壯大。

第四版:路飛學城

老男孩,自己做路飛學城。初期,由學生投資,占2%,還有銷售投資,2個人,一人50萬。路飛學城正式成立!

視頻,單獨錄制

由導致單獨錄制,根據知識點,錄制一小段

導師獎懲

以前是靠人工,現在是自動化。

聊天信息,導師上傳到服務器。每周都要有,7天聊天沒有,就扣錢!

學員購買課程后,安排導師,導師賬號有一定的金額。導師沒有安裝規定,就扣錢。導師自己肯定不愿意扣錢的,所以會主動跟進!

學員不做作業,導師差評,都扣錢

學員換導師,導師分配的金額就沒有了,給另外的導師!

以前導師是兼值的,沒有辦法做到實時回復,導師也有自己公司的事情要做。

后來招了全職導師,做了服務升級。這些是需要掏錢的!

顛覆了在線教育,以前都是看視頻,現在是一對一輔導!

學員獎懲

學生,有獎學金

作業做的好,學的快,有獎勵金額!

全線課程6個月,是有時效的。過了6個月,就沒有了!需要重新購買,這樣可以督促學生學習!

學習是根據階段的,一個階段必須考核通過,才能進入下一階段。不能超階段學習!

每個模塊的視頻是按照階段進行的,不是隨意看的!

項目架構

主站

使用vue.js + django rest framework的架構,前后端分離。全面采用https,前端和后端在同一臺服務器(同域名同端口),避免跨域問題。

主站,注意是給學員使用的,用來購買課程,查看視頻等....

里面沒有退款按鈕,但是有簽訂協議的。可以休學,延長時間。

退款,需要聯系銷售人員確認,跟進。為什么不能做到退款瞬時完成呢?因為有競爭對手,它會刷量。

賬戶的流入流出,是有手續費的。量大的話,會虧損的。

淘寶目前是7天退款,也是為了避免刷量。

導師后臺

主要給導師使用的,賬戶金額,學員跟進...

管理后臺

主要是運營使用,用來上傳視頻,編輯課程相關信息...

開發人員

ContractedBlock.gif

ExpandedBlockStart.gif

主站:-前端:- 前端姑娘 v1,vue.js 1.0

- 前端姑娘 v2,vue.js 2.0(目前全面使用2.0)-后端:-老村長-產品經理- alex/wu sir/文周

導師: 1人

管理后臺:1人+ 兼職導師

View Code

二、django ContentType組件

請求頭ContentType

ContentType指的是請求體的編碼類型,常見的類型共有3種:

1. application/x-www-form-urlencoded

這應該是最常見的 POST 提交數據的方式了。瀏覽器的原生

表單,如果不設置 enctype 屬性,那么最終就會以 application/x-www-form-urlencoded 方式提交數據。請求類似于下面這樣(無關的請求頭在本文中都省略掉了)

ContractedBlock.gif

ExpandedBlockStart.gif

POST http://www.example.com HTTP/1.1Content-Type: application/x-www-form-urlencoded;charset=utf-8user=yuan&age=22

View Code

2. multipart/form-data

這又是一個常見的 POST 數據提交的方式。我們使用表單上傳文件時,必須讓

表單的 enctype 等于 multipart/form-data。直接來看一個請求示例:

ContractedBlock.gif

ExpandedBlockStart.gif

POST http://www.example.com HTTP/1.1Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA------WebKitFormBoundaryrGKCBY7qhFd3TrwA

Content-Disposition: form-data; name="user"yuan------WebKitFormBoundaryrGKCBY7qhFd3TrwA

Content-Disposition: form-data; name="file"; filename="chrome.png"Content-Type: image/png

PNG ... content of chrome.png ...------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

View Code

這個例子稍微復雜點。首先生成了一個 boundary 用于分割不同的字段,為了避免與正文內容重復,boundary 很長很復雜。然后 Content-Type 里指明了數據是以 multipart/form-data 來編碼,本次請求的 boundary 是什么內容。消息主體里按照字段個數又分為多個結構類似的部分,每部分都是以 --boundary 開始,緊接著是內容描述信息,然后是回車,最后是字段具體內容(文本或二進制)。如果傳輸的是文件,還要包含文件名和文件類型信息。消息主體最后以 --boundary-- 標示結束。關于 multipart/form-data 的詳細定義,請前往 rfc1867 查看。

這種方式一般用來上傳文件,各大服務端語言對它也有著良好的支持。

上面提到的這兩種 POST 數據的方式,都是瀏覽器原生支持的,而且現階段標準中原生

表單也只支持這兩種方式(通過 元素的 enctype 屬性指定,默認為 application/x-www-form-urlencoded。其實 enctype 還支持 text/plain,不過用得非常少)。

隨著越來越多的 Web 站點,尤其是 WebApp,全部使用 Ajax 進行數據交互之后,我們完全可以定義新的數據提交方式,給開發帶來更多便利。

3. application/json

application/json 這個 Content-Type 作為響應頭大家肯定不陌生。實際上,現在越來越多的人把它作為請求頭,用來告訴服務端消息主體是序列化后的 JSON 字符串。由于 JSON 規范的流行,除了低版本 IE 之外的各大瀏覽器都原生支持 JSON.stringify,服務端語言也都有處理 JSON 的函數,使用 JSON 不會遇上什么麻煩。

JSON 格式支持比鍵值對復雜得多的結構化數據,這一點也很有用。記得我幾年前做一個項目時,需要提交的數據層次非常深,我就是把數據 JSON 序列化之后來提交的。不過當時我是把 JSON 字符串作為 val,仍然放在鍵值對里,以 x-www-form-urlencoded 方式提交。

舉例:

新建一個項目untitled1

修改urls.py

ContractedBlock.gif

ExpandedBlockStart.gif

from django.contrib importadminfrom django.urls importpathfrom app01 importviews

urlpatterns=[

path('admin/', admin.site.urls),

path('index/', views.index),

]

View Code

修改views.py

ContractedBlock.gif

ExpandedBlockStart.gif

from django.shortcuts importrender,HttpResponsefrom django.views.decorators.csrf importcsrf_exempt,csrf_protect#Create your views here.

@csrf_exempt #跳過csrf

defindex(request):print(request.POST)return HttpResponse('...')

View Code

啟動項目,訪問首頁

1341090-20180811144527764-1905073634.png

這個時候,boss,發了一個post請求,給index

新建一個boss.py,放在項目根目錄中

ContractedBlock.gif

ExpandedBlockStart.gif

importrequests

requests.post(

url='http://127.0.0.1:8000/index/',

data={"user":"root","pwd":123}

)

View Code

執行boss.py,查看Pycharm控制臺輸出:

收到了一條post請求

假設boss發送的是json數據呢?

再次執行boss.py,查看控制臺

發現得到是空字典?為什么呢?boss說,已經給你發過去了,你怎么回答?

注意:request.POST不是萬能的,因為它只接收請求頭為application/x-www-form-urlencoded,其他類型一概不支持。

查看源碼,只有為application/x-www-form-urlencoded,才會解析數據

ContractedBlock.gif

ExpandedBlockStart.gif

def_load_post_and_files(self):"""Populate self._post and self._files if the content-type is a form type"""

if self.method != 'POST':

self._post, self._files= QueryDict(encoding=self._encoding), MultiValueDict()return

if self._read_started and not hasattr(self, '_body'):

self._mark_post_parse_error()return

if self.content_type == 'multipart/form-data':if hasattr(self, '_body'):#Use already read data

data =BytesIO(self._body)else:

data=selftry:

self._post, self._files=self.parse_file_upload(self.META, data)exceptMultiPartParserError:#An error occurred while parsing POST data. Since when

#formatting the error the request handler might access

#self.POST, set self._post and self._file to prevent

#attempts to parse POST data again.

#Mark that an error occurred. This allows self.__repr__ to

#be explicit about it instead of simply representing an

#empty POST

self._mark_post_parse_error()raise

elif self.content_type == 'application/x-www-form-urlencoded':

self._post, self._files= QueryDict(self.body, encoding=self._encoding), MultiValueDict()else:

self._post, self._files= QueryDict(encoding=self._encoding), MultiValueDict()

View Code

使用request.body,它是萬能。它直接接收請求體的原始數據,是一個bytes類型的數據

修改views.py,改為body

ContractedBlock.gif

ExpandedBlockStart.gif

from django.shortcuts importrender,HttpResponsefrom django.views.decorators.csrf importcsrf_exempt,csrf_protect#Create your views here.

@csrf_exempt #跳過csrf

defindex(request):print(request.body)return HttpResponse('...')

View Code

再次執行腳本,查看控制臺輸出:

b'{"pwd": 123, "user": "root"}'

如果需要獲取user,需要對數據進行解碼

ContractedBlock.gif

ExpandedBlockStart.gif

from django.shortcuts importrender,HttpResponsefrom django.views.decorators.csrf importcsrf_exempt,csrf_protectimportjson#Create your views here.

@csrf_exempt #跳過csrf

defindex(request):print(request.body)

data= (request.body).decode('utf-8') #解碼

dict_data = json.loads(data) #反序列化

print(dict_data,type(dict_data)) #查看類型為dict

print(dict_data.get("user")) #獲取user

return HttpResponse('...')

View Code

再次執行腳本,查看控制臺輸出:

b'{"user": "root", "pwd": 123}'{'pwd': 123, 'user': 'root'} root

ContentType組件

contenttypes 是Django內置的一個應用,可以追蹤項目中所有app和model的對應關系,并記錄在ContentType表中。

ContentTypes做了什么?

當使用django-admin初始化一個django項目的時候,可以看到在默認的INSTALL_APPS已經包含了django.contrib.contenttypes:

INSTALLED_APPS =['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles',

]

而且注意django.contrib.contenttypes是在django.contrib.auth之后,這是因為auth中的permission系統是根據contenttypes來實現的。

接著查閱了一下django.contrib.contenttypes.models文件:

ContractedBlock.gif

ExpandedBlockStart.gif

classContentType(models.Model):

app_label= models.CharField(max_length=100)

model= models.CharField(_('python model class name'), max_length=100)

objects=ContentTypeManager()classMeta:

verbose_name= _('content type')

verbose_name_plural= _('content types')

db_table= 'django_content_type'unique_together= (('app_label', 'model'),)def __str__(self):return self.name

View Code

可以看到ContentType就是一個簡單的django model,而且它在數據庫中的表的名字為django_content_type。

在第一次對Django的model進行migrate之后,就可以發現在數據庫中出現了一張默認生成的名為django_content_type的表。

如果沒有建立任何的model,默認django_content_type是這樣的:

ContractedBlock.gif

ExpandedBlockStart.gif

+----+--------------+--------------+

| id | app_label | model |

+----+--------------+--------------+

| 1 | admin | logentry |

| 2 | auth | group |

| 3 | auth | user |

| 4 | auth | permission |

| 5 | contenttypes | contenttype |

| 6 | sessions | session |

+----+--------------+--------------+

View Code

因此,django_content_type記錄了當前的Django項目中所有model所屬的app(即app_label屬性)以及model的名字(即model屬性)。

當然,django_content_type并不只是記錄屬性這么簡單,contenttypes是對model的一次封裝,因此可以通過contenttypes動態的訪問model類型,而不需要每次import具體的model類型

每當我們創建了新的model并執行數據庫遷移后,ContentType表中就會自動新增一條記錄。比如我在應用app01的models.py中創建表class Electrics(models.Model): pass。從數據庫查看ContentType表,顯示如下:

idapp_labelmodel

admin, auth等內置應用…

5

contenttypes

contenttype

6

app01

electrics

舉例說明

方式1:適用于1張表和另一張表要關聯的時候。

那么這個表有什么作用呢?這里提供一個場景,路飛學城

1.路飛學城表設計

1341090-20180811110357542-482509105.png

2.將2個價格策略表合并1張表。

1341090-20180811110434684-848643395.png

3.如果再加一張表,那價格策略表的表結構會發生改變。 這樣不合理的,我們的表結構一般設計完就不會改變。

1341090-20180811110507162-369225895.png

方式2:適用于1張表和多張表關聯的時候。

4.接下來換一種方式。表名+id 數據庫表結構不會改變。

1341090-20180811110606402-1390809824.png

5.創建一個新項目

1341090-20180811112804043-1447265008.png

6.創建表

修改models.py

ContractedBlock.gif

ExpandedBlockStart.gif

from django.db importmodels#Create your models here.

classCourse(models.Model):"""普通課程"""title= models.CharField(max_length=32)classDegreeCourse(models.Model):"""學位課程"""title= models.CharField(max_length=32)classPricePolicy(models.Model):"""價格策略"""price=models.IntegerField()

period=models.IntegerField()

table_name= models.CharField(verbose_name="關聯的表名稱")

object_id= models.CharField(verbose_name="關聯的表中的數據行ID")

View Code

方式3:ContentType組件

7.settings.py,默認就有contenttypes

1341090-20180811114235442-1676839429.png

8. 使用ContentType組件

修改models.py

主要改動部分如下:

ContractedBlock.gif

ExpandedBlockStart.gif

from django.contrib.contenttypes.fields importGenericForeignKey,GenericRelationfrom django.contrib.contenttypes.models importContentType

content_type= models.ForeignKey(ContentType, verbose_name="關聯的表名稱",on_delete=models.CASCADE)

object_id= models.IntegerField(verbose_name="關聯的表中的數據行ID")

View Code

完整代碼如下:

ContractedBlock.gif

ExpandedBlockStart.gif

from django.db importmodelsfrom django.contrib.contenttypes.fields importGenericForeignKey,GenericRelationfrom django.contrib.contenttypes.models importContentType#Create your models here.

classCourse(models.Model):"""普通課程"""title= models.CharField(max_length=32)classDegreeCourse(models.Model):"""學位課程"""title= models.CharField(max_length=32)classPricePolicy(models.Model):"""價格策略"""price=models.IntegerField()

period=models.IntegerField()

content_type= models.ForeignKey(ContentType, verbose_name="關聯的表名稱",on_delete=models.CASCADE)

object_id= models.IntegerField(verbose_name="關聯的表中的數據行ID")

View Code

9.假設,表數據很多,有個關聯表的名字改了,需要改所有的數據很麻煩。那就再創建一張表,專門存放表名字。

1341090-20180811114503013-259885161.png

10. 第三張表不用自己創建 ContentType 組件已經幫我們創建好了,專門用來存放表名字。

1341090-20180811114522271-1354279319.png

11.生成表,查看表數據

使用2個命令生成表

python manage.py makemigrations

python manage.py migrate

效果如下:

1341090-20180811114555187-1905882850.png

注意:價格策略表,它是萬能的。可以適用于任何課程的價格策略。

contenttypes 應用

這里提供一個場景,網上商城購物時,會有各種各樣的優惠券,比如通用優惠券,滿減券,或者是僅限特定品類的優惠券。在數據庫中,可以通過外鍵將優惠券和不同品類的商品表關聯起來:

ContractedBlock.gif

ExpandedBlockStart.gif

from django.db importmodelsfrom django.contrib.contenttypes.fields importGenericForeignKey,GenericRelationfrom django.contrib.contenttypes.models importContentType#Create your models here.

classElectrics(models.Model):"""id name

1 日立冰箱

2 三星電視

3 小天鵝洗衣機"""name= models.CharField(max_length=32)classFoods(models.Model):"""id name

1 面包

2 烤鴨"""name= models.CharField(max_length=32)classClothes(models.Model):

name= models.CharField(max_length=32)classCoupon(models.Model):"""id name Electrics Foods Clothes more...

1 通用優惠券 null null null

2 冰箱滿減券 2 null null

3 面包狂歡節 null 1 null"""name= models.CharField(max_length=32)

electric_obj= models.ForeignKey(to='Electrics', null=True,on_delete=models.CASCADE)

food_obj= models.ForeignKey(to='Foods', null=True,on_delete=models.CASCADE)

cloth_obj= models.ForeignKey(to='Clothes', null=True,on_delete=models.CASCADE)

View Code

如果是通用優惠券,那么所有的ForeignKey為null,如果僅限某些商品,那么對應商品ForeignKey記錄該商品的id,不相關的記錄為null。但是這樣做是有問題的:實際中商品品類繁多,而且很可能還會持續增加,那么優惠券表中的外鍵將越來越多,但是每條記錄僅使用其中的一個或某幾個外鍵字段。

通過使用contenttypes 應用中提供的特殊字段GenericForeignKey,我們可以很好的解決這個問題。只需要以下三步:

在model中定義ForeignKey字段,并關聯到ContentType表。通常這個字段命名為“content_type”

在model中定義PositiveIntegerField字段,用來存儲關聯表中的主鍵。通常這個字段命名為“object_id”

在model中定義GenericForeignKey字段,傳入上述兩個字段的名字。

為了更方便查詢商品的優惠券,我們還可以在商品類中通過GenericRelation字段定義反向關系。

創建一個新的項目tmall

修改models.py

ContractedBlock.gif

ExpandedBlockStart.gif

from django.db importmodelsfrom django.contrib.contenttypes.models importContentTypefrom django.contrib.contenttypes.fields importGenericForeignKey,GenericRelationclassElectrics(models.Model):

name= models.CharField(max_length=32)

coupons= GenericRelation(to='Coupon') #用于反向查詢,不會生成表字段

def __str__(self):returnself.nameclassFoods(models.Model):

name= models.CharField(max_length=32)

coupons= GenericRelation(to='Coupon')def __str__(self):returnself.nameclassClothes(models.Model):

name= models.CharField(max_length=32)

coupons= GenericRelation(to='Coupon')def __str__(self):returnself.nameclassCoupon(models.Model):

name= models.CharField(max_length=32)

content_type= models.ForeignKey(ContentType,on_delete=models.CASCADE,default=None) #step 1

object_id = models.PositiveIntegerField(default=None) #step 2

content_object = GenericForeignKey('content_type', 'object_id') #step 3

def __str__(self):returnself.nameclassOftenAskedQuestion(models.Model):"""常見問題"""content_type= models.ForeignKey(ContentType,on_delete=models.CASCADE,default=None)

object_id= models.PositiveIntegerField(default=None)

content_object= GenericForeignKey('content_type', 'object_id')

question= models.CharField(max_length=255)

answer= models.TextField(max_length=1024)

View Code

使用2個命令,生成表

python manage.py makemigrations

python manage.py migrate

創建記錄和查詢

使用navicat打開sqllite數據庫,執行以下sq

ContractedBlock.gif

ExpandedBlockStart.gif

INSERT INTO app01_coupon ("id", "name", "content_type_id", "object_id") VALUES (1, '通用優惠券', 7, 2);

INSERT INTO app01_coupon ("id", "name", "content_type_id", "object_id") VALUES (2, '冰箱滿減券', 7, 1);

INSERT INTO app01_coupon ("id", "name", "content_type_id", "object_id") VALUES (3, '面包狂歡節', 8, 1);

INSERT INTO app01_electrics ("id", "name") VALUES (1, '日立冰箱');

INSERT INTO app01_electrics ("id", "name") VALUES (2, '三星電視');

INSERT INTO app01_electrics ("id", "name") VALUES (3, '小天鵝洗衣機');

INSERT INTO app01_foods ("id", "name") VALUES (1, '面包');

INSERT INTO app01_foods ("id", "name") VALUES (2, '烤鴨');

INSERT INTO app01_oftenaskedquestion ("id", "object_id", "question", "answer", "content_type_id") VALUES (1, 1, '多少錢', 10, 7);

View Code

解釋一下,electrics和oftenaskedquestion是如何關聯的

先來看django_content_type表記錄,electrics表對應的主鍵id為7

1341090-20180811190050508-2100940821.png

再看看oftenaskedquestion表記錄

1341090-20180811190231034-2090184058.png

object_id表示electrics表的主鍵id=1的記錄

question,表示問題

answer,表示答案

content_type_id,表示electrics表在django_content_type表中的主鍵id

通過最后一條sql語句,electrics表和oftenaskedquestion表,就建立了一條關聯記錄!

修改urls.py

ContractedBlock.gif

ExpandedBlockStart.gif

from django.contrib importadminfrom django.urls importpathfrom app01 importviews

urlpatterns=[

path('admin/', admin.site.urls),

path('test/', views.test),

]

View Code

修改views.py

ContractedBlock.gif

ExpandedBlockStart.gif

from django.shortcuts importrender, HttpResponsefrom app01 importmodelsfrom django.contrib.contenttypes.models importContentTypedeftest(request):if request.method == 'GET':#ContentType表對象有model_class() 方法,取到對應model

content = ContentType.objects.filter(app_label='app01', model='electrics').first() #表名小寫

cloth_class = content.model_class() #cloth_class 就相當于models.Electrics

res =cloth_class.objects.all()print(res)#為三星電視(id=2)創建一條優惠記錄

s_tv = models.Electrics.objects.filter(id=2).first()print(s_tv)#models.Coupon.objects.create(name='電視優惠券', content_object=s_tv)

#查詢優惠券(id=1)綁定了哪些商品

coupon_obj = models.Coupon.objects.filter(id=1).first()

prod=coupon_obj.content_objectprint(prod)# #查詢三星電視(id=2)的所有優惠券

res =s_tv.coupons.all()print(res)# #查詢obj的所有優惠券:如果沒有定義反向查詢字段,通過如下方式:

content = ContentType.objects.filter(app_label='app01', model='electrics').first()print(content)

res= models.OftenAskedQuestion.objects.filter(content_type=content, object_id=coupon_obj.pk).all()print(res)return HttpResponse('....')

View Code

啟動項目,訪問頁面

1341090-20180811163643500-850173417.png

查看Pycharm控制臺輸出:

, , ]>三星電視

三星電視]>electrics]>

總結:

當一張表和多個表FK關聯,并且多個FK中只能選擇其中一個或其中n個時,可以利用contenttypes app,只需定義三個字段就搞定!

一、路飛學城表結構

創建項目luffycity,應用名為api。注意:django版本為1.11

修改models.py

ContractedBlock.gif

ExpandedBlockStart.gif

from django.contrib.contenttypes.fields importGenericForeignKey, GenericRelationfrom django.contrib.contenttypes.models importContentTypefrom django.db.models importQfrom django.utils.safestring importmark_safefrom django.db importmodelsimporthashlib######################### 課程相關 ########################

classCourseCategory(models.Model):"""課程大類, e.g 前端 后端..."""name= models.CharField(max_length=64, unique=True)def __str__(self):return "%s" %self.nameclassMeta:

verbose_name_plural= "01.課程大類"

classCourseSubCategory(models.Model):"""課程子類, e.g python linux"""category= models.ForeignKey("CourseCategory")

name= models.CharField(max_length=64, unique=True)def __str__(self):return "%s" %self.nameclassMeta:

verbose_name_plural= "02.課程子類"

classDegreeCourse(models.Model):"""學位課程"""name= models.CharField(max_length=128, unique=True)

course_img= models.CharField(max_length=255, verbose_name="縮略圖")

brief= models.TextField(verbose_name="學位課程簡介", )

total_scholarship= models.PositiveIntegerField(verbose_name="總獎學金(貝里)", default=40000) #2000 2000

mentor_compensation_bonus = models.PositiveIntegerField(verbose_name="本課程的導師輔導費用(貝里)", default=15000)

period= models.PositiveIntegerField(verbose_name="建議學習周期(days)", default=150) #為了計算學位獎學金

prerequisite = models.TextField(verbose_name="課程先修要求", max_length=1024)

teachers= models.ManyToManyField("Teacher", verbose_name="課程講師")#用于GenericForeignKey反向查詢, 不會生成表字段,切勿刪除

#coupon = GenericRelation("Coupon")

#用于GenericForeignKey反向查詢,不會生成表字段,切勿刪除

degreecourse_price_policy = GenericRelation("PricePolicy")def __str__(self):returnself.nameclassMeta:

verbose_name_plural= "03.學位課"

classTeacher(models.Model):"""講師、導師表"""name= models.CharField(max_length=32)

role_choices= ((0, '講師'), (1, '導師'))

role= models.SmallIntegerField(choices=role_choices, default=0)

title= models.CharField(max_length=64, verbose_name="職位、職稱")

signature= models.CharField(max_length=255, help_text="導師簽名", blank=True, null=True)

image= models.CharField(max_length=128)

brief= models.TextField(max_length=1024)def __str__(self):returnself.nameclassMeta:

verbose_name_plural= "04.導師或講師"

classScholarship(models.Model):"""學位課程獎學金"""degree_course= models.ForeignKey("DegreeCourse")

time_percent= models.PositiveSmallIntegerField(verbose_name="獎勵檔位(時間百分比)", help_text="只填百分值,如80,代表80%")

value= models.PositiveIntegerField(verbose_name="獎學金數額")def __str__(self):return "%s:%s" %(self.degree_course, self.value)classMeta:

verbose_name_plural= "05.學位課獎學金"

classCourse(models.Model):"""專題課/學位課模塊表"""name= models.CharField(max_length=128, unique=True)

course_img= models.CharField(max_length=255)

sub_category= models.ForeignKey("CourseSubCategory")

course_type_choices= ((0, '付費'), (1, 'VIP專享'), (2, '學位課程'))

course_type= models.SmallIntegerField(choices=course_type_choices)#不為空;學位課的某個模塊

#為空;專題課

degree_course = models.ForeignKey("DegreeCourse", blank=True, null=True, help_text="若是學位課程,此處關聯學位表")

brief= models.TextField(verbose_name="課程概述", max_length=2048)

level_choices= ((0, '初級'), (1, '中級'), (2, '高級'))

level= models.SmallIntegerField(choices=level_choices, default=1)

pub_date= models.DateField(verbose_name="發布日期", blank=True, null=True)

period= models.PositiveIntegerField(verbose_name="建議學習周期(days)", default=7) # order = models.IntegerField("課程順序", help_text="從上一個課程數字往后排")

attachment_path= models.CharField(max_length=128, verbose_name="課件路徑", blank=True, null=True)

status_choices= ((0, '上線'), (1, '下線'), (2, '預上線'))

status= models.SmallIntegerField(choices=status_choices, default=0)

template_id= models.SmallIntegerField("前端模板id", default=1)#coupon = GenericRelation("Coupon")

#用于GenericForeignKey反向查詢,不會生成表字段,切勿刪除

price_policy = GenericRelation("PricePolicy")

asked_question= GenericRelation("OftenAskedQuestion")def __str__(self):return "%s(%s)" %(self.name, self.get_course_type_display())def save(self, *args, **kwargs):if self.course_type == 2:if notself.degree_course:raise ValueError("學位課程必須關聯對應的學位表")

super(Course, self).save(*args, **kwargs)classMeta:

verbose_name_plural= "06.專題課或學位課模塊"

classCourseDetail(models.Model):"""課程詳情頁內容"""course= models.OneToOneField("Course")

hours= models.IntegerField("課時")

course_slogan= models.CharField(max_length=125, blank=True, null=True)

video_brief_link= models.CharField(verbose_name='課程介紹', max_length=255, blank=True, null=True)

why_study= models.TextField(verbose_name="為什么學習這門課程")

what_to_study_brief= models.TextField(verbose_name="我將學到哪些內容")

career_improvement= models.TextField(verbose_name="此項目如何有助于我的職業生涯")

prerequisite= models.TextField(verbose_name="課程先修要求", max_length=1024)

recommend_courses= models.ManyToManyField("Course", related_name="recommend_by", blank=True)

teachers= models.ManyToManyField("Teacher", verbose_name="課程講師")def __str__(self):return "%s" %self.courseclassMeta:

verbose_name_plural= "07.課程或學位模塊詳細"

classOftenAskedQuestion(models.Model):"""常見問題"""content_type= models.ForeignKey(ContentType) #關聯course or degree_course

object_id =models.PositiveIntegerField()

content_object= GenericForeignKey('content_type', 'object_id')

question= models.CharField(max_length=255)

answer= models.TextField(max_length=1024)def __str__(self):return "%s-%s" %(self.content_object, self.question)classMeta:

unique_together= ('content_type', 'object_id', 'question')

verbose_name_plural= "08. 常見問題"

classCourseOutline(models.Model):"""課程大綱"""course_detail= models.ForeignKey("CourseDetail")

title= models.CharField(max_length=128)#前端顯示順序

order = models.PositiveSmallIntegerField(default=1)

content= models.TextField("內容", max_length=2048)def __str__(self):return "%s" %self.titleclassMeta:

unique_together= ('course_detail', 'title')

verbose_name_plural= "09. 課程大綱"

classCourseChapter(models.Model):"""課程章節"""course= models.ForeignKey("Course", related_name='coursechapters')

chapter= models.SmallIntegerField(verbose_name="第幾章", default=1)

name= models.CharField(max_length=128)

summary= models.TextField(verbose_name="章節介紹", blank=True, null=True)

pub_date= models.DateField(verbose_name="發布日期", auto_now_add=True)classMeta:

unique_together= ("course", 'chapter')

verbose_name_plural= "10. 課程章節"

def __str__(self):return "%s:(第%s章)%s" %(self.course, self.chapter, self.name)classCourseSection(models.Model):"""課時目錄"""chapter= models.ForeignKey("CourseChapter", related_name='coursesections')

name= models.CharField(max_length=128)

order= models.PositiveSmallIntegerField(verbose_name="課時排序", help_text="建議每個課時之間空1至2個值,以備后續插入課時")

section_type_choices= ((0, '文檔'), (1, '練習'), (2, '視頻'))

section_type= models.SmallIntegerField(default=2, choices=section_type_choices)

section_link= models.CharField(max_length=255, blank=True, null=True, help_text="若是video,填vid,若是文檔,填link")

video_time= models.CharField(verbose_name="視頻時長", blank=True, null=True, max_length=32) #僅在前端展示使用

pub_date = models.DateTimeField(verbose_name="發布時間", auto_now_add=True)

free_trail= models.BooleanField("是否可試看", default=False)classMeta:

unique_together= ('chapter', 'section_link')

verbose_name_plural= "11. 課時"

def __str__(self):return "%s-%s" %(self.chapter, self.name)classHomework(models.Model):

chapter= models.ForeignKey("CourseChapter")

title= models.CharField(max_length=128, verbose_name="作業題目")

order= models.PositiveSmallIntegerField("作業順序", help_text="同一課程的每個作業之前的order值間隔1-2個數")

homework_type_choices= ((0, '作業'), (1, '模塊通關考核'))

homework_type= models.SmallIntegerField(choices=homework_type_choices, default=0)

requirement= models.TextField(max_length=1024, verbose_name="作業需求")

threshold= models.TextField(max_length=1024, verbose_name="踩分點")

recommend_period= models.PositiveSmallIntegerField("推薦完成周期(天)", default=7)

scholarship_value= models.PositiveSmallIntegerField("為該作業分配的獎學金(貝里)")

note= models.TextField(blank=True, null=True)

enabled= models.BooleanField(default=True, help_text="本作業如果后期不需要了,不想讓學員看到,可以設置為False")classMeta:

unique_together= ("chapter", "title")

verbose_name_plural= "12. 章節作業"

def __str__(self):return "%s - %s" %(self.chapter, self.title)#class CourseReview(models.Model):#"""課程評價"""#enrolled_course = models.OneToOneField("EnrolledCourse")#about_teacher = models.FloatField(default=0, verbose_name="講師講解是否清晰")#about_video = models.FloatField(default=0, verbose_name="內容實用")#about_course = models.FloatField(default=0, verbose_name="課程內容通俗易懂")#review = models.TextField(max_length=1024, verbose_name="評價")#disagree_number = models.IntegerField(default=0, verbose_name="踩")#agree_number = models.IntegerField(default=0, verbose_name="贊同數")#tags = models.ManyToManyField("Tags", blank=True, verbose_name="標簽")#date = models.DateTimeField(auto_now_add=True, verbose_name="評價日期")#is_recommend = models.BooleanField("熱評推薦", default=False)#hide = models.BooleanField("不在前端頁面顯示此條評價", default=False)#

#def __str__(self):#return "%s-%s" % (self.enrolled_course.course, self.review)#

#class Meta:#verbose_name_plural = "13. 課程評價(購買課程后才能評價)"#

#

#class DegreeCourseReview(models.Model):#"""學位課程評價#為了以后可以定制單獨的評價內容,所以不與普通課程的評價混在一起,單獨建表#"""#enrolled_course = models.ForeignKey("EnrolledDegreeCourse")#course = models.ForeignKey("Course", verbose_name="評價學位模塊", blank=True, null=True,#help_text="不填寫即代表評價整個學位課程", limit_choices_to={'course_type': 2})#about_teacher = models.FloatField(default=0, verbose_name="講師講解是否清晰")#about_video = models.FloatField(default=0, verbose_name="視頻質量")#about_course = models.FloatField(default=0, verbose_name="課程")#review = models.TextField(max_length=1024, verbose_name="評價")#disagree_number = models.IntegerField(default=0, verbose_name="踩")#agree_number = models.IntegerField(default=0, verbose_name="贊同數")#tags = models.ManyToManyField("Tags", blank=True, verbose_name="標簽")#date = models.DateTimeField(auto_now_add=True, verbose_name="評價日期")#is_recommend = models.BooleanField("熱評推薦", default=False)#hide = models.BooleanField("不在前端頁面顯示此條評價", default=False)#

#def __str__(self):#return "%s-%s" % (self.enrolled_course, self.review)#

#class Meta:#verbose_name_plural = "14. 學位課評價(購買課程后才能評價)"

classPricePolicy(models.Model):"""價格與有課程效期表"""content_type= models.ForeignKey(ContentType) #關聯course or degree_course

object_id =models.PositiveIntegerField()

content_object= GenericForeignKey('content_type', 'object_id')#course = models.ForeignKey("Course")

valid_period_choices = ((1, '1天'), (3, '3天'),

(7, '1周'), (14, '2周'),

(30, '1個月'),

(60, '2個月'),

(90, '3個月'),

(180, '6個月'), (210, '12個月'),

(540, '18個月'), (720, '24個月'),

)

valid_period= models.SmallIntegerField(choices=valid_period_choices)

price=models.FloatField()classMeta:

unique_together= ("content_type", 'object_id', "valid_period")

verbose_name_plural= "15. 價格策略"

def __str__(self):return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)

View Code

關鍵字解釋:

Course表

Course表里面有price_policy屬性,里面定義了GenericRelation,它是用來做反向查詢的

表示它會從django_content_type表中找到model字段為pricepolicy(價格策略表)

PricePolicy表

content_type,約定俗成叫這個命令,它和django_content_type表做了外鍵關聯。ContentType表示django_content_type表

object_id 表示關聯表中的主鍵。通常這個字段命名為“object_id”。

content_object 傳入上述兩個字段的名字,用來建立外鍵關系。

Course表,假設id=1,課程名為Python全棧。要加一條價格策略,比如7天30塊

查看django_content_type表,course的id為8

1341090-20180811173626544-2083953488.png

那么api_pricepolicy表的記錄應該這么寫

1341090-20180811173745538-441007254.png

object_id,表示關聯表的主鍵id,也就是course表的id=1的記錄

vaild_period,表示有效期

price,表示價格

content_type_id,表示django_content_type表中主鍵id。8就表示course表

通過這樣,2個表,就建立了一條關系!

django_content_type對所有表做了外鍵,所以它記錄了所有表名!

假設,需要查詢Course表中id=1的課程所有價格策略,可以這么查詢

obj = models.Course.objects.filter(id=1).first()print(i.price_policy.all())

all()表示查詢所有記錄

假設,查詢所有課程的價格策略

obj =models.Course.objects.all()for i inobj:print(i.price_policy.all())

顯示課程列表,并打印每個課程的所有價格信息

obj =models.Course.objects.all()for i inobj:print(i.name)

ps=i.price_policy.all()for p inps:print('-->',p.valid_period,p.price)

執行之后,效果應該是這樣的

Python開發入門7天特訓營--> 7 10.0

--> 30 50.0Linux系統基礎5周入門精講--> 14 20.0

--> 60 80.0

Course表里面有專題課和學位課。

專題課,可以單獨售賣。

Course里面的degree_course。為空,表示專題課。否則為學位課!

判斷為空,使用如下語句

models.Course.objects.filter(degree_course__isnull=True)

表結構圖關系圖如下:

1341090-20180811165757443-1119929567.png

總結:

ContractedBlock.gif

ExpandedBlockStart.gif

1. django contenttypes組件的作用?-數據庫遷移時,找到所有app中的表并錄入到django_content_type表-內置兩個字段:

GenericRelation

GenericForeignKey2. 使用contenttypes組件3. 路飛表結構

a. 課程分類-課程大類-課程子類

b. 學位課-學位課-獎學金-老師

c. 專題課or學位課模塊- 專題課 or學位課模塊-課程詳細-課程大綱-常見問題-章節-課時-作業

d. 價格- 價格策略

View Code

今日作業:

ContractedBlock.gif

ExpandedBlockStart.gif

一、準備工作:1. 通過admin對13張表錄入數據2. ORM練習

a. 查看所有學位課并打印學位課名稱以及授課老師

b. 查看所有學位課并打印學位課名稱以及學位課的獎學金

c. 展示所有的專題課

models.Course.objects.filter(degree_course__isnull=True)

d. 查看id=1的學位課對應的所有模塊名稱

e. 獲取id=1的專題課,并打印:課程名、級別(中文)、why_study、what_to_study_brief、所有recommend_courses

f. 獲取id=1的專題課,并打印該課程相關的所有常見問題

g. 獲取id=1的專題課,并打印該課程相關的課程大綱

h. 獲取id=1的專題課,并打印該課程相關的所有章節

i. 獲取id=1的專題課,并打印該課程相關的所有課時

第1章·Python 介紹、基礎語法、流程控制01-課程介紹(一)01-課程介紹(一)01-課程介紹(一)01-課程介紹(一)01-課程介紹(一)

第1章·Python 介紹、基礎語法、流程控制01-課程介紹(一)01-課程介紹(一)01-課程介紹(一)01-課程介紹(一)01-課程介紹(一)

i. 獲取id=1的專題課,并打印該課程相關的所有的價格策略

二、基于django rest framework 寫路飛的接口(作業一+rest framework 序列化)-課程列表API- 課程詳細API

View Code

答案

一、ORM查詢

ContractedBlock.gif

ExpandedBlockStart.gif

a. 查看所有學位課并打印學位課名稱以及授課老師

obj= models.DegreeCourse.objects.values("name","teachers__name")print(obj)

b. 查看所有學位課并打印學位課名稱以及學位課的獎學金

obj= models.DegreeCourse.objects.all().values("name","total_scholarship")print(obj)

c. 展示所有的專題課

obj= models.Course.objects.filter(degree_course__isnull=True)

d. 查看id=1的學位課對應的所有模塊名稱

obj= models.Course.objects.filter(id=1).values("name","degree_course")

e. 獲取id=1的專題課,并打印:課程名、級別(中文)、why_study、what_to_study_brief、所有recommend_courses

obj= models.Course.objects.filter(degree_course__isnull=True,id=1).values("name","level","coursedetail__why_study","coursedetail__what_to_study_brief","coursedetail__recommend_courses")

f. 獲取id=1的專題課,并打印該課程相關的所有常見問題

obj= models.Course.objects.filter(id=1)for i inobj:print(i.asked_question.all())

g. 獲取id=1的專題課,并打印該課程相關的課程大綱

obj= models.CourseDetail.objects.filter(id=1).values("courseoutline__title")print(obj)

h. 獲取id=1的專題課,并打印該課程相關的所有章節

obj= models.Course.objects.filter(id=1).values("coursechapters__name")print(obj)

i. 獲取id=1的專題課,并打印該課程相關的所有的價格策略

obj= models.Course.objects.filter(id=1).values("coursechapters__name","coursechapters__coursesections__name")print(obj)

View Code

二、api

github下載地址

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/184584.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息