相关文章推荐
成熟的香烟  ·  mysql ...·  1 年前    · 

Models 层是 Django 框架中最强大的部分之一, 大大方便了 Web 层与数据层的交互。由于对 Model 层缺少系统理解,在使用 model Api 时经常需要查找文档, 在此做一次系统地整理。
本文主要是对 Django Model 文档的翻译, 文档地址

Models 层是 Django 框架中最强大的部分之一, 大大方便了 Web 层与数据层的交互。由于对 Model 层缺少系统理解,在使用 model Api 时经常需要查找文档, 在此做一次系统地整理。
本文主要是对 Django Model 文档的翻译, 文档地址

Model 层对象

一个model类对应数据库中的一张表, 类中的属性代表数据库中的各个字段。 类实例化后的对象, 代表数据库中的一条记录。
本文将基于下面的 models 对象展开, 由 Blog, Author, Entry 三个 models 组成。 Blog, Author 是两个独立的 model(表), 没有任何外键字段。 Entry 和 Blog 是多对一的关系, 通过外键关联; Entry 和 Author 是多对多关系。
假设 Blog 类所在目录为 mysite/blog/models.py, Author 类所在目录为 mysite/author/models.py, Entry 类所在目录为 mysite/entry/models.py

class Blog (models.Model):
name = models.CharField(max_length= 100)
tagline = models.TextField()
class Author (models.Model):
name = models.CharField(max_length= 200)
email = models.EmailField()
class Entry (models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length= 255) //外键
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author) // 多对多
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()
# 获取 id 为 1 的 entry 对象, 执行 SELECT 操作
entry = Entry.objects.get(pk= 1)
# 获取 name 为 'Cheddar Talk' 的 blog 对象
cheese_blog = Blog.objects.get(name= 'Cheddar Talk')
# 更新 entry 的 blog 属性
entry.blog = cheese_blog
# 写入更新
entry.save()
# 采用 create() 方法创建对象,会将 create 和 save () 同时进行,
# 不需要单独调用 save() 方法
joe = Author.objects.create(name= 'Joe')
# entry 为上例中实例的对象
entry.authors.add(joe)
# add() 方法可以添加对个参数
john = Author.objects.create(name= 'john')
paul = Author.objects.create(name= 'paul')
ringo = Author.objects.create(name= 'ringo')
entry.authors.add(john, paul, ringo)

对数据库中对象的检索, 是通过 model Manage 来构造一个 QuerySet 对象来实现。每个 model 类都有一个 Manage 方法, Model 类通过 objects 来调用 Manage 方法。 model 对象中没有 objects 属性。QuerySet 对象是一个model 类对应的实例集合, 即数据库对应表的子集。
QuerySet 可以是 空(zero), 单个对象(one), 多个对象(many).
QuerySet 通过 Filters 方法来实现查询结果的过滤。 对于 SQL 来说, QuerySet 等同于 SELECT 声明, Filter 等同于 LIMIT, WHERE 声明。

检索所有对象

查找 model 类对应表中的所有对象 (数据), 是通过 all() 方法来实现, 其返回一个 QuerySet 对象。

上面所列的三个条件最终会整合成一条 SQL 语句去执行:
SELECT * FROM entry(表名) WHERE headline LIKE ‘What%’ AND NOT pub_date = ‘2017-3-28’ AND pub_date > ‘2017-3-10’

4、每个 filter 返回的对象都是不相关的

每次查询生成的 QuerySet 对象都是相互独立的, 可以保存或重复使用

q1 = Entry.objects.filter(headline__startwith= 'What')
# QuerySet 对象中有 filter, exclude 方法
q2 = q1.exclude(pub_data__gte=datetime.date.today())
q3 = q1.filter(pub_data__gte=datetime.date.today())
q = Entry.objects.filter(headline__startwith= 'What')
q = q.filter(pub_date__lte=datetime.date.today())
q = q.exclude(body_text__icontains= 'food')
print(q)

