【完整版】Django--Models和Database--建立查询(model查询和原始sql)[包括Q查询 F查询]

建立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的异常。

限制QuerySets的条目

使用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.

startswithendswith
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
这个文章总结的非常好,帮助我解决的由于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语言这点很相似

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值