|
本節(jié)內(nèi)容
?
- 路由系統(tǒng)
- models模型
- admin?
- views視圖
- template模板
?
?
引子
講django的models之前, 先來想一想, 讓你通過django操作數(shù)據(jù)庫,你怎么做? 做苦思冥想,可能會這樣寫。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import?pymysql
?
?
def?index(request):
????
????conn?=?pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='alex123', db='luffy_dev')
????
????cursor?=?conn.cursor()
?
????cursor.execute("select username,email,mobile from web_account")
????data_set?=?cursor.fetchall()
?
????cursor.close()
????conn.close()
?
????return?HttpResponse(data_set)
|
?
很方便就實(shí)現(xiàn)了從數(shù)據(jù)庫里取數(shù)據(jù),事實(shí)上,很多人確實(shí)就是這么做的。但這樣做會帶來2個問題
- SQL注入危險,因為有的時候你操作數(shù)據(jù)庫的語句不是寫死在代碼里的,而是通過前端傳參數(shù)拼接的,這就給黑客有了可趁之機(jī),通過拼接參數(shù)實(shí)現(xiàn)sql注入。
- 語句跟代碼揉在一起了,增加后續(xù)維護(hù)成本
?
那怎么辦呢?ORM提供了新思路。
什么是ORM呢?
對象關(guān)系映射(Object Relational Mapping),它的實(shí)質(zhì)就是將關(guān)系數(shù)據(jù)(庫)中的業(yè)務(wù)數(shù)據(jù)用對象的形式表示出來,并通過面向?qū)ο螅∣bject-Oriented)的方式將這些對象組織起來,實(shí)現(xiàn)系統(tǒng)業(yè)務(wù)邏輯的過程。
在ORM過程中最重要的概念是映射(Mapping),通過這種映射可以使業(yè)務(wù)對象與數(shù)據(jù)庫分離。從面向?qū)ο髞碚f,數(shù)據(jù)庫不應(yīng)該和業(yè)務(wù)邏輯綁定到一起,ORM則起到這樣的分離作用,使數(shù)據(jù)庫層透明,開發(fā)人員真正的面向?qū)ο蟆?/p>
上面的解釋有點(diǎn)蒙蔽對不?其實(shí)你只需要抓住2個關(guān)鍵詞, “映射” 和 “對象”,就能知道orm是什么干什么的了。
- 映射(Mapping) —— 把表結(jié)構(gòu)映射成類
- 對象 —— 像操作類對象一樣,操作數(shù)據(jù)庫里的數(shù)據(jù)
映射
看下面的圖,就是直觀的例子,把右邊的表結(jié)構(gòu)映射成了左邊的類

?Sql語句到對象
ORM可以使你不用再寫原生SQL, 而是像操作對象一樣就可以實(shí)現(xiàn)對表里數(shù)據(jù)的增刪改查

?
好棒棒,媽媽再也不用逼你寫原生sql啦!
但是不要開心太早,ORM確實(shí)提高了開發(fā)效率,并且降低了數(shù)據(jù)操作與代碼之間的耦合,不過有利就有弊,我們總結(jié)一下orm的優(yōu)缺點(diǎn)。
優(yōu)點(diǎn):
- 實(shí)現(xiàn)了代碼與數(shù)據(jù)操作的解耦合
- 不需自己寫原生sql, 提高開發(fā)效率
- 防止SQL注入, 通過對象操作的方式,默認(rèn)就是防止sql注入的。
缺點(diǎn):
- 犧牲性能, 對象到原生SQL勢必會有轉(zhuǎn)換消耗,對性能有一定的影響?
- 復(fù)雜語句力不從心, 一些復(fù)雜的sql語句,用orm對象操作的方式很難實(shí)現(xiàn),就還得用原生sql
?
講Django為什么說ORM? 哈,? 好啦,是時候該引出主角啦,因為Django的models基于架構(gòu)ORM實(shí)現(xiàn)的。
Models模型
Django 的models把數(shù)據(jù)庫表結(jié)構(gòu)映射成了一個個的類, 表里的每個字段就是類的屬性。我們都知道數(shù)據(jù)庫有很多字段類型,int,float,char等, Django的models類針對不同的字段也設(shè)置了不同的類屬性。
|
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
|
AutoField?????????
BigAutoField??????
BigIntegerField???
BinaryField???????
BooleanField???
CharField
DateField?????????
DateTimeField?????
DecimalField?????
DurationField?????
EmailField
FileField?????????
FloatField
ImageField????????
IntegerField
GenericIPAddressField?
NullBooleanField??????
PositiveIntegerField??
PositiveSmallIntegerField?
SlugField?
SmallIntegerField
TextField???
TimeField???
URLField
UUIDField???
|
?
除了普通的表字段,針對外鍵也有映射
|
1
2
3
4
|
ForeignKey??
ManyToManyField??
?
OneToOneField??
|
好啦,接下來就用django的orm來設(shè)計一個博客表。
需求
- 每個用戶有自己的賬戶信息
- 用戶可以發(fā)文章
- 文章可以打多個標(biāo)簽
根據(jù)需求,我們設(shè)計3張表