QuerySet 的其他方法

当进行数据库查询时,常用的也就是 all(), get(), filter() 和 exclude() 等方法。但是这些方法,无法完成一些复杂的查询方法。下面将介绍一些 QuerySet 的复杂的查询方法。

Limiting 查询

利用 Python Array 中的分片, 可以限制返回查询结果的数量。 其对应的 SQL 语法为 LIMIT 和 OFFSET。
返回结果为 QuerySet 对象

例如:
限制返回查询结果集中的前5组数据 (LIMIT 5)

# 返回长度为 1 的 QuerySet 对象
Entry.objects.all()[ 0: 1]
# 注意其返回的不是 QuerySet 对象, 而是 Entry 的 model 对象
# 如果查询结果不存在, 将会抛出 DoesNotExist 异常
Entry.objects.all()[ 0]
# Entry.objects.all()[0] 等同于如下:
Entry.objects.all()[ 0: 1].get()
Entry.objects.filter(pub_date__lte=datetime.date.today())
# 其对应如下 SQL 语句, 假设今天为 2017-03-30
# SELECT * FROM entry WHERE pub_date <= '2017-03-30';
Entry.objects.filter(headline__iexact= 'cat Bites Dog')
# 估计类型下列查询语句
# SQL: SELECT * FROM entry WHERE lower(headline) = lower('cat Bites Dog')
# 或者SQL: SELECT * FROM entry WHERE UPPER(headline) = UPPER('cat Bites Dog')
# blog 为 Entry mode 外键字段, name 为 Blog model 中的字段, __ 联接
# 分解成两步理解: 1、根据 blog 表中 name='beatles blog' 得到对应的数据,
# 再 1、中获得的结果,到 entry 表中查询出最终的结果
Entry.objects.filter(blog__name= 'beatles blog')
# SQL 估计为: SELECT a.id, a.blog, a.headline ... FROM entry a INNER JOIN blog b ON a.blog = b.id WHERE b.name = 'beatles blog'
# 查询 entry 中 headline 包含 Lennon 的 Blog 和 发表日期在 2013 年的 blog, 两个查询条件结果取并集
Blog.objects.filter(entry__headline__contains= 'Lennon', entry__pub_date__year= 2013)
# 查询 entry 中 headline 包含 Lennon 的 Blog 并且 发表日期是 2013 年的, 两个查询条件取交集
Blog.objects.filter(entry__headline__contains= 'Lennon').filter(entry__pub_date__year= 2013)
#查询: entry 表中 author name 等于 blog name 的数据
Entry.objects.filter(authors__name=F( 'blog__name'))
#查询, entry 表中发表3天后还有修改的数据
from datetime import timedelta
Entry.objects.filter(mod_date__gt=F( 'pub_date') + timedelta(day= 3))
queryset = Entry.objects.all()
# 因为使用了整个 queryset 对象
[entry for entry in Entry.objects.all()]
# 从缓存中取数据
print(queryset[ 5])
# 从缓存中取数据
print(queryset[ 5])
from django.db.models import Q
entry = Entry.objects.filter(Q(headline__startswith= 'what') | Q(headline__startswith= 'who'))
# SQL: SELECT * FROM entry WHERE headline LIKE 'what%' OR headline LIKE 'who%'
Entry.objects.filter(Q(headline__startswith= 'what'), Q(pub_date=date( 2017, 3, 29)) | Q(pub_date=date( 2017, 3, 31)))
# SQL SELECT * FROM entry WHERE headline LIKE 'what%' AND (pub_date='2017-3-29' OR pub_date='2017-3-31')
# 有效查询
Entry.objects.filter(Q(pub_date=date( 2017, 3, 29)) | Q(pub_date=date( 2017, 3, 31)), headline__startswith= 'what')
# 无效查询, 因为普通查询条件在 Q 对象之前
Entry.objects.filter(headline__startswith= 'what', Q(pub_date=date( 2017, 3, 29)) | Q(pub_date=date( 2017, 3, 31)))

