您的位置:首页 > 数据库 > SQL

【完整版】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 (
**kwargs
 in 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 
Entry
 in 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 python sql 对象 models