昨日內容回顧
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天退款,也是為了避免刷量。
導師后臺
主要給導師使用的,賬戶金額,學員跟進...
管理后臺
主要是運營使用,用來上傳視頻,編輯課程相關信息...
開發人員
主站:-前端:- 前端姑娘 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 方式提交數據。請求類似于下面這樣(無關的請求頭在本文中都省略掉了)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。直接來看一個請求示例: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
from django.contrib importadminfrom django.urls importpathfrom app01 importviews
urlpatterns=[
path('admin/', admin.site.urls),
path('index/', views.index),
]
View Code
修改views.py
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
啟動項目,訪問首頁
這個時候,boss,發了一個post請求,給index
新建一個boss.py,放在項目根目錄中
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,才會解析數據
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
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,需要對數據進行解碼
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文件:
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是這樣的:
+----+--------------+--------------+
| 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.路飛學城表設計
2.將2個價格策略表合并1張表。
3.如果再加一張表,那價格策略表的表結構會發生改變。 這樣不合理的,我們的表結構一般設計完就不會改變。
方式2:適用于1張表和多張表關聯的時候。
4.接下來換一種方式。表名+id 數據庫表結構不會改變。
5.創建一個新項目
6.創建表
修改models.py
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
8. 使用ContentType組件
修改models.py
主要改動部分如下:
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
完整代碼如下:
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.假設,表數據很多,有個關聯表的名字改了,需要改所有的數據很麻煩。那就再創建一張表,專門存放表名字。
10. 第三張表不用自己創建 ContentType 組件已經幫我們創建好了,專門用來存放表名字。
11.生成表,查看表數據
使用2個命令生成表
python manage.py makemigrations
python manage.py migrate
效果如下:
注意:價格策略表,它是萬能的。可以適用于任何課程的價格策略。
contenttypes 應用
這里提供一個場景,網上商城購物時,會有各種各樣的優惠券,比如通用優惠券,滿減券,或者是僅限特定品類的優惠券。在數據庫中,可以通過外鍵將優惠券和不同品類的商品表關聯起來:
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
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
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
再看看oftenaskedquestion表記錄
object_id表示electrics表的主鍵id=1的記錄
question,表示問題
answer,表示答案
content_type_id,表示electrics表在django_content_type表中的主鍵id
通過最后一條sql語句,electrics表和oftenaskedquestion表,就建立了一條關聯記錄!
修改urls.py
from django.contrib importadminfrom django.urls importpathfrom app01 importviews
urlpatterns=[
path('admin/', admin.site.urls),
path('test/', views.test),
]
View Code
修改views.py
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
啟動項目,訪問頁面
查看Pycharm控制臺輸出:
, , ]>三星電視
三星電視]>electrics]>
總結:
當一張表和多個表FK關聯,并且多個FK中只能選擇其中一個或其中n個時,可以利用contenttypes app,只需定義三個字段就搞定!
一、路飛學城表結構
創建項目luffycity,應用名為api。注意:django版本為1.11
修改models.py
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
那么api_pricepolicy表的記錄應該這么寫
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)
表結構圖關系圖如下:
總結:
1. django contenttypes組件的作用?-數據庫遷移時,找到所有app中的表并錄入到django_content_type表-內置兩個字段:
GenericRelation
GenericForeignKey2. 使用contenttypes組件3. 路飛表結構
a. 課程分類-課程大類-課程子類
b. 學位課-學位課-獎學金-老師
c. 專題課or學位課模塊- 專題課 or學位課模塊-課程詳細-課程大綱-常見問題-章節-課時-作業
d. 價格- 價格策略
View Code
今日作業:
一、準備工作: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查詢
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下載地址
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态