当 Django 删除一个数据时, 其默认或使用 SQL 的 CASCADE 删除模式, 即: 在父表上 delete 删除一条数据是,与之通过 ForeignKey 关联的子表中的数据也对应删除。

在 Django 中复制一条数据只需要将对应的 model 对象的 pk(id) 清除即可

blog = Blog(name= 'My blog', tagline= 'Blogging is easy')
blog.save() # blog.pk = 1
# 复制一条
blog.pk = None
blog.save() # blog.pk = 2
entry = Entry.objects.all()[ 0]
auther_old = entry.authors.all() #提取 authors
entry.pk = None
entry.save()
entry.authors.set(auther_old) # 更新对应关系

对象的对应关系

当在 model 定义是添加了 ForeignKey, OneToOneFiedl, ManyToMangField 的字段时, model 会自动生成相关 API 来获取相关数据。

One-To-Many 关系

1、正向获取 父表 to 子表
如果一个 model 包含有 ForeignKey 字段, 这个 model 的对象可以方便的获取与之关联的另一个 model 的对象。
例如: 获取 Entry 对象对应的 Blog 对象

e = Entry.objects.get(id= 3)
e.authors.all() # 返回 e 对象对应的所有 authors
e.authors.count() # authors 的数量
e.authors.filter(name__contains= 'John') # 返回名字中包含 John 的作者
a = Author.objects.get(id= 5)
# 返回所有与 a 对象对应的 Entry 对象
a.entry_set.all()
class EntryDetail (models.Model):
entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
details = models.TextField()
ed = EntryDetail.objects.get(pk= 3)
en.entry # 返回与之对应的 Entry 对象
e = Entry.objects.get(pk= 3)
# 取得与 Entry 对象对应的 EntryDetail 对象,
# 只需调用 EntryDetail 的小写 entrydetail 即可
e.entrydetail
e.entrydetail = ed2
e.save()
# Blog 对象 b 的 id = 5
Entry.objects.filter(blog=b) # 通过整个对象进行查询
Entry.objects.filter(blog=b.id) # 通过 b 的 id 属性查询
Entry.objects.filter(blog= 5) # 直接用 id 值查询 hard code

做个标记: 下期整理 QuerySet 比较常用的 API

Model 层对象

一个model类对应数据库中的一张表, 类中的属性代表数据库中的各个字段。 类实例化后的对象, 代表数据库中的一条记录。
本文将基于下面的 models 对象展开, 由 Blog, Author, Entry 三个 models 组成。 Blog, Author 是两个独立的 model(表), 没有任何外键字段。 Entry 和 Blog 是多对一的关系, 通过外键关联; Entry 和 Author 是多对多关系。
假设 Blog 类所在目录为 mysite/blog/models.py, Author 类所在目录为 mysite/author/models.py, Entry 类所在目录为 mysite/entry/models.py

class Blog (models.Model):
name = models.CharField(max_length= 100)
tagline = models.TextField()
class Author (models.Model):
name = models.CharField(max_length= 200)
email = models.EmailField()
class Entry (models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length= 255) //外键
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author) // 多对多
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()
# 获取 id 为 1 的 entry 对象, 执行 SELECT 操作
entry = Entry.objects.get(pk= 1)
# 获取 name 为 'Cheddar Talk' 的 blog 对象
cheese_blog = Blog.objects.get(name= 'Cheddar Talk')
# 更新 entry 的 blog 属性
entry.blog = cheese_blog
# 写入更新
entry.save()
# 采用 create() 方法创建对象,会将 create 和 save () 同时进行,
# 不需要单独调用 save() 方法
joe = Author.objects.create(name= 'Joe')
# entry 为上例中实例的对象
entry.authors.add(joe)
# add() 方法可以添加对个参数
john = Author.objects.create(name= 'john')
paul = Author.objects.create(name= 'paul')
ringo = Author.objects.create(name= 'ringo')
entry.authors.add(john, paul, ringo)

