Django中的ORM

通过《如何创建一个Django网站》大概清楚了如何创建一个简单的 Django 网站,并了解了Django 中模板模型使用方法。本篇文章主要在此基础上,了解 Django 中 ORM 相关的用法。

一个 blog 的应用中 mysite/blog/models.py 有以下实体:

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=50)
    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

创建对象

>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()

你也可以修改实体,然后保存:

>>> b5.name = 'New name'
>>> b5.save()

保存外键信息

下面例子更新 entry 实例的 blog 属性:

>>> 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()

下面是更新一个 ManyToManyField 字段:

>>> 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)

查询对象

Django 中查询数据库需要 Manager 和 QuerySet 两个对象。从数据库里检索对象,可以通过模型的 Manage 来建立 QuerySet,一个 QuerySet 表现为一个数据库中对象的结合,他可以有0个一个或多个过滤条件,在 SQL里 QuerySet 相当于 select 语句用 where 或 limit 过滤。你通过模型的 Manage 来获取 QuerySet。

Manager

Manager 对象附在模型类里,如果没有特指定,每个模型类都会有一个 objects 属性,它构成了这个模型在数据库所有基本查询。

Manager 的几个常用方法:

  • all:返回一个包含模式里所有数据库记录的 QuerySet
  • filter:返回一个包含符合指定条件的模型记录的 QuerySet
  • exclude:和 filter 相反,查找不符合条件的那些记录
  • get:获取单个符合条件的记录(没有找到或者又超过一个结果都会抛出异常)
  • order_by:改变 QuerySet 默认的排序

你可以通过模型的 Manager 对象获取 QuerySet 对象:

>>> 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."

获取所有的 blog 内容:

>>> all_entries = Entry.objects.all()

#正向排序
Entry.objects.all().order_by("headline")
#反向排序
Entry.objects.all().order_by("-headline")

获取 headline 为 Python 开头的 blog :

Entry.objects.filter(headline__startswith="Python")

#支持链式操作
Entry.objects.filter(headline__startswith="Python").exclude(pub_date__gte=datetime.now()).filter(pub_date__gte=datetime(2014, 1, 1))

QuerySet 类

QuerySet 接受动态的关键字参数,然后转换成合适的 SQL 语句在数据库上执行。

QuerySet 的几个常用方法:

  • distinct
  • values
  • values_list
  • select_related
  • filter:返回一个包含符合指定条件的模型记录的 QuerySet
  • extra:增加结果集以外的字段

延时查询

每次你完成一个 QuerySet,你获得一个全新的结果集,不包括前面的。每次完成的结果集是可以贮存,使用或复用:

>>> q1 = Entry.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())

三个 QuerySets 是分开的,第一个是 headline 以 “What” 单词开头的结果集,第二个是第一个的子集,即 pub_date 不大于现在的,第三个是第一个的子集 ,pub_date 大于现在的。

QuerySets 是延迟的,创建 QuerySets 不会触及到数据库操作,你可以多个过滤合并到一起,直到求值的时候 django才会开始查询。如:

>>> 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 q 的时候,django 才开始查询执行 SQL 到数据库。

可以使用 python 的数组限制语法限定 QuerySet,如:

>>> Entry.objects.all()[:5]
>>> Entry.objects.all()[5:10]

>>> Entry.objects.all().order_by("headline")[:4]
>>> Entry.objects.all().order_by("headline")[4:8]

一般的,限制 QuerySet 返回新的 QuerySet,不会立即求值查询,除非你使用了 “step” 参数

>>> Entry.objects.all()[:10:2]
>>> Entry.objects.order_by('headline')[0]
>>> Entry.objects.order_by('headline')[0:1].get()

字段过滤

字段查找是指定 SQL 语句的 WHERE 条件从句,通过 QuerySet 的方法 filter()exclude() 和 get() 指定查询关键字。

格式为:field__lookuptype=value