注意Article表和Tag表是屬于多對多關(guān)系,什么是多對多?即一個文章有多個標(biāo)簽,一個標(biāo)簽又可以屬于多個文章。?
比如上圖的Article表中id為3的文章 ,它的標(biāo)簽是4,26, 即投資、大文娛、社交, 你看“投資”這個標(biāo)簽同時還屬于文章2。 這就是多對多關(guān)系 , 即many to many .?
那這種多對多的關(guān)系如何在表中存儲呢?難道真的像上圖中一樣,在Article表中加個tags字段,關(guān)聯(lián)Tag表里的多條數(shù)據(jù),通過逗號區(qū)分?
這倒確實(shí)是個解決辦法。但是也有問題,一個字段里存多條紀(jì)錄的id,就沒辦法做查詢優(yōu)化了。比如不能做索引等。
所以若想實(shí)現(xiàn)多對多關(guān)系的高效存儲 查詢優(yōu)化,可以在Article and Tag表之間再搞出一張表。

這樣是不是就實(shí)現(xiàn)了多對多關(guān)聯(lián)?
yes, 沒錯, django也是這么做的, django 有個專門的字段,叫ManyToManyField, 就是用來實(shí)現(xiàn)多對多關(guān)聯(lián)的,它會自動生成一個如上圖一樣的第3張表來存儲多對多關(guān)系。
?
正式的表結(jié)構(gòu)
|
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
|
from?django.db?import?models
?
?
?
class?Account(models.Model):
????username?=?models.CharField(max_length=64,unique=True)
????email?=?models.EmailField()
????password?=?models.CharField(max_length=128)
????register_date?=?models.DateTimeField("注冊日期",auto_now_add=True)
????signature?=?models.CharField(verbose_name="簽名",max_length=128,blank=True,null=True)
?
?
class?Article(models.Model):
????
????title?=?models.CharField(max_length=255,unique=True)
????content?=?models.TextField("文章內(nèi)容")
????account?=?models.ForeignKey("Account",verbose_name="作者",on_delete=models.CASCADE)
????tags?=?models.ManyToManyField("Tag",blank=True)
????pub_date?=?models.DateTimeField()
????read_count?=?models.IntegerField(default=0)
?
?
class?Tag(models.Model):
????
????name?=?models.CharField(max_length=64,unique=True)
????date?=?models.DateTimeField(auto_now_add=True)
|
我們發(fā)現(xiàn),每個字段其實(shí)都是一個獨(dú)立的對象,一張表其實(shí)是很多類的組合。
上面好多字段里還跟了些參數(shù),我們來看以下常用的:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
null????????
blank???????
?
db_column???
db_index????
default?????
editable????
help_text???
primary_key?
unique??????
unique_for_date????
?
unique_for_month???
unique_for_year???
verbose_name????
|
?
還有幾個特殊的字段屬性需要單獨(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.
|
1
2
3
4
5
6
7
8
9
10
11
12
|
class?Student(models.Model):
????YEAR_IN_SCHOOL_CHOICES?=?(
????????('FR',?'Freshman'),
????????('SO',?'Sophomore'),
????????('JR',?'Junior'),
????????('SR',?'Senior'),
????)
????year_in_school?=?models.CharField(
????????max_length=2,
????????choices=YEAR_IN_SCHOOL_CHOICES,
????????default=FRESHMAN,
????)
|
?
ForeignKey.on_delete
當(dāng)一條記錄關(guān)聯(lián)的外鍵紀(jì)錄被刪除時,django 也會根據(jù)外鍵關(guān)聯(lián)限制的配置來決定如何處理當(dāng)前這條紀(jì)錄。舉例,如果你有個可以為null的外鍵關(guān)聯(lián),并且你想在本紀(jì)錄關(guān)聯(lián)的數(shù)據(jù)被刪除時,把當(dāng)前紀(jì)錄的關(guān)聯(lián)字段設(shè)為null,那就配置如下
|
1
2
3
4
5
6
|
user?=?models.ForeignKey(
????User,
????on_delete=models.SET_NULL,
????blank=True,
????null=True,
)
|
這個on_delete就是決定在關(guān)聯(lián)對象被刪除時,如何處理當(dāng)前紀(jì)錄的,常用的參數(shù)如下:
- CASCADE——Cascade deletes. Django emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey.
- PROTECT——Prevent deletion of the referenced object by raising ProtectedError, a subclass of django.db.IntegrityError.
- SET_NULL——Set the ForeignKey null; this is only possible if null is True.
- SET_DEFAULT——Set the ForeignKey to its default value; a default for the ForeignKey must be set.
?
配置Django數(shù)據(jù)庫連接信息
Django支持多種數(shù)據(jù)庫,Sqlite、Mysql、Oracle、PostgreSQL,默認(rèn)的是小型文件數(shù)據(jù)庫Sqlite
|
1
2
3
4
5
6
|
DATABASES?=?{
????'default': {
????????'ENGINE':?'django.db.backends.sqlite3',
????????'NAME': os.path.join(BASE_DIR,?'db.sqlite3'),
????}
}
|
?
咱們是干大事的人,怎么也得用個Mysql呀, 改成mysql 也so easy.
|
1
2
3
4
5
6
7
8
9
10
|
DATABASES?=?{
????'default': {
????????'ENGINE':?'django.db.backends.mysql',
????????'NAME':?'my_db',
????????'USER':?'mydatabaseuser',
????????'PASSWORD':?'mypassword',
????????'HOST':?'127.0.0.1',
????????'PORT':?'3306',
????}
}
|
?
不過注意,python3 連接mysql的得使用pymysql,MysqlDB模塊300年沒更新了,但django默認(rèn)調(diào)用的還是MySQLdb, so pymysql有個功能可以讓django以為是用了MySQLdb. 即在項目目錄下的__init__.py中加上句代碼就好
|
1
2
3
|
import?pymysql
?
pymysql.install_as_MySQLdb()
|
?
不加的話,一會連接數(shù)據(jù)時會報錯噢 。
?
同步數(shù)據(jù)庫
你在ORM定義的表結(jié)構(gòu)如何同步到真實(shí)的數(shù)據(jù)庫里呢? 只需2條命令。但django只能幫你自動創(chuàng)建表,數(shù)據(jù)庫本身還是得你自己來。
|
1
|
create?database?my_db charset utf8;
|
好了,可以同步了,說好只需2步。
1. 生成同步文件,?django自帶一個專門的工具叫migrations, 負(fù)責(zé)把你的orm表轉(zhuǎn)成實(shí)際的表結(jié)構(gòu),它不旦可以幫自動創(chuàng)建表,對表結(jié)構(gòu)的修改,比如增刪改字段、改字段屬性等也都能自動同步。只需通過下面神奇的命令。
|
1
|
python manage.py makemigrations
|
?不出意外的話,會顯示類似以下信息
|
1
2
3
4
5
6
7
|
$ python manage.py makemigrations
Migrations?for?'app01':
??app01/migrations/0001_initial.py
????- Create model Account
????- Create model Article
????- Create model Tag
????- Add field tags to article
|
?
此時你會發(fā)現(xiàn),你的app下的migrations目錄里多了一個0001_initial.py的文件 ,這個文件就是因為你這條命令而創(chuàng)建的,migrations工具就會根據(jù)這個文件來創(chuàng)建數(shù)據(jù)庫里的表。
2. 同步到數(shù)據(jù)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
$ python manage.py migrate
Operations to perform:
??Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
??Applying contenttypes.0001_initial... OK
??Applying auth.0001_initial... OK
??Applying admin.0001_initial... OK
??Applying admin.0002_logentry_remove_auto_add... OK
??Applying app01.0001_initial... OK
??Applying contenttypes.0002_remove_content_type_name... OK
??Applying auth.0002_alter_permission_name_max_length... OK
??Applying auth.0003_alter_user_email_max_length... OK
??Applying auth.0004_alter_user_username_opts... OK
??Applying auth.0005_alter_user_last_login_null... OK
??Applying auth.0006_require_contenttypes_0002... OK
??Applying auth.0007_alter_validators_add_error_messages... OK
??Applying auth.0008_alter_user_username_max_length... OK
??Applying auth.0009_alter_user_last_name_max_length... OK
??Applying sessions.0001_initial... OK
(venv_django2) Alexs-MacBook-Pro:mysite alex$
|
?
此時登錄你的數(shù)據(jù)庫,會發(fā)現(xiàn)創(chuàng)建了好多張表
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
mysql> show tables;
----------------------------
| Tables_in_luffy_dev2?????? |
----------------------------
| app01_account????????????? |??
| app01_article????????????? |??
| app01_article_tags???????? |??
| app01_tag????????????????? |??
| auth_group???????????????? |??
| auth_group_permissions???? |??
| auth_permission??????????? |??
| auth_user????????????????? |??
| auth_user_groups?????????? |
| auth_user_user_permissions |
| django_admin_log?????????? |??
| django_content_type??????? |??
| django_migrations????????? |??
| django_session???????????? |??
----------------------------
14 rows?in?set?(0.00 sec)
|
好啦,表結(jié)構(gòu)也有了,我們可以往里面插數(shù)據(jù)啦。
之前說好的是可以不用SQL語句的,一點(diǎn)不騙你。
?
用orm對表數(shù)據(jù)進(jìn)行增刪改查
先進(jìn)入已經(jīng)連接好數(shù)據(jù)庫的django python環(huán)境
|
1
2
3
4
5
6
7
|
(venv_django2) Alexs-MacBook-Pro:mysite alex$ python manage.py shell?
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 26 2016, 10:47:25)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type?"help",?"copyright",?"credits"?or?"license"?for?more?information.
(InteractiveConsole)
>>>
>>> from app01?import?models
|
創(chuàng)建
創(chuàng)建數(shù)據(jù)簡單的令人發(fā)指

