【完整版】Django--Models和Database--建立查询(model查询和原始sql)[包括Q查询 F查询]
2016-09-07 14:28
555 查看
建立model查询¶
一但创建了models,Django会自动给你一个抽象数据的API接口让你创建、获取、更新和删除对象。下面的例子中我们会用如下的models,实现了一个网页blog的应用:
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def __str__(self): # __unicode__ on Python 2 return self.name class Author(models.Model): name = models.CharField(max_length=200) email = models.EmailField() def __str__(self): # __unicode__ on Python 2 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): # __unicode__ on Python 2 return self.headline
创建对象¶
为了代表Python对象中的数据库表格数据,Django使用了一种直观化的系统:一个model类来代表一个数据库表格,这个类的实例代表了数据库表中的某一条记录。为了创建一个对象,使用关键词来实例化model类,然后调用save()来保存数据库。
假设models在文件
mysite/blog/models.py, 如下:
>>> from blog.models import Blog >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') >>> b.save()
其实这是执行了一个插入的SQL语句。Django在你调用save()前不会接触数据库。
save()方法没有返回值。
保存对象的变化¶
使用save()来保存数据库中已有对象的变化。假设Blog的b5实例已经保存过数据库了,下面的例子改变了name字段的值并且在数据库中更新了记录:
>>> b5.name = 'New name' >>> b5.save()
这里执行了更新的SQL语句。直到你调用save()Django才与数据库接触。
保存外键和多对多字段
更新外键:>>> from blog.models import Entry >>> entry = Entry.objects.get(pk=1) >>> cheese_blog = Blog.objects.get(name="Cheddar Talk") >>> entry.blog = cheese_blog >>> entry.save()
更新多对多字段有一点不同--需要使用add()给多对多关系增加一条记录。
>>> from blog.models import Author >>> joe = Author.objects.create(name="Joe") >>> entry.authors.add(joe)
增加多条记录到多对多字段的方法:
>>> john = Author.objects.create(name="John") >>> paul = Author.objects.create(name="Paul") >>> george = Author.objects.create(name="George") >>> ringo = Author.objects.create(name="Ringo") >>> entry.authors.add(john, paul, george, ringo)
获取对象¶
为了从数据库获取对象,需要通过你的model类的Manager构造一个QuerySet。一个QuerySet代表了一组数据库的对象。可以有零个,一个或者多个筛选。在SQL术语中,一个Qeryset等同于一个Select语句,一个filter相当于一个条件例如Where或者Limit。
你可以通过使用你的model的Manger得到一个QuerySet。每个model有至少一个Manager,默认为objects。
>>> Blog.objects <django.db.models.manager.Manager object at ...> >>> b = Blog(name='Foo', tagline='Bar') >>> b.objects Traceback: ... AttributeError: "Manager isn't accessible via Blog instances."
Note
Managers只有通过model类才可以得到,而不是直接从model实例获取,这就是“表级别”操作和“记录级别”操作的区别。
Manager是一个model的主要QuerySets。例如。Blog.obkects.all()返回一个包含所有数据库中BLog对象的QuerySet。
获取所有对象¶
最简单的获取表格中对象的方法是返回所有内容。使用Manager中的all()方法即可:>>> all_entries = Entry.objects.all()
使用filters返回指定对象
重新定义原始QuerySet增加filter条件就可以指定返回某一些数据集。两种最常见的方法是:filter(**kwargs)返回匹配指定查询参数的所有对象
exclude(**kwargs)返回不匹配指定查询参数的所有对象The lookup parameters (
**kwargsin the above function definitions) should be in the format described
in Field lookups below.
例如,得到2006年的blog对象:
Entry.objects.filter(pub_date__year=2006)
也可以用默认的manager类:
Entry.objects.all().filter(pub_date__year=2006)
链接filters¶
例如:>>> Entry.objects.filter( ... headline__startswith='What' ... ).exclude( ... pub_date__gte=datetime.date.today() ... ).filter( ... pub_date__gte=datetime(2005, 1, 30) ... )
最后返回的QuerySet包含所有hedaline开始于“what”,出版时间为2005年1月30日到今天的对象。
QuerySets很懒
>>> q = Entry.objects.filter(headline__startswith="What") >>> q = q.filter(pub_date__lte=datetime.date.today()) >>> q = q.exclude(body_text__icontains="food") >>> print(q)
尽管看起来有三次和数据库接触,但是事实上只接触了一次,就是最后的print。也就是说,Queryset的结果只有当你“ask”时才会去数据库取。
用get()获取单一对象
filter()永远返回的是Queryset,即使只有一个对象匹配查询,这种情况下,QuerySet就是只包含一个元素。当你知道只有一个对象匹配你的查询时,你可以使用Manager中的get()方法来直接返回对象:
>>> one_entry = Entry.objects.get(pk=1)
注意的是,使用get()和filter()有一个不同,在于切片[0]上。如果没有结果匹配查询,get()将会抛出DoesNotExist的异常。
同样,如果有不止一个对象匹配查询,get()方法会抛出MultipleObjectsReturned的异常。
限制QuerySet
s的条目¶
使用Python数组的子集-切片来限制你的QuerySet的返回的数量。这和SQL中的Limit和offset条件一样。例如,返回前五个对象:
>>> Entry.objects.all()[:5]
返回第六到第十个对象 (
OFFSET 5 LIMIT 5):
>>> Entry.objects.all()[5:10]
不支持负值的索引 (如
Entry.objects.all()[-1]) 。
获取单一对象而不是一个list (e.g.
SELECT foo FROM bar LIMIT 1),
使用一个索引而不是一个切片。use a simple index instead of a slice. For example, this returns the first
Entryin the database, after ordering entries alphabetically by headline:
>>> Entry.objects.order_by('headline')[0]
等价于:
>>> Entry.objects.order_by('headline')[0:1].get()
注意,如果没有对象匹配给定的条件,第一种情况会抛出IndexError的异常,第二种会抛出DoesNotExist的异常。
字段查询¶
字段查询相当于SQL的where条件。它们被用作QuerySet方法中的关键词。基本的查找关键词语句是
field__lookuptype=value. (双下划线). 例如:
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
翻译成如下的SQL语句:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
查找中的字段必须是model字段的名字。除了一种情况:外键你可以用字段名后缀_id的方式。
>>> Entry.objects.filter(blog_id=4)
还有一些会用到的lookups:
exact
“exact” 例如:
>>> Entry.objects.get(headline__exact="Cat bites dog")
类似如下的SQL语句:
SELECT ... WHERE headline = 'Cat bites dog';
如果你没有听lookup类型,也就是说,如果你的关键词语句不包含双下划线,查询的类型被认为是exact。如下两个是等价的。
>>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is implied
这是为了方便,因为exact查询时常见情况。
iexact
不区分大小写的查询:
>>> Blog.objects.get(name__iexact="beatles blog")
会匹配到"Beatles Blog",
"beatles blog",
甚至
"BeAtlES blOG".
contains
大小写敏感的包含查询:
Entry.objects.get(headline__contains='Lennon')
翻译成SQL:
SELECT ... WHERE headline LIKE '%Lennon%';
注意大小写敏感。
还有一个大小写不敏感的版本,
icontains.
startswith,
endswith
Starts-with 和 ends-with 查询也有大小写不敏感的版本叫做
istartswith和
iendswith.
跨关系的查询¶
Django提供强大的方法追踪查询的关系,自动实现SQL的Joins。为了跨关系,只需要用models中相应的字段,由两个下划线分开,直到你活得你想要的字段。获得所有的Entry对象的Blog的name字段是
'Beatles Blog':
>>> Entry.objects.filter(blog__name='Beatles Blog')
>>> Blog.objects.filter(entry__headline__contains='Lennon')
isnull的写法:
Blog.objects.filter(entry__authors__name__isnull=True)
Filters 可以引用model中的字段¶
如果想要用某一个model的字段和这个model的另一个字段比较可以用到F()。例如,找到所有的blog中comments比pingbacks多的实例,我们引用F()对象得到pingback的数量:
>>> from django.db.models import F >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))
Django支持使用F()对象进行加减乘除,取余,平方等。得到所有的比pingbacks多两倍comments的blog对象:
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
发现所有rating比pingback和comment的和少的实例:
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
涉及到时间的子弹,你可以加或者减一个timedelta对象。
>>> from datetime import timedelta >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
F()也支持按位运算通过
.bitand()and
.bitor(),
例如:
>>> F('somefield').bitand(16)
使用Q对象进行复杂查询
Q对象 (django.db.models.Q) 是一个用来封装一系列关键词语句的对象。这些关键词语句就是上面的“查询字段”。
例如用Q对象封装一个Like查询:
from django.db.models import Q Q(question__startswith='What')
Q对象可以用&和|运算符组合。例如,or查询:
Q(question__startswith='Who') | Q(question__startswith='What')
这个等同于如下的SQL Where条件:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
Q对象还可以用反运算符~,相当于NOT:
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
组合使用:
Poll.objects.get( Q(question__startswith='Who'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) )
翻译成SQL是:
SELECT * from polls WHERE question LIKE 'Who%' AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
注意的是,如果有个Q对象,需要在任何关键词语句的前面,例如:
Poll.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), question__startswith='Who', )
是正确的,但是如下是不正确的:
# INVALID QUERY Poll.objects.get( question__startswith='Who', Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) )
比较对象
如下两个语句是等效的:>>> some_entry == other_entry >>> some_entry.id == other_entry.id
删除对象¶
删除方法delete()返回要删除的对象的数量以及有每个对象类型的数量的字典: >>> e.delete() (1, {'weblog.Entry': 1})
Changed in Django 1.9:
这是刚加入的功能
使用原始 SQL¶
如果你不习惯用Django的查询,可以用原始SQL。>>> Person.objects.raw('''SELECT first AS first_name, ... last AS last_name, ... bd AS birth_date, ... pk AS id, ... FROM some_other_table''')
raw()也支持索引:
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]
给raw()传递参数:
>>> lname = 'Doe' >>> Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])
直接执行传统SQL
from django.db import connection def my_custom_sql(self): cursor = connection.cursor() cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz]) cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz]) row = cursor.fetchone() return row
注意如果你想包括百分符,在传参情况下需要多一个百分符:
cursor.execute("SELECT foo FROM bar WHERE baz = '30%'") cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND id = %s", [self.id])
如果使用不止一个数据库,
django.db.connections来获得某一个数据库的链接。
from django.db import connections cursor = connections['my_db_alias'].cursor() # Your code here...
默认情况下,Python DB API 返回的结果没有字段名,也就是说返回一个数值list而不是字典。你可以用下面的方法返回一个字典:
def dictfetchall(cursor): "Return all rows from a cursor as a dict" columns = [col[0] for col in cursor.description] return [ dict(zip(columns, row)) for row in cursor.fetchall() ]
还有一种方法是使用
collections.namedtuple()。
namedtuple是一个括号对象,可以用属性来获得对象,它可以用索引。
from collections import namedtuple def namedtuplefetchall(cursor): "Return all rows from a cursor as a namedtuple" desc = cursor.description nt_result = namedtuple('Result', [col[0] for col in desc]) return [nt_result(*row) for row in cursor.fetchall()]
这是三者的区别:
>>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2"); >>> cursor.fetchall() ((54360982, None), (54360880, None)) >>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2"); >>> dictfetchall(cursor) [{'parent_id': None, 'id': 54360982}, {'parent_id': None, 'id': 54360880}] >>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2"); >>> results = namedtuplefetchall(cursor) >>> results [Result(id=54360982, parent_id=None), Result(id=54360880, parent_id=None)] >>> results[0].id 54360982 >>> results[0][0] 54360982
相关文章推荐
- django 1.8 官方文档翻译: 2-5-2 进行原始的sql查询
- Blog.objects.filter()反查外键,django数据库models中的跨表查询,相当于sql的join
- 在Django的模型中执行原始SQL查询的方法
- [Django] 查看orm自动执行的原始查询sql
- django执行原始查询sql,并返回Dict字典
- 在Django的模型中执行原始SQL查询的方法
- Oracle 表建立Model对象和拼接Select查询语句SQL
- [Django] 查看orm自己主动运行的原始查询sql
- SQL存储过程事务和优化方法(包括查询方式语句结合)
- SQL优化-索引 (三)只要建立索引就能显著提高查询速度
- 菜鸟学Python(15):打出Django中的sql查询语句
- SQL语法精讲(包括建库、建表、建视图、查询、增加、删除、修改)
- 用JAVA实现的类FileOperate,包括文件建立,查询,删除等。。。(继续更新中)
- 用JAVA实现的类FileOperate,包括文件建立,查询,删除等。。。
- Web Sql Database 操作类,将查询语句作为参数传入。
- SQL存储过程事务和优化方法(包括查询方式语句结合)
- 原始SQL查询结果
- 分别使用ADO.Net Entity Data Model 和Linq to Sql 建立数据访问层
- 黄聪:在C#中建立复杂的、灵活的SQL查询/命令
- 在C#中建立复杂的、灵活的SQL查询/命令