无意中在网上发现了这个网站自强学堂(作者还是校友),上面整理的教程都很不错,正好适合刚学完DjangoBasic篇之后的扩展。本系列作为该自强学堂教程的笔记,为节省时间,与DjangoBasic部分重叠的话,会省略。
注意:
Django的标签导致Jekyll无法编译,都修改为了[%
和[{
,使用时需要替换将[
替换为{
!
关于模型,在DjangoBasic的 here 有所涉及。
1. 模型操作的基本函数-新建对象
假设已经建立了一个模型Person,有name和age的field。
-
Person.objects.create(name=name,age=age)
,创建对象时直接写入所需字段 -
先新建对象,再保存
p = Person(name="WZ", age=23)
p.save()
- 新建对象,再赋值最后保存
p = Person(name="TWZ")
p.age = 23
p.save()
Person.objects.get_or_create(name="WZT", age=23)
,这种方法是防止重复很好的方法,但是速度要相对慢些,返回一个元组,第一个为Person对象,第二个为True或False, 新建时返回的是True, 已经存在时返回False.
2. 模型操作的基本函数-查询对象
-
Person.objects.all()
,获取所有对象 -
Person.objects.all()[:10]
,切片操作,获取10个人,不支持负索引,切片可以节约内存 -
Person.objects.get(name=name)
,获取指定name的对象 -
Person.objects.filter(name="abc")
,名称严格等于 “abc” 的人 -
Person.objects.filter(name__exact="abc")
,名称严格等于 “abc” 的人 -
Person.objects.filter(name__iexact="abc")
,名称为 abc 但是不区分大小写,可以找到 ABC, Abc, aBC,这些都符合条件 -
Person.objects.filter(name__contains="abc")
,名称中包含 “abc”的人 -
Person.objects.filter(name__icontains="abc")
,名称中包含 “abc”,且abc不区分大小写 -
Person.objects.filter(name__regex="^abc")
,正则表达式查询 -
Person.objects.filter(name__iregex="^abc")
,正则表达式不区分大小写 -
Person.objects.exclude(name__contains="WZ")
, 排除包含 WZ 的Person对象 -
Person.objects.filter(name__contains="abc").exclude(age=23)
,找出名称含有abc, 但是排除年龄是23岁的
3. QuerySet API
前面讲解了基本的创建和查询,这里专门讲解数据库相关的接口,示例基于如下的模型:
from django.db import models
# Create your models here.
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
def __str__(self):
return self.name
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()
def __str__(self):
return self.headline
3.1 创建对象,与第一部分的方式一样
# 生成数据库sql文件
(myvenv) C:\Users\utane\OneDrive\pythonCode\zqxtTest>python manage.py makemigrations learn
Migrations for 'learn':
learn\migrations\0001_initial.py
- Create model Author
- Create model Blog
- Create model Entry
# 执行数据库sql文件
(myvenv) C:\Users\utane\OneDrive\pythonCode\zqxtTest>python manage.py migrate learn
Operations to perform:
Apply all migrations: learn
Running migrations:
Applying learn.0001_initial... OK
(myvenv) C:\Users\utane\OneDrive\pythonCode\zqxtTest>python manage.py shell
# 引入对应的模型
>>> from learn.models import Blog,Author,Entry
# 创建并保持对象
>>> b = Blog(name="lijun",tagline='love')
>>> b.save()
# 创建对象-方法1
>>> Author.objects.create(name="lijunhaha",email="lijun@qq.com")
<Author: lijunhaha>
# 创建对象-方法2
>>> wangling = Author(name="wangling",email="wanging@qq.com")
>>> wangling.save()
# 创建对象-方法3
>>> utane = Author()
>>> utane.name = "utane"
>>> utane.email = "utane@qq.com"
>>> utane.save()
# 创建对象-方法4
>>> Author.objects.get_or_create(name="utasuke",email="utasuke@qq.com")
(<Author: utasuke>, True)
>>>
3.2 相关对象查询以及赋值
如下代码的条件是entry已经被创建了,这里先取出,再将外key关联到其他对象(生成的blog对象)。
>>> entry = Entry.objects.get(pk=1)
>>> lj_blog = Blog.objects.get(name="lijun")
>>> entry.blog = lj_blog
>>> entry.save()
3.3 获取对象的方法
参考第二部分
3.4 删除/更新
与上面类似,先获取对象或是对象集合,然后delete就可以。危险操作,正式场合务必谨慎!
Person.objects.filter(name__contains="abc").delete()
people = Person.objects.filter(name__contains="abc")
people.delete()
批量更新,适用于 .all() .filter() .exclude() 等后面 (危险操作,正式场合操作务必谨慎)
Person.objects.filter(name__contains="abc").update(name='xxx') # 名称中包含 "abc"的人 都改成 xxx
Person.objects.all().delete() # 删除所有 Person 记录
单个 object 更新,适合于 .get(), get_or_create(), update_or_create() 等得到的 obj,和新建很类似。
twz = Author.objects.get(name="WeizhongTu")
twz.name="WeizhongTu"
twz.email="tuweizhong@163.com"
twz.save() # 最后不要忘了保存!!!
3.5 其他属性
# 数量查询
>>> aut = Author.objects.all()
>>> aut.count()
4
# QuerySet转换为列表
>>> list(aut)
[<Author: lijunhaha>, <Author: wangling>, <Author: utane>, <Author: utasuke>]
>>> aut
<QuerySet [<Author: lijunhaha>, <Author: wangling>, <Author: utane>, <Author: utasuke>]>
# 切片及数量查询
>>> Author.objects.all()[:2].count()
2
>>> len(aut)
4
# 是否存在的验证
>>> aut.exists()
True
>>> Entry.objects.all().exists()
False
# 迭代(直接打印对象a,调用的是其__str__)
>>> for a in aut:
... print(a)
...
lijunhaha
wangling
utane
utasuke
>>>
3.6 查询结果排序
# 正向排序
>>> aut.order_by("name")
<QuerySet [<Author: lijunhaha>, <Author: utane>, <Author: utasuke>, <Author: wangling>]>
# 反向排序
>>> aut.order_by("-name")
<QuerySet [<Author: wangling>, <Author: utasuke>, <Author: utane>, <Author: lijunhaha>]>
>>>
3.7 链式查询
>>> Author.objects.all()
<QuerySet [<Author: lijunhaha>, <Author: wangling>, <Author: utane>, <Author: utasuke>]>
>>> Author.objects.filter(name__contains="uta")
<QuerySet [<Author: utane>, <Author: utasuke>]>
>>> Author.objects.filter(name__contains="uta").filter(email__contains="suke")
<QuerySet [<Author: utasuke>]>
>>> Author.objects.filter(name__contains="uta").exclude(email__contains="suke")
<QuerySet [<Author: utane>]>
3.8 不支持负向索引
# 先反向,再取前两个,以达到实际取最后两个的效果
>>> Author.objects.all().reverse()[:2]
<QuerySet [<Author: lijunhaha>, <Author: wangling>]>
# 取ID最大的两个,这个id是django自动添加的主key
>>> Author.objects.order_by("-id")[:2]
<QuerySet [<Author: utasuke>, <Author: utane>]>
# 取id最小的两个(最早添加的两个元素)
>>> Author.objects.order_by("id")[:2]
<QuerySet [<Author: lijunhaha>, <Author: wangling>]>
>>>
3.9 去除重复元素
本身Queryset中不会出现重复记录,但是如果多个结果集合并后,就有可能重复,这时用distinct()函数去除重复元素。
qs1 = Pathway.objects.filter(label__name='x')
qs2 = Pathway.objects.filter(reaction__name='A + B >> C')
qs3 = Pathway.objects.filter(inputer__name='WeizhongTu')
# 合并到一起
qs = qs1 | qs2 | qs3
这个时候就有可能出现重复的
# 去重方法
qs = qs.distinct()
4. QuerySet进阶
4.0 准备工作
- 准备数据模型
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=50)
qq = models.CharField(default="qq",max_length=10)
addr = models.TextField(default="address")
email = models.EmailField(default='***@***.com')
def __str__(self):
return self.name
class Article(models.Model):
title = models.CharField(max_length=50)
author = models.ForeignKey(Author)
content = models.TextField()
score = models.IntegerField() # 文章的打分
tags = models.ManyToManyField('Tag')
def __str__(self):
return self.title
class Tag(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
- 生成数据表
python manage.py makemigrations
python manage.py migrate
- 在manage.py同目录下创建db的初始化文件 initdb.py
from __future__ import unicode_literals
import random
from zqxtTest.wsgi import *
from learn.models import Author, Article, Tag
author_name_list = ['WeizhongTu', 'twz915', 'dachui', 'zhe', 'zhen']
article_title_list = ['Django 教程', 'Python 教程', 'HTML 教程']
def create_authors():
for author_name in author_name_list:
author, created = Author.objects.get_or_create(name=author_name)
# 随机生成9位数的QQ,
author.qq = ''.join(
str(random.choice(range(10))) for _ in range(9)
)
author.addr = 'addr_%s' % (random.randrange(1, 3))
author.email = '%s@ziqiangxuetang.com' % (author.addr)
author.save()
def create_articles_and_tags():
# 随机生成文章
for article_title in article_title_list:
# 从文章标题中得到 tag
tag_name = article_title.split(' ', 1)[0]
tag, created = Tag.objects.get_or_create(name=tag_name)
random_author = random.choice(Author.objects.all())
for i in range(1, 21):
title = '%s_%s' % (article_title, i)
article, created = Article.objects.get_or_create(
title=title, defaults={
'author': random_author, # 随机分配作者
'content': '%s 正文' % title,
'score': random.randrange(70, 101), # 随机给文章一个打分
}
)
article.tags.add(tag)
def main():
create_authors()
create_articles_and_tags()
if __name__ == '__main__':
main()
print("Done!")
-
执行上述py,生成样本数据库表
python initdb.py
-
确认是否导入成功
python manage.py shell
>>> from blog.models import Article, Author, Tag
>>> Article.objects.all()
4.1 查看 Django queryset 执行的 SQL
有时我们需要查看Django到底是如何处理数据的,如下的语句能查询其生成的SQL语句。
>>> print(str(Author.objects.all().query))
SELECT "learn_author"."id", "learn_author"."name", "learn_author"."qq", "learn_author"."addr", "learn_author"."email" FROM "learn_author"
>>> print(str(Author.objects.all().filter(name__contains="wei").query))
SELECT "learn_author"."id", "learn_author"."name", "learn_author"."qq", "learn_author"."addr", "learn_author"."email" FROM "learn_author" WHERE "learn_author"."name" LIKE %wei% ESCAPE '\'
>>> print(str(Author.objects.all().filter(name__contains="wei")))
<QuerySet [<Author: WeizhongTu>]>
- 生成的表名,是app名加model名(因为一个db中可能包含了不同的app,各个app的model可能会有重名的case)
- 自动增加了id字段。
4.2 values_list 获取元组形式的结果集
# 获取所有作者的name和qq
>>> authors = Author.objects.values_list('name','qq')
# 只取前4个,结果集是QuerySet
>>> authors[:4]
<QuerySet [('lijunhaha', 'qq'), ('wangling', 'qq'), ('utane', 'qq'), ('utasuke', 'qq')]>
# 将其转化为了lsit,结果集是list
>>> list(authors[:4])
[('lijunhaha', 'qq'), ('wangling', 'qq'), ('utane', 'qq'), ('utasuke', 'qq')]
# 取一个字段
>>> authors = Author.objects.values_list('name')
>>> authors
<QuerySet [('lijunhaha',), ('wangling',), ('utane',), ('utasuke',), ('WeizhongTu',), ('twz915',), ('dachui',), ('zhe',), ('zhen',)]>
# 取一个字段,明确指定flat为true,看返回的QuerySet的区别
>>> authors = Author.objects.values_list('name',flat=True)
>>> authors
<QuerySet ['lijunhaha', 'wangling', 'utane', 'utasuke', 'WeizhongTu', 'twz915', 'dachui', 'zhe', 'zhen']>
# 将其结果集前4个转换为list
>>> list(authors[0:4])
['lijunhaha', 'wangling', 'utane', 'utasuke']
4.3 values 获取字典形式的结果
注意返回的结果集的数据类型,现在是字典形式(key:value)
>>> Author.objects.values('name','qq')[:4]
<QuerySet [{'name': 'lijunhaha', 'qq': 'qq'}, {'name': 'wangling', 'qq': 'qq'}, {'name': 'utane', 'qq': 'qq'}, {'name': 'utasuke', 'qq': 'qq'}]>
>>> list(Author.objects.values('name','qq')[:4])
[{'name': 'lijunhaha', 'qq': 'qq'}, {'name': 'wangling', 'qq': 'qq'}, {'name': 'utane', 'qq': 'qq'}, {'name': 'utasuke', 'qq': 'qq'}]
注意:
- values_list 和 values 返回的并不是真正的 列表 或 字典,也是 queryset,他们也是 lazy evaluation 的(惰性评估,通俗地说,就是用的时候才真正的去数据库查)
- 如果查询后没有使用,在数据库更新后再使用,你发现得到在是新内容!!!如果想要旧内容保持着,数据库更新后不要变,可以 list 一下
- 如果只是遍历这些结果,没有必要 list 它们转成列表(浪费内存,数据量大的时候要更谨慎!!!)
4.4 extra 实现 别名,条件,排序等
extra 中可实现别名,条件,排序等,后面两个用 filter, exclude 一般都能实现,排序用 order_by 也能实现。我们主要看一下别名这个 比如 Author 中有 name, Tag 中有 name 我们想执行:
# 给name取个别名 tag_name
>>> tags = Tag.objects.all().extra(select={"tag_name":"name"})
>>> tags[0].name
'Django'
>>> tags[0].tag_name
'Django'
# 查看下上面的SQL语句,发现查询了name两次,有重复
>>> tags = Tag.objects.all().extra(select={"tag_name":"name"}).query
>>> print(tags)
SELECT (name) AS "tag_name", "learn_tag"."id", "learn_tag"."name" FROM "learn_tag"
# 通过defer去除重复的name
>>> tags = Tag.objects.all().extra(select={"tag_name":"name"}).defer('name')
>>> tags[0].name
'Django'
# 上面查询的sql语句,确实只有一次name了
>>> print(tags.query)
SELECT (name) AS "tag_name", "learn_tag"."id" FROM "learn_tag"
下面的内容,需要的时候再来查阅,暂时跳过
4.5 annotate 聚合 计数,求和,平均数等
4.6 select_related 优化一对一,多对一查询
参考 6. select_related 优化一对一,多对一查询
4.7 prefetch_related 优化一对多,多对多查询
参考 7. prefetch_related 优化一对多,多对多查询
4.8 defer 排除不需要的字段
4.8 only 仅选择需要的字段
4.9 自定义聚合功能
参考 10. 自定义聚合功能