?
?
?
查

filter 支持很多的過濾條件,我們來看下:
?
?
contains
包含,相當(dāng)于sql的like條件
|
1
|
Entry.objects.get(headline__contains='Lennon')
|
SQL equivalent:
|
1
|
SELECT?...?WHERE?headline?LIKE?'%Lennon%';
|
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.
|
1
|
Entry.objects.filter(id__in=[1,?3,?4])
|
SQL equivalent:
|
1
|
SELECT?...?WHERE?id?IN?(1, 3, 4);
|
You can also use a queryset to dynamically evaluate the list of values instead of providing a list of literal values:
|
1
2
|
inner_qs?=?Blog.objects.filter(name__contains='Cheddar')
entries?=?Entry.objects.filter(blog__in=inner_qs)
|
This queryset will be evaluated as subselect statement:
|
1
|
SELECT?...?WHERE?blog.id?IN?(SELECT?id?FROM?...?WHERE?NAME?LIKE?'%Cheddar%')
|
gt
|
1
|
Entry.objects.filter(id__gt=4)
|
SQL equivalent:
|
1
|
SELECT?...?WHERE?id > 4;
|
gte Greater than or equal to.
lt Less than.
lte Less than or equal to.
startswith Case-sensitive starts-with.
|
1
|
Entry.objects.filter(headline__startswith='Lennon')
|
SQL equivalent:
|
1
|
SELECT?...?WHERE?headline?LIKE?'Lennon%';
|
SQLite doesn’t support case-sensitive LIKE statements; startswith acts like istartswith for SQLite
istartswith Case-insensitive starts-with.
endswith Case-sensitive ends-with.
iendswith Case-insensitive ends-with
range 區(qū)間過渡,可對數(shù)字、日期進(jìn)行過濾
|
1
2
3
4
|
import?datetime
start_date?=?datetime.date(2005,?1,?1)
end_date?=?datetime.date(2005,?3,?31)
Entry.objects.filter(pub_date__range=(start_date, end_date))
|
SQL equivalent:
|
1
|
SELECT ... WHERE pub_date BETWEEN?'2005-01-01'?and?'2005-03-31';
|
Warning!
Filtering a DateTimeField with dates won’t include items on the last day, because the bounds are interpreted as “0am on the given date”. If pub_date was a DateTimeField, the above expression would be turned into this SQL:
SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00'; Generally speaking, you can’t mix dates and datetimes.
date
For datetime fields, casts the value as date. Allows chaining additional field lookups. Takes a date value.
|
1
2
|
Entry.objects.filter(pub_date__date=datetime.date(2005,?1,?1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005,?1,?1))
|
year For date and datetime fields, an exact year match. Allows chaining additional field lookups. Takes an integer year.
|
1
2
|
Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)
|
SQL equivalent:
|
1
2
|
SELECT?...?WHERE?pub_date?BETWEEN?'2005-01-01'?AND?'2005-12-31';
SELECT?...?WHERE?pub_date >=?'2005-01-01';
|
When USE_TZ is True, datetime fields are converted to the current time zone before filtering. 簡單解決辦法是把USE_TZ=False
month For date and datetime fields, an exact month match. Allows chaining additional field lookups. Takes an integer 1 (January) through 12 (December).
|
1
2
|
Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)
|
When?USE_TZ?is?True, datetime fields are converted to the current time zone before filtering. This requires?time zone definitions in the database.
SQL equivalent:
|
1
2
|
SELECT?...?WHERE?EXTRACT('month'?FROM?pub_date) =?'12';
SELECT?...?WHERE?EXTRACT('month'?FROM?pub_date) >=?'6';
|
day For date and datetime fields, an exact day match. Allows chaining additional field lookups. Takes an integer day.
|
1
2
|
Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)
|
SQL equivalent:
|
1
2
|
SELECT?...?WHERE?EXTRACT('day'?FROM?pub_date) =?'3';
SELECT?...?WHERE?EXTRACT('day'?FROM?pub_date) >=?'3';
|
week
New in Django 1.11.
For date and datetime fields, return the week number (1-52 or 53) according to?ISO-8601, i.e., weeks start on a Monday and the first week contains the year’s first Thursday.
Example:
|
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';同
|
同時,還支持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) ';?
?
SELECT?...?WHERE?REGEXP_LIKE(title,?'^(An?|The) ',?'c');?
?
SELECT?...?WHERE?title ~?'^(An?|The) ';?
?
SELECT?...?WHERE?title REGEXP?'^(An?|The) ';?
|
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()
|
?
?
數(shù)據(jù)返回后的展示
values()
values(*fields,?**expressions)
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()
order_by(*fields)
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]
|
ORM對象操作
|
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
|
單表對象操作
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>]>
?
?
多對多操作
>>> o?=?models.Article.objects.all()[1]
>>> o.tags.all()
<QuerySet [<Tag: 投資>, <Tag: 科技>]>
?
?
多對多反向操作
>>> t?=?models.Tag.objects.get(name="投資")
>>> t.article_set.all()
<QuerySet [<Article: 你好,2018>, <Article: 粉絲超過10萬后,我經(jīng)歷了抖音盜號風(fēng)波>]>
|
?
好啦,orm的操作先點(diǎn)到為止,后面學(xué)項目時再帶你搞復(fù)雜的。
練習(xí)題
- 基于前面課程設(shè)計的表結(jié)構(gòu),完成以下練習(xí):
- 創(chuàng)建5條account和5條新tag紀(jì)錄
- 創(chuàng)建5條article信息,關(guān)聯(lián)上面的不同的用戶和tag
- 在account表里找到用戶名包含al的紀(jì)錄,然后把密碼改掉
- 在article表找到文章內(nèi)容包含“電影”2個字的,把這些文章加上”大文娛”tag
- 把用戶elina發(fā)表的文章找出來,并且把作者都改成alex
- 找到用戶表里注冊日期在2018-04月,并且signature為空的紀(jì)錄
- 打到文章中標(biāo)簽為“投資”的所有文章
- 找到每個月8號注冊的用戶
- 找到每年5月發(fā)表的文章?
- 找到2015-2017年5月發(fā)表的文章?
- 找到文章作者以’a’或’k’開頭的文章
來源:https://www./content-4-328551.html
|