对数据库中对象的检索, 是通过 model Manage 来构造一个 QuerySet 对象来实现。每个 model 类都有一个 Manage 方法, Model 类通过 objects 来调用 Manage 方法。 model 对象中没有 objects 属性。QuerySet 对象是一个model 类对应的实例集合, 即数据库对应表的子集。
QuerySet 可以是 空(zero), 单个对象(one), 多个对象(many).
QuerySet 通过 Filters 方法来实现查询结果的过滤。 对于 SQL 来说, QuerySet 等同于 SELECT 声明, Filter 等同于 LIMIT, WHERE 声明。

检索所有对象

查找 model 类对应表中的所有对象 (数据), 是通过 all() 方法来实现, 其返回一个 QuerySet 对象。

上面所列的三个条件最终会整合成一条 SQL 语句去执行:
SELECT * FROM entry(表名) WHERE headline LIKE ‘What%’ AND NOT pub_date = ‘2017-3-28’ AND pub_date > ‘2017-3-10’

4、每个 filter 返回的对象都是不相关的

每次查询生成的 QuerySet 对象都是相互独立的, 可以保存或重复使用

q1 = Entry.objects.filter(headline__startwith= 'What')
# QuerySet 对象中有 filter, exclude 方法
q2 = q1.exclude(pub_data__gte=datetime.date.today())
q3 = q1.filter(pub_data__gte=datetime.date.today())
q = Entry.objects.filter(headline__startwith= 'What')
q = q.filter(pub_date__lte=datetime.date.today())
q = q.exclude(body_text__icontains= 'food')
print(q)

QuerySet 的其他方法

当进行数据库查询时,常用的也就是 all(), get(), filter() 和 exclude() 等方法。但是这些方法,无法完成一些复杂的查询方法。下面将介绍一些 QuerySet 的复杂的查询方法。

Limiting 查询

利用 Python Array 中的分片, 可以限制返回查询结果的数量。 其对应的 SQL 语法为 LIMIT 和 OFFSET。
返回结果为 QuerySet 对象

例如:
限制返回查询结果集中的前5组数据 (LIMIT 5)

