本節(jié)內(nèi)容 ?
? ? 引子講django的models之前, 先來(lái)想一想, 讓你通過(guò)django操作數(shù)據(jù)庫(kù),你怎么做? 做苦思冥想,可能會(huì)這樣寫。
? 很方便就實(shí)現(xiàn)了從數(shù)據(jù)庫(kù)里取數(shù)據(jù),事實(shí)上,很多人確實(shí)就是這么做的。但這樣做會(huì)帶來(lái)2個(gè)問(wèn)題
? 那怎么辦呢?ORM提供了新思路。 什么是ORM呢?
上面的解釋有點(diǎn)蒙蔽對(duì)不?其實(shí)你只需要抓住2個(gè)關(guān)鍵詞, “映射” 和 “對(duì)象”,就能知道orm是什么干什么的了。
映射看下面的圖,就是直觀的例子,把右邊的表結(jié)構(gòu)映射成了左邊的類 ?Sql語(yǔ)句到對(duì)象ORM可以使你不用再寫原生SQL, 而是像操作對(duì)象一樣就可以實(shí)現(xiàn)對(duì)表里數(shù)據(jù)的增刪改查 ? 好棒棒,媽媽再也不用逼你寫原生sql啦! 但是不要開(kāi)心太早,ORM確實(shí)提高了開(kāi)發(fā)效率,并且降低了數(shù)據(jù)操作與代碼之間的耦合,不過(guò)有利就有弊,我們總結(jié)一下orm的優(yōu)缺點(diǎn)。 優(yōu)點(diǎn):
缺點(diǎn):
? 講Django為什么說(shuō)ORM? 哈,? 好啦,是時(shí)候該引出主角啦,因?yàn)镈jango的models基于架構(gòu)ORM實(shí)現(xiàn)的。 Models模型Django 的models把數(shù)據(jù)庫(kù)表結(jié)構(gòu)映射成了一個(gè)個(gè)的類, 表里的每個(gè)字段就是類的屬性。我們都知道數(shù)據(jù)庫(kù)有很多字段類型,int,float,char等, Django的models類針對(duì)不同的字段也設(shè)置了不同的類屬性。
? 除了普通的表字段,針對(duì)外鍵也有映射
好啦,接下來(lái)就用django的orm來(lái)設(shè)計(jì)一個(gè)博客表。 需求
根據(jù)需求,我們?cè)O(shè)計(jì)3張表 注意Article表和Tag表是屬于多對(duì)多關(guān)系,什么是多對(duì)多?即一個(gè)文章有多個(gè)標(biāo)簽,一個(gè)標(biāo)簽又可以屬于多個(gè)文章。? 比如上圖的Article表中id為3的文章 ,它的標(biāo)簽是4,26, 即投資、大文娛、社交, 你看“投資”這個(gè)標(biāo)簽同時(shí)還屬于文章2。 這就是多對(duì)多關(guān)系 , 即many to many .? 那這種多對(duì)多的關(guān)系如何在表中存儲(chǔ)呢?難道真的像上圖中一樣,在Article表中加個(gè)tags字段,關(guān)聯(lián)Tag表里的多條數(shù)據(jù),通過(guò)逗號(hào)區(qū)分? 這倒確實(shí)是個(gè)解決辦法。但是也有問(wèn)題,一個(gè)字段里存多條紀(jì)錄的id,就沒(méi)辦法做查詢優(yōu)化了。比如不能做索引等。 所以若想實(shí)現(xiàn)多對(duì)多關(guān)系的高效存儲(chǔ) 查詢優(yōu)化,可以在Article and Tag表之間再搞出一張表。 這樣是不是就實(shí)現(xiàn)了多對(duì)多關(guān)聯(lián)? yes, 沒(méi)錯(cuò), django也是這么做的, django 有個(gè)專門的字段,叫ManyToManyField, 就是用來(lái)實(shí)現(xiàn)多對(duì)多關(guān)聯(lián)的,它會(huì)自動(dòng)生成一個(gè)如上圖一樣的第3張表來(lái)存儲(chǔ)多對(duì)多關(guān)系。 ? 正式的表結(jié)構(gòu)
我們發(fā)現(xiàn),每個(gè)字段其實(shí)都是一個(gè)獨(dú)立的對(duì)象,一張表其實(shí)是很多類的組合。 上面好多字段里還跟了些參數(shù),我們來(lái)看以下常用的:
? 還有幾個(gè)特殊的字段屬性需要單獨(dú)介紹下 ? choices An iterable (e.g., a list or tuple) consisting itself of iterables of exactly two items (e.g. [(A, B), (A, B) ...]) to use as choices for this field. The first element in each tuple is the actual value to be set on the model, and the second element is the human-readable name.
? ForeignKey.on_delete 當(dāng)一條記錄關(guān)聯(lián)的外鍵紀(jì)錄被刪除時(shí),django 也會(huì)根據(jù)外鍵關(guān)聯(lián)限制的配置來(lái)決定如何處理當(dāng)前這條紀(jì)錄。舉例,如果你有個(gè)可以為null的外鍵關(guān)聯(lián),并且你想在本紀(jì)錄關(guān)聯(lián)的數(shù)據(jù)被刪除時(shí),把當(dāng)前紀(jì)錄的關(guān)聯(lián)字段設(shè)為null,那就配置如下
這個(gè)on_delete就是決定在關(guān)聯(lián)對(duì)象被刪除時(shí),如何處理當(dāng)前紀(jì)錄的,常用的參數(shù)如下:
? 配置Django數(shù)據(jù)庫(kù)連接信息Django支持多種數(shù)據(jù)庫(kù),Sqlite、Mysql、Oracle、PostgreSQL,默認(rèn)的是小型文件數(shù)據(jù)庫(kù)Sqlite
? 咱們是干大事的人,怎么也得用個(gè)Mysql呀, 改成mysql 也so easy.
? 不過(guò)注意,python3 連接mysql的得使用pymysql,MysqlDB模塊300年沒(méi)更新了,但django默認(rèn)調(diào)用的還是MySQLdb, so pymysql有個(gè)功能可以讓django以為是用了MySQLdb. 即在項(xiàng)目目錄下的__init__.py中加上句代碼就好
? 不加的話,一會(huì)連接數(shù)據(jù)時(shí)會(huì)報(bào)錯(cuò)噢 。 ? 同步數(shù)據(jù)庫(kù)你在ORM定義的表結(jié)構(gòu)如何同步到真實(shí)的數(shù)據(jù)庫(kù)里呢? 只需2條命令。但django只能幫你自動(dòng)創(chuàng)建表,數(shù)據(jù)庫(kù)本身還是得你自己來(lái)。
好了,可以同步了,說(shuō)好只需2步。 1. 生成同步文件,?django自帶一個(gè)專門的工具叫migrations, 負(fù)責(zé)把你的orm表轉(zhuǎn)成實(shí)際的表結(jié)構(gòu),它不旦可以幫自動(dòng)創(chuàng)建表,對(duì)表結(jié)構(gòu)的修改,比如增刪改字段、改字段屬性等也都能自動(dòng)同步。只需通過(guò)下面神奇的命令。
?不出意外的話,會(huì)顯示類似以下信息
? 此時(shí)你會(huì)發(fā)現(xiàn),你的app下的migrations目錄里多了一個(gè)0001_initial.py的文件 ,這個(gè)文件就是因?yàn)槟氵@條命令而創(chuàng)建的,migrations工具就會(huì)根據(jù)這個(gè)文件來(lái)創(chuàng)建數(shù)據(jù)庫(kù)里的表。 2. 同步到數(shù)據(jù)
? 此時(shí)登錄你的數(shù)據(jù)庫(kù),會(huì)發(fā)現(xiàn)創(chuàng)建了好多張表
好啦,表結(jié)構(gòu)也有了,我們可以往里面插數(shù)據(jù)啦。 之前說(shuō)好的是可以不用SQL語(yǔ)句的,一點(diǎn)不騙你。 ? 用orm對(duì)表數(shù)據(jù)進(jìn)行增刪改查先進(jìn)入已經(jīng)連接好數(shù)據(jù)庫(kù)的django python環(huán)境
創(chuàng)建 創(chuàng)建數(shù)據(jù)簡(jiǎn)單的令人發(fā)指 ? ? ? 查 filter 支持很多的過(guò)濾條件,我們來(lái)看下: ? ? contains 包含,相當(dāng)于sql的like條件
SQL equivalent:
Note this will match the headline 'Lennon honored today' but not 'lennon honored today'. icontains? 大小寫不敏感
in In a given iterable; often a list, tuple, or queryset.
SQL equivalent:
You can also use a queryset to dynamically evaluate the list of values instead of providing a list of literal values:
This queryset will be evaluated as subselect statement:
gt
SQL equivalent:
gte lt lte startswith
SQL equivalent:
SQLite doesn’t support case-sensitive LIKE statements; startswith acts like istartswith for SQLite istartswith endswith iendswith
range
SQL equivalent:
date For datetime fields, casts the value as date. Allows chaining additional field lookups. Takes a date value.
year
SQL equivalent:
When USE_TZ is True, datetime fields are converted to the current time zone before filtering. 簡(jiǎn)單解決辦法是把USE_TZ=False month
When? SQL equivalent:
day
SQL equivalent:
New in Django 1.11.
|
1 2 |
Entry.objects. filter (pub_date__week = 52 )
Entry.objects. filter (pub_date__week__gte = 32 , pub_date__week__lte = 38 )
|
week_day
For date and datetime fields, a ‘day of the week’ match. Allows chaining additional field lookups.
Takes an integer value representing the day of week from 1 (Sunday) to 7 (Saturday).
Example:
1 2 |
Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)
|
hour
For datetime and time fields, an exact hour match. Allows chaining additional field lookups. Takes an integer between 0 and 23.
Example:
1 2 3 |
Event.objects. filter (timestamp__hour = 23 )
Event.objects. filter (time__hour = 5 )
Event.objects. filter (timestamp__hour__gte = 12 )
|
SQL equivalent:
1 2 3 |
SELECT ?...? WHERE ?EXTRACT( 'hour' ?FROM ?timestamp ) =? '23' ;
SELECT ?...? WHERE ?EXTRACT( 'hour' ?FROM ?time ) =? '5' ;
SELECT ?...? WHERE ?EXTRACT( 'hour' ?FROM ?timestamp ) >=? '12' ;同
|
同時(shí),還支持mintue,second
1 2 3 4 |
Event.objects.filter(time__minute=46)
?
?
Event.objects.filter(timestamp__second=31)
|
isnull
Takes either?True
?or?False
, which correspond to SQL queries of?IS?NULL
?and?IS?NOT?NULL
, respectively.
Example:
1 |
Entry.objects. filter (pub_date__isnull = True )
|
SQL equivalent:
1 |
SELECT ?...? WHERE ?pub_date? IS ?NULL ;
|
regex
Case-sensitive regular expression match.
Example:
1 |
Entry.objects.get(title__regex = r '^(An?|The) ' )
|
SQL equivalents:
1 2 3 4 5 6 7 |
SELECT ?...? WHERE ?title REGEXP? BINARY ?'^(An?|The) ' ;? -- MySQL
?
SELECT ?...? WHERE ?REGEXP_LIKE(title,? '^(An?|The) ' ,? 'c' );? -- Oracle
?
SELECT ?...? WHERE ?title ~? '^(An?|The) ' ;? -- PostgreSQL
?
SELECT ?...? WHERE ?title REGEXP? '^(An?|The) ' ;? -- SQLite
|
iregex?大小寫不敏感
改刪
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 批量修改
models.Account.objects. filter (username = 'elina' ).update(password = "Luffy#21" )
?
# 單條修改
obj? = ?models.Account.objects.get(username = 'linux' )
obj.username? = ?'python'
obj.save()
?
?
# 批量刪除
models.User.objects.get(password = 'oldboy' ).delete()
?
# 單條刪除
obj? = ?models.User.objects.get( id = 3 )
obj.delete()
|
?
?
values()
Returns a?QuerySet
?that returns dictionaries, rather than model instances, when used as an iterable.
1 2 3 4 |
>>> Blog.objects.values()
<QuerySet [{ 'id' :? 1 ,? 'name' :? 'Beatles Blog' ,? 'tagline' :? 'All the latest Beatles news.' }]>
>>> Blog.objects.values( 'id' ,? 'name' )
<QuerySet [{ 'id' :? 1 ,? 'name' :? 'Beatles Blog' }]>
|
order_by()
By default, results returned by a?QuerySet
?are ordered by the ordering tuple given by the?ordering
?option in the model’s?Meta
. You can override this on a per-QuerySet
?basis by using the?order_by
?method.
1 |
Entry.objects. filter (pub_date__year = 2005 ).order_by( '-pub_date' ,? 'headline' )
|
The result above will be ordered by?pub_date
?descending, then by?headline
?ascending. The negative sign in front of?"-pub_date"
indicates?descending?order. Ascending order is implied.?
reverse()
Use the?reverse()
?method to reverse the order in which a queryset’s elements are returned. Calling?reverse()
?a second time restores the ordering back to the normal direction.
To retrieve the “l(fā)ast” five items in a queryset, you could do this:
1 |
my_queryset.reverse()[: 5 ]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
單表對(duì)象操作
o? = ?models.Article.objects. all ()[ 0 ]
o.tilte
?
外鍵關(guān)聯(lián)
>>> o.account.username
'jack'
>>> o.account.username? = ?rain
?
外鍵反向關(guān)聯(lián)操作
>>> a? = ?models.Account.objects.get(username = 'alex' )
>>> a.article_set. all ()
<QuerySet [<Article: 你好, 2018 >]>
>>> a.article_set.select_related()
<QuerySet [<Article: 你好, 2018 >]>
?
?
多對(duì)多操作
>>> o? = ?models.Article.objects. all ()[ 1 ]
>>> o.tags. all ()
<QuerySet [<Tag: 投資>, <Tag: 科技>]>
?
?
多對(duì)多反向操作
>>> t? = ?models.Tag.objects.get(name = "投資" )
>>> t.article_set. all ()
<QuerySet [<Article: 你好, 2018 >, <Article: 粉絲超過(guò) 10 萬(wàn)后,我經(jīng)歷了抖音盜號(hào)風(fēng)波>]>
|
?
好啦,orm的操作先點(diǎn)到為止,后面學(xué)項(xiàng)目時(shí)再帶你搞復(fù)雜的。
練習(xí)題
來(lái)源:https://www./content-4-328551.html
|
來(lái)自: 印度阿三17 > 《開(kāi)發(fā)》