lookuptype 有以下几种:

  • gt : 大于
  • gte : 大于等于
  • in : 包含
  • lt : 小于
  • lte : 小于等于
  • exact
  • iexact
  • contains:包含查询,区分大小写
  • icontains:不区分大小写
  • startswith:匹配开头
  • endswith:匹配结尾
  • istartswith:匹配开头,不区分大小写
  • iendswith:匹配结尾,不区分大小写

>>> Entry.objects.filter(pub_date__lte='2006-01-01')

等价于:

SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';

当实体中存在 ForeignKey 时,其外键字段名称为模型名称加上 ‘_id’:

>>> Entry.objects.filter(blog_id=4)

下面是一些举例:

a、exact

>>> Entry.objects.get(headline__exact="Man bites dog")

相当于:

SELECT ... WHERE headline = 'Man bites dog';

如果查询没有提供双下划线,那么会默认 __exact:

Entry.objects.get(id__exact=14) # Explicit form
Entry.objects.get(id=14) # __exact is implied

#主键查询
Entry.objects.get(pk=14) # pk implies id__exact

b、iexact——忽略大小写

>>> Blog.objects.get(name__iexact="beatles blog")

将要匹配 blog 名称为 “Beatles Blog”, “beatles blog”, 甚至是 “BeAtlES blOG”。

c、contains——包含查询,区分大小写

Entry.objects.get(headline__contains='Lennon')

转化为 SQL:

SELECT ... WHERE headline LIKE '%Lennon%';

如果有百分号,则会进行转义:

Entry.objects.filter(headline__contains='%')

转义为:

SELECT ... WHERE headline LIKE '%\%%';

d、in 查询

# Get blogs  with id 1, 4 and 7
Entry.objects.filter(pk__in=[1,4,7])

跨关系查询

跨关系查询是针对有主外键依赖关系的对象而言的,例如上面的 Author 和 Entry 对象是多对多的映射,可以通过 Entry 对象来过滤 Author的 name:

获取所有 blog 名称为 Beatles Blog 的 Entry 列表:

>>> Entry.objects.filter(blog__name='Beatles Blog')

也可以反向查询:

>>> Blog.objects.filter(entry__headline__contains='Lennon')

如果跨越多层关系查询,中间模型没有值,django会作为空对待不会发生异常。

Blog.objects.filter(entry__authors__name='Lennon')
Blog.objects.filter(entry__authors__name__isnull=True)
Blog.objects.filter(entry__authors__isnull=False,
        entry__authors__name__isnull=True)

也支持多条件跨关系查询:

Blog.objects.filter(entry__headline__contains='Lennon',
        entry__pub_date__year=2008)

或者:

Blog.objects.filter(entry__headline__contains='Lennon').filter(
        entry__pub_date__year=2008)

使用 Extra 调整 SQL

用extra可以修复QuerySet生成的原始SQL的各个部分,它接受四个关键字参数。如下:

  • select:修改select语句
  • where:提供额外的where子句
  • tables:提供额外的表
  • params:安全的替换动态参数

增加结果集以外的字段:

queryset.extra(select={'成年':'age>18'})

提供额外的 where 条件:

queryset.extra(where=["first like '%小明%' "])

提供额外的表:

queryset.extra(tables=['myapp_person'])

安全的替换动态参数:

##'%s' is not replaced with normal string
matches = Author.objects.all().extra(where=["first = '%s' "], params= [unknown-input ( ) ])

F 关键字参数

前面给的例子里,我们建立了过滤,比照模型字段值和一个固定的值,但是如果我们想比较同一个模型里的一个字段和另一个字段的值,django 提供 F()——专门取对象中某列值的操作。

>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))

当然,还支持加减乘除和模计算:

>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
>>>
>>> Entry.objects.filter(authors__name=F('blog__name'))

对于日期类型字段,可以使用 timedelta 方法:

>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

还支持位操作 .bitand() 和 .bitor()

>>> F('somefield').bitand(16)

主键查找

Django 支持使用 pk 代替主键:

