建立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。
使用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” 例如:
>>> 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查询时常见情况。
-
不区分大小写的查询:
>>> Blog.objects.get(name__iexact="beatles blog")
会匹配到"Beatles Blog"
,"beatles blog"
, 甚至"BeAtlES blOG"
. -
大小写敏感的包含查询:
Entry.objects.get(headline__contains='Lennon')
翻译成SQL:
SELECT ... WHERE headline LIKE '%Lennon%';
注意大小写敏感。
还有一个大小写不敏感的版本,
icontains
. -
Starts-with 和 ends-with 查询也有大小写不敏感的版本叫做
istartswith
和iendswith
.
exact
iexact
contains
startswith
,
endswith
跨关系的查询¶
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})
这是刚加入的功能
使用原始 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
这个文章总结的非常好,帮助我解决的由于from import误差出现的问题
# from django.db import models # count = models.Userinfo.objects.filter(username=username,password=password).count()
# from django.db import models
# count = models.Userinfo.objects.filter(username=username,password=password).count()
from boycott.models import Userinfo
count = Userinfo.objects.filter(username=username,password=password).count()
这完全是两个东西,model这块对于初学者确实很太容易弄混,还好我明白了一点,和js语言这点很相似