Django_Models - QLGQ/learning-python GitHub Wiki
Django 模型(数据库)
Django 模型是与数据库相关的,与数据库相关的代码一般写在 models.py 中,Django 支持 sqlite3, MySQL, PostgreSQL等数据库,只需要在settings.py中配置即可,不用更改models.py中的代码,丰富的API极大的方便了使用。
django-admin.py startproject learn_models # 新建一个项目
cd learn_models # 进入到该项目的文件夹
django-admin.py startapp people # 新建一个 people 应用(app)
新建app也可以用 python manage.py startapp people, 需要指出的是,django-admin.py 是安装Django后多出的一个命令,并不是指一个 django-admin.py 脚本在当前目录下。那么project和app什么关系呢,一个项目一般包含多个应用,一个应用也可以用在多个项目中。
将我们新建的应用(people)添加到 settings.py 中的 INSTALLED_APPS中,也就是告诉Django有这么一个应用。
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'people',
)
我们打开 people/models.py 文件,修改其中的代码如下:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
def __unicode__(self):
# 在Python3中使用 def __str__(self)
return self.name
我们新建了一个Person类,继承自models.Model, 一个人有姓名和年龄,这里用到了两种Field。
使用以下命令同步数据库:
python manage.py makemigrations
python manage.py migrate
Django提供了丰富的API, 下面演示如何使用它。
$ python manage.py shell
>>> from people.models import Person
>>> Person.objects.create(name="QiangWu", age=24)
<Person: QiangWu>
>>> Person.objects.get(name="QiangWu")
<Person: QiangWu>
name 和 age 等字段中不能有 __(双下划线,因为在Django QuerySet API中有特殊含义(用于关系,包含,不区分大小写,以什么开头或结尾,日期的大于小于,正则等)。也不能有Python中的关键字,name 是合法的,student_name 也合法,但是student__name不合法,try, class, continue 也不合法,因为它是Python的关键字。( import keyword; print(keyword.kwlist) 可以打出所有的关键字)
Objects
新建一个对象的方法有以下几种:
- 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.)
获取对象有以下方法:
- Person.objects.all()
- Person.objects.all()[:10] 切片操作,获取10个人,不支持负索引,切片可以节约内存
- Person.objects.get(name=name)
- Person.objects.filter(name="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岁的
- 检查Person中是否有对象,应该用
Person.objects.all().exists()
。 - 用 len(Person.objects.all()) 可以得到Person的数量,但是推荐用
Person.objects.count()
来查询数量,后者用的是SQL:SELECT COUNT(*)。 - list(Person.objects.all()) 可以强行将 QuerySet 变成列表。
- 一般的情况下,QuerySet 中不会出来重复的,重复是很罕见的,但是当跨越多张表进行检索后,结果并到一起,可以会出来重复的值,QuerySet 重复的问题,使用
.distinct()
去重。 defer排除不需要的字段
:Article.objects.all().defer('content')only仅选择需要的字段
:Author.objects.all().only('name')- 原生的SQL查询:
authors = Author.objects.raw('select name from blog_author limit 1')
,原生SQL查询必须包含 主键! - select_related 优化一对一,多对一查询:articles = Article.objects.all().select_related('author')[:10]
- prefetch_related 优化一对多,多对多查询:articles = Article.objects.all().prefetch_related('tags')[:10]
- extra 实现别名
比如 Author 中有 name, Tag 中有 name 我们想执行
SELECT name AS tag_name FROM blog_tag;
这样的语句,就可以用 select 来实现,如下:
In [44]: tags = Tag.objects.all().extra(select={'tag_name': 'name'})
In [45]: tags[0].name
Out[45]: u'Django'
In [46]: tags[0].tag_name
Out[46]: u'Django'
我们发现 name 和 tag_name 都可以使用,确认一下执行的 SQL
In [47]: Tag.objects.all().extra(select={'tag_name': 'name'}).query.__str__()
Out[47]: u'SELECT (name) AS "tag_name", "blog_tag"."id", "blog_tag"."name" FROM "blog_tag"'
我们发现查询的时候弄了两次 (name) AS "tag_name" 和 "blog_tag"."name"
如果我们只想其中一个能用,可以用 defer 排除掉原来的 name (后面有讲)
In [49]: Tag.objects.all().extra(select={'tag_name': 'name'}).defer('name').query.__str__()
Out[49]: u'SELECT (name) AS "tag_name", "blog_tag"."id" FROM "blog_tag"'
- annotate 聚合 计数,求和,平均数等(下例中的
跨表查询 Article.objects.values('author__name') 使用的是双下划线
!)
5.1 计数
我们来计算一下每个作者的文章数(我们每个作者都导入的Article的篇数一样,所以下面的每个都一样)
In [66]: from django.db.models import Count
In [66]: Article.objects.all().values('author_id').annotate(count=Count('author')).values('author_id', 'count')
Out[66]: <QuerySet [{'count': 20, 'author_id': 1}, {'count': 20, 'author_id': 2}, {'count': 20, 'author_id': 4}]>
这是怎么工作的呢?
In [67]: Article.objects.all().values('author_id').annotate(count=Count('author')).values('author_id', 'count').query.__str__()
Out[67]: u'SELECT "blog_article"."author_id", COUNT("blog_article"."author_id") AS "count" FROM "blog_article" GROUP BY "blog_article"."author_id"'
简化一下SQL: SELECT author_id, COUNT(author_id) AS count FROM blog_article GROUP BY author_id
我们也可以获取作者的名称 及 作者的文章数
In [72]: Article.objects.all().values('author__name').annotate(count=Count('author')).values('author__name', 'count')
Out[72]: <QuerySet [{'count': 20, 'author__name': u'WeizhongTu'}, {'count': 20, 'author__name': u'twz915'}, {'count': 20, 'author__name': u'xiaoming'}]>
这时候会查询两张表,细心的同学会发现,因为作者名称中 blog_author 这张表中,author_id 在 blog_article 表中本身就有的
5.2 求和 与 平均值
5.2.1 求一个作者的所有文章的得分(score)平均值
In [6]: from django.db.models import Avg
In [7]: Article.objects.values('author_id').annotate(avg_score=Avg('score')).values('author_id', 'avg_score')
Out[7]: <QuerySet [{'author_id': 1, 'avg_score': 86.05}, {'author_id': 2, 'avg_score': 83.75}, {'author_id': 5, 'avg_score': 85.65}]>
执行的SQL
In [8]: Article.objects.values('author_id').annotate(avg_score=Avg('score')).values('author_id', 'avg_score').qu
...: ery.__str__()
Out[8]: u'SELECT "blog_article"."author_id", AVG("blog_article"."score") AS "avg_score" FROM "blog_article" GROUP BY "blog_article"."author_id"'
5.2.2 求一个作者所有文章的总分
In [12]: from django.db.models import Sum
In [13]: Article.objects.values('author__name').annotate(sum_score=Sum('score')).values('author__name', 'sum_score')
Out[13]: <QuerySet [{'author__name': u'WeizhongTu', 'sum_score': 1721}, {'author__name': u'twz915', 'sum_score': 1675}, {'author__name': u'zhen', 'sum_score': 1713}]>
执行的SQL
In [14]: Article.objects.values('author__name').annotate(sum_score=Sum('score')).values('author__name', 'sum_sco
...: re').query.__str__()
Out[14]: u'SELECT "blog_author"."name", SUM("blog_article"."score") AS "sum_score" FROM "blog_article" INNER JOIN "blog_author" ON ("blog_article"."author_id" = "blog_author"."id") GROUP BY "blog_author"."name"'
打印出在数据库中执行的语句
修改settings.py让Django打印出在数据库中执行的语句,在settings.py尾部加上:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG' if DEBUG else 'INFO',
},
},
}
这样当DEBUG为True的时候,我们可以看出django执行了什么SQL语句。