【完整版】Django--Models和Database--建立查询(model查询和原始sql)[包括Q查询 F查询]
来源:互联网 发布:linux 端口扫描 nmap 编辑:程序博客网 时间:2024/05/14 11:18
建立model查询¶
一但创建了models,Django会自动给你一个抽象数据的API接口让你创建、获取、更新和删除对象。
下面的例子中我们会用如下的models,实现了一个网页blog的应用:
from django.db import modelsclass Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def __str__(self): # __unicode__ on Python 2 return self.nameclass Author(models.Model): name = models.CharField(max_length=200) email = models.EmailField() def __str__(self): # __unicode__ on Python 2 return self.nameclass 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.objectsTraceback: ...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 QQ(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 QUERYPoll.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 connectiondef 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 connectionscursor = 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 namedtupledef 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].id54360982>>> results[0][0]54360982
- 【完整版】Django--Models和Database--建立查询(model查询和原始sql)[包括Q查询 F查询]
- django Q和F查询
- django Q和F查询
- Django中的Q查询
- Django的Q查询
- Django Q查询
- django Q查询
- Django中的Q查询
- Django Model 查询
- django关于model查询
- 原始SQL查询结果
- SQL查询之基础(语法和概念)完整版
- SQL Sql连接查询和联合查询
- django ORM model filter 条件过滤,及多表连接查询、反向查询 和 多条件查询
- django官方文档——执行原始sql查询
- [Django] 查看orm自动执行的原始查询sql
- django执行原始查询sql,并返回Dict字典
- 建立索引和查询系统
- 归并排序
- 配置 择时 sel stock
- Lua4虚拟机运行概述
- VC++下处理UTF8编码的字符串
- 推荐个好用的firefox插件 hostadmin 可以修改hosts
- 【完整版】Django--Models和Database--建立查询(model查询和原始sql)[包括Q查询 F查询]
- Linux(ubuntu)配置apache,mysql,php
- Android-SharePreferences的commit和apply
- Cordova in action学习笔记二:My First Project
- 当eclipse卡死的时候关闭重启eclipse,运行TOMCAT时,提示8080端口被占用的解决方法
- 笔记
- java 基础的String转XML
- 九张思维导图教你学习JavaScript
- 关于App程序员泡沫(被头条、搜狐等多家网站转载)