>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact

pk 还可以用于其他的查找类型:

# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1,4,7])

# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)

>>> Entry.objects.filter(blog__id__exact=3) # Explicit form
>>> Entry.objects.filter(blog__id=3)        # __exact is implied
>>> Entry.objects.filter(blog__pk=3)        # __pk implies __id__exact

Q 关键字参数

QuerySet 可以通过一个叫 Q 的关键字参数封装类进一步参数化,允许使用更复杂的逻辑查询。其结果 Q对 象可以作为 filter 或 exclude 方法的关键字参数。

例子:

from django.db.models import Q
Q(question__startswith='What')
支持 & 和 操作符:

Q(question__startswith='Who') | Q(question__startswith='What')

上面的查询翻译成 sql 语句:

WHERE question LIKE 'Who%' OR question LIKE 'What%'

取反操作:

Q(question__startswith='Who') | ~Q(pub_date__year=2005)

也可以用在 filter()exclude()get() 中:

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')

删除对象

>>>entry = Entry.objects.get(pk=1)
>>>entry.delete()
>>>Blog.objects.all().delete()

>>>Entry.objects.filter(pub_date__year=2005).delete()

关系对象

当对象之间存在映射关系或者关联时,该如何查询呢?

当你在模型里定义一个关系时,模型实例会有一个方便的 API 来访问关系对象。以下分几种映射关系分别描述。

One-to-many关系

如果一个对象有ForeignKey,这个模型实例访问关系对象通过简单的属性:

>>> e = Entry.objects.get(id=2)
>>> e.blog # Returns the related Blog object.

你可以凭借外键属性获取和赋值,修改外键值知道执行 save() 方法才会保存到数据库:

>>> e = Entry.objects.get(id=2)
>>> e.blog = some_blog
>>> e.save()

如果关联的对象可以为空,则可以将关联对象职位 None,删除关联:

>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"

子查询:

>>> e = Entry.objects.get(id=2)
>>> print(e.blog)  # Hits the database to retrieve the associated Blog.
>>> print(e.blog)  # Doesn't hit the database; uses cached version.

也可以使用 select_related() 方法,该方法会提前将关联对象查询出来:

>>> e = Entry.objects.select_related().get(id=2)
>>> print(e.blog)  # Doesn't hit the database; uses cached version.
>>> print(e.blog)  # Doesn't hit the database; uses cached version.

你也可以通过 模型_set 来访问关系对象的另一边,在 Blog 对象并没有维护 Entry 列表,但是你可以通过下面方式从 Blog 对象访问 Entry 列表:

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog.

# b.entry_set is a Manager that returns QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()

模型_set 可以通过 related_name 属性来修改,例如将 Entry 模型中的定义修改为:

 blog = ForeignKey(Blog, related_name='entries')

上面的查询就会变成:

>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to Blog.

# b.entries is a Manager that returns QuerySets.
>>> b.entries.filter(headline__contains='Lennon')
>>> b.entries.count()

Many-to-many关系

e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains='John')

a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.

One-to-one关系

class EntryDetail(models.Model):
    entry = models.OneToOneField(Entry)
    details = models.TextField()

ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.

当反向查询时:

e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object

这时候如果没有关联对象,则会抛出 DoesNotExist 异常。

并且还可以修改:

e.entrydetail = ed
时间: 2024-09-22 08:51:16

Django中的ORM的相关文章

利用Python的Django框架中的ORM建立查询API_python

 摘要 在这篇文章里,我将以反模式的角度来直接讨论Django的低级ORM查询方法的使用.作为一种替代方式,我们需要在包含业务逻辑的模型层建立与特定领域相关的查询API,这些在Django中做起来不是非常容易,但通过深入地了解ORM的内容原理,我将告诉你一些简捷的方式来达到这个目的. 概览 当编写Django应用程序时,我们已经习惯通过添加方法到模型里以此达到封装业务逻辑并隐藏实现细节.这种方法看起来是非常的自然,而且实际上它也用在Django的内建应用中.   >>> from dja