# 返回长度为 1 的 QuerySet 对象
Entry.objects.all()[ 0: 1]
# 注意其返回的不是 QuerySet 对象, 而是 Entry 的 model 对象
# 如果查询结果不存在, 将会抛出 DoesNotExist 异常
Entry.objects.all()[ 0]
# Entry.objects.all()[0] 等同于如下:
Entry.objects.all()[ 0: 1].get()
Entry.objects.filter(pub_date__lte=datetime.date.today())
# 其对应如下 SQL 语句, 假设今天为 2017-03-30
# SELECT * FROM entry WHERE pub_date <= '2017-03-30';
Entry.objects.filter(headline__iexact= 'cat Bites Dog')
# 估计类型下列查询语句
# SQL: SELECT * FROM entry WHERE lower(headline) = lower('cat Bites Dog')
# 或者SQL: SELECT * FROM entry WHERE UPPER(headline) = UPPER('cat Bites Dog')
# blog 为 Entry mode 外键字段, name 为 Blog model 中的字段, __ 联接
# 分解成两步理解: 1、根据 blog 表中 name='beatles blog' 得到对应的数据,
# 再 1、中获得的结果,到 entry 表中查询出最终的结果
Entry.objects.filter(blog__name= 'beatles blog')
# SQL 估计为: SELECT a.id, a.blog, a.headline ... FROM entry a INNER JOIN blog b ON a.blog = b.id WHERE b.name = 'beatles blog'
# 查询 entry 中 headline 包含 Lennon 的 Blog 和 发表日期在 2013 年的 blog, 两个查询条件结果取并集
Blog.objects.filter(entry__headline__contains= 'Lennon', entry__pub_date__year= 2013)
# 查询 entry 中 headline 包含 Lennon 的 Blog 并且 发表日期是 2013 年的, 两个查询条件取交集
Blog.objects.filter(entry__headline__contains= 'Lennon').filter(entry__pub_date__year= 2013)
#查询: entry 表中 author name 等于 blog name 的数据
Entry.objects.filter(authors__name=F( 'blog__name'))
#查询, entry 表中发表3天后还有修改的数据
from datetime import timedelta
Entry.objects.filter(mod_date__gt=F( 'pub_date') + timedelta(day= 3))
queryset = Entry.objects.all()
# 因为使用了整个 queryset 对象
[entry for entry in Entry.objects.all()]
# 从缓存中取数据
print(queryset[ 5])
# 从缓存中取数据
print(queryset[ 5])
from django.db.models import Q
entry = Entry.objects.filter(Q(headline__startswith= 'what') | Q(headline__startswith= 'who'))
# SQL: SELECT * FROM entry WHERE headline LIKE 'what%' OR headline LIKE 'who%'
Entry.objects.filter(Q(headline__startswith= 'what'), Q(pub_date=date( 2017, 3, 29)) | Q(pub_date=date( 2017, 3, 31)))
# SQL SELECT * FROM entry WHERE headline LIKE 'what%' AND (pub_date='2017-3-29' OR pub_date='2017-3-31')
# 有效查询
Entry.objects.filter(Q(pub_date=date( 2017, 3, 29)) | Q(pub_date=date( 2017, 3, 31)), headline__startswith= 'what')
# 无效查询, 因为普通查询条件在 Q 对象之前
Entry.objects.filter(headline__startswith= 'what', Q(pub_date=date( 2017, 3, 29)) | Q(pub_date=date( 2017, 3, 31)))

当 Django 删除一个数据时, 其默认或使用 SQL 的 CASCADE 删除模式, 即: 在父表上 delete 删除一条数据是,与之通过 ForeignKey 关联的子表中的数据也对应删除。

在 Django 中复制一条数据只需要将对应的 model 对象的 pk(id) 清除即可

blog = Blog(name= 'My blog', tagline= 'Blogging is easy')
blog.save() # blog.pk = 1
# 复制一条
blog.pk = None
blog.save() # blog.pk = 2
entry = Entry.objects.all()[ 0]
auther_old = entry.authors.all() #提取 authors
entry.pk = None
entry.save()
entry.authors.set(auther_old) # 更新对应关系

对象的对应关系

当在 model 定义是添加了 ForeignKey, OneToOneFiedl, ManyToMangField 的字段时, model 会自动生成相关 API 来获取相关数据。

One-To-Many 关系

1、正向获取 父表 to 子表
如果一个 model 包含有 ForeignKey 字段, 这个 model 的对象可以方便的获取与之关联的另一个 model 的对象。
例如: 获取 Entry 对象对应的 Blog 对象

e = Entry.objects.get(id= 3)
e.authors.all() # 返回 e 对象对应的所有 authors
e.authors.count() # authors 的数量
e.authors.filter(name__contains= 'John') # 返回名字中包含 John 的作者
a = Author.objects.get(id= 5)
# 返回所有与 a 对象对应的 Entry 对象
a.entry_set.all()
class EntryDetail (models.Model):
entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
details = models.TextField()
ed = EntryDetail.objects.get(pk= 3)
en.entry # 返回与之对应的 Entry 对象
e = Entry.objects.get(pk= 3)
# 取得与 Entry 对象对应的 EntryDetail 对象,
# 只需调用 EntryDetail 的小写 entrydetail 即可
e.entrydetail
e.entrydetail = ed2
e.save()
# Blog 对象 b 的 id = 5
Entry.objects.filter(blog=b) # 通过整个对象进行查询
Entry.objects.filter(blog=b.id) # 通过 b 的 id 属性查询
Entry.objects.filter(blog= 5) # 直接用 id 值查询 hard code