Django中的模型

Django 中的模型主要用于定义数据的来源信息,其包括一些必要的字段和一些对存储的数据的操作.通常,一个模型对应着数据库中的一个表. 简单的概念: Django 中每一个 Model 都继承自 django.db.models.Model. 在 Model 当中每一个属性 attribute 都代表一个数据库字段. 通过 Django Model API 可以执行数据库的增删改查, 而不需要写一些数据库的查询语句. 1. 模型 1.1 一个示例 下面在 myapp 应用种定义了一个 Perso

Python的Django中django-userena组件的简单使用教程

  这篇文章主要介绍了Python的Django中django-userena组件的简单使用教程,包括用户登陆和注册等简单功能的实现,需要的朋友可以参考下 利用twitter/bootstrap,项目的基础模板算是顺利搞定.接下来开始处理用户中心. 用户中心主要包括用户登陆.注册以及头像等个人信息维护.此前,用户的注册管理我一直使用django-registration.只是这个APP有些不思进取,09年发布了0.8alpha版后就一直没什么动静.这次决定尝试另外一个用户模块组件django-u

Django中使用group

本文实例讲述了Django中使用group_by的方法.分享给大家供大家参考.具体分析如下: 在Django中怎样使用group_by语句呢?找了很多资料,都没有看到好的,在这里分享两种方法给大家: 首先,我们先建一个简单的模型. ? 1 2 3 4 5 class Book(models.Model): name = models.CharField(u'书名',max_length=255,db_index = True) author = models.CharField(u'作者',ma

Django中实现点击图片链接强制直接下载的方法

  本文实例讲述了Django中实现点击图片链接强制直接下载的方法.分享给大家供大家参考.具体分析如下: 当用户点击图片连接时,默认为在浏览器中直接开打图片,这段代码可以让图片链接变成下载 这段代码也非常适合下载大文件,基本不会消耗内存,每次只读取一部分数据到内存,然后提供下载 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def Download(request): def readFile(fn, buf_size=262144): f = open

Python中编写ORM框架的入门指引

  这篇文章主要介绍了Python中编写ORM框架的入门指引,示例代码基于Python2.x版本,需要的朋友可以参考下 有了db模块,操作数据库直接写SQL就很方便.但是,我们还缺少ORM.如果有了ORM,就可以用类似这样的语句获取User对象: ? 1 user = User.get('123') 而不是写SQL然后再转换成User对象: ? 1 2 u = db.select_one('select * from users where id=?', '123') user = User(*

Django中几种重定向方法

  这篇文章主要介绍了Django中几种重定向方法,本文讲解了使用HttpResponseRedirect.redirect.reverse以及配置文件中配置URL等方法,需要的朋友可以参考下 这里使用的是django1.5 需求: 有一个界面A,其中有一个form B, 前台提交B之后,后台保存数据之后,返回界面A,如果保存失败需要在A界面提示错误. 这里就需要后台的重定向,而且需要可以带着参数,也就是error message 这里收集了几种方法,简答说下需要那些包,怎么简单使用. 一. 使

django中使用jquery ajax post数据出现403错误的解决办法(两种方法)_AJAX相关

在django中,使用jquery ajax post数据,会出现403的错误 方法一: 如果用jQuery来处理ajax的话,Django直接送了一段解决问题的代码.把它放在一个独立的js文件中,在html页面中都引入即可.注意这个js文件必须在jquery的js文件引入之后,再引入即可 $(document).ajaxSend(function(event, xhr, settings) { function getCookie(name) { var cookieValue = null;

Django中的模板

通过<如何创建一个Django网站>大概清楚了如何创建一个简单的 Django 网站,这篇文章主要是在此基础上介绍 Django 中模板相关的用法. 视图中使用模板 在<如何创建一个Django网站>中使用模板的方式如下: from django.http import HttpResponse from django.template import RequestContext, loader from polls.models import Question def index