在第5章里,我们介绍了Django的数据层如何定义数据模型以及如何使用数据库API来创建、检索、更新以及删除记录 在这章里,我们将向你介绍Django在这方面的一些更高级功能。
如果要写 Django 的plugin,上一章非常有用。 我之前看 django_tables2 里面的 {% render_table table %} 就觉得挺神奇。当时不知道 怎么实现。
相关对象
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __unicode__(self):
return self.name
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
def __unicode__(self):
return self.title
ManyToManyField 表示表与表之间字段的多对多关系,实际操作上是建立一个中间表如table1.column1.ManyToManyField(table2) 就建立一个相关的column1_table2表
如我们在第5章的讲解,获取数据库对象的特定字段的值只需直接使用属性。 例如,要确定ID为50的书本的标题,我们这样做:
>>> from mysite.books.models import Book
>>> b = Book.objects.get(id=50)
>>> b.title
u'The Django Book'
在mysql中试试 下边的口令 改下乱码字段的编码方式:ALTER TABLE 表名 MODIFY COLUMN 字段名 VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;
但是,在之前有一件我们没提及到的是表现为ForeignKey 或 ManyToManyField的关联对象字段,它们的作用稍有不同。
访问外键(Foreign Key)值
当你获取一个ForeignKey 字段时,你会得到相关的数据模型对象。 例如:
>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
u'http://www.apress.com/'
这里对应的sql语句是不是"select publisher.website from publisher,book where book.id='50' and book.publisher_id = publisher.id"?
select publisher.website from publisher where publisher.id in (select publisher from book where book.id=50)
对于用ForeignKey 来定义的关系来说,在关系的另一端也能反向的追溯回来,只不过由于不对称性的关系而稍有不同。 通过一个publisher 对象,直接获取 books ,用 publisher.book_set.all() ,如下:
>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]
这里需要先导入Publisher这个对象才能实例化哦~不然会提示没有定义的: from mysite.books.models import Publisher 这样就OK啦~
实际上,book_set 只是一个 QuerySet(参考第5章的介绍),所以它可以像QuerySet一样,能实现数据过滤和分切,例如:
>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.filter(name__icontains='django')
[<Book: The Django Book>, <Book: Pro Django>]
属性名称book_set是由模型名称的小写(如book)加_set组成的。
访问多对多值(Many-to-Many Values)
多对多和外键工作方式相同,只不过我们处理的是QuerySet而不是模型实例。 例如,这里是如何查看书籍的作者:
>>> b = Book.objects.get(id=50)
>>> b.authors.all()
[<Author: Adrian Holovaty>, <Author: Jacob Kaplan-Moss>]
>>> b.authors.filter(first_name='Adrian')
[<Author: Adrian Holovaty>]
>>> b.authors.filter(first_name='Adam')
[]
@楼上:Django对于MTM在自动创建表时会有三张,在此处,即books_book,books_author,books_book_author。以id键关联。那本处的sql可能相当于:select a.first_name,a.last_name from books_authors a,books_book b,books_book_authors c where b.id='50' and c.book_id =b.id and a.id=c.author_id and a.first_name='Adrian'。
反向查询也可以。 要查看一个作者的所有书籍,使用author.book_set ,就如这样:
>>> a = Author.objects.get(first_name='Adrian', last_name='Holovaty')
>>> a.book_set.all()
[<Book: The Django Book>, <Book: Adrian's Other Book>]
authors = models.ManyToManyField(Author, related_name='books') 使用related_name : a.books.all()
这里,就像使用 ForeignKey字段一样,属性名book_set是在数据模型(model)名后追加_set。
更改数据库模式(Database Schema)
更改数据库那节没有看懂,不过用第三方插件south来管理数据库确实非常方便,大家可以看一下:http://blog.chinaunix.net/uid-21633169-id-4356835.html
1.7之后的版本使用 python manage.py makemigrations books 生成sql语句,之后用python manage.py migrate books 提交, 很方便 python manage.py makemigrations --empty books
在我们在第5章介绍 syncdb 这个命令时, 我们注意到 syncdb仅仅创建数据库里还没有的表,它 并不 对你数据模型的修改进行同步,也不处理数据模型的删除。 如果你新增或修改数据模型里的字段,或是删除了一个数据模型,你需要手动在数据库里进行相应的修改。 这段将解释了具体怎么做:
当处理模型修改的时候,将Django的数据库层的工作流程铭记于心是很重要的。
- 如果模型包含一个未曾在数据库里建立的字段,Django会报出错信息。 当你第一次用Django的数据库API请求表中不存在的字段时会导致错误(就是说,它会在运行时出错,而不是编译时)。
- Django不关心数据库表中是否存在未在模型中定义的列。
- Django不关心数据库中是否存在未被模型表示的表格。
改变模型的模式架构意味着需要按照顺序更改Python代码和数据库。
当要向一个产品设置表(或者说是model)添加一个字段的时候,要使用的技巧是利用Django不关心表里是否包含model里所没有的列的特性。 策略就是现在数据库里加入字段,然后同步Django的模型以包含新字段。
然而 这里有一个鸡生蛋蛋生鸡的问题 ,由于要想了解新增列的SQL语句,你需要使用Django的 manage.py sqlall命令进行查看 ,而这又需要字段已经在模型里存在了。 (注意:你并 不是非得使用与Django相同的SQL语句创建新的字段,但是这样做确实是一个好主意 ,它能让一切都保持同步。)
django 1.10以上版本,已经内置migrate,查询说起来语句,可以使用如下命令行python manger.py sqlmigrate theapp 0001,theapp是你创建的app
在Django2.0里,sqlall已被废弃,新的使用方法如下: manage.py makemigrations appname [,migrations]生成迁移数据(在appname/migrations下) manage.py makemigrations appname migrations 查看sql语句 manage.py migrate appname 执行语句修改数据库
这个鸡-蛋的问题的解决方法是在开发者环境里而不是发布环境里实现这个变化。 (你正使用的是测试/开发环境,对吧?)下面是具体的实施步骤。
首先,进入开发环境(也就是说,不是在发布环境里):
- 在你的模型里添加字段。
- 运行
manage.py sqlall [yourapp]来测试模型新的CREATE TABLE语句。 注意为新字段的列定义。 - 开启你的数据库的交互命令界面(比如,
psql或mysql, 或者可以使用manage.py dbshell)。 执行ALTER TABLE语句来添加新列。 - 使用Python的
manage.py shell,通过导入模型和选中表单(例如,MyModel.objects.all()[:5])来验证新的字段是否被正确的添加 ,如果一切顺利,所有的语句都不会报错。
然后在你的产品服务器上再实施一遍这些步骤。
- 启动数据库的交互界面。
- 执行在开发环境步骤中,第三步的
ALTER TABLE语句。 - 将新的字段加入到模型中。 如果你使用了某种版本控制工具,并且在第一步中,已经提交了你在开发环境上的修改,现在,可以在生产环境中更新你的代码了(例如,如果你使用Subversion,执行
svn update。 - 重新启动Web server,使修改生效。
这主要是考虑到在你改模型的同时,可能会有用户查询数据库的问题。上面说了,你先改数据库没问题,但如果你先改模型,数据库还没来得及改,那么这时候如果有用户用新模型查询了旧数据库,就会出错。所以在开发环境下先利用 sqlall 查看改数据库的 sql 命令,再去部署环境里提交这条命令,最后在部署环境里修改模型,这样整个过程都不会出错。
让我们实践下,比如添加一个num_pages字段到第五章中Book模型。首先,我们会把开发环境中的模型改成如下形式:
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
**num_pages = models.IntegerField(blank=True, null=True)**
def __unicode__(self):
return self.title
(注意 阅读第六章的“设置可选字段”以及本章下面的“添加非空列”小节以了解我们在这里添加blank=True和null=True的原因。)
然后,我们运行命令manage.py sqlall books 来查看CREATE TABLE语句。 语句的具体内容取决与你所使用的数据库, 大概是这个样子:
CREATE TABLE "books_book" (
"id" serial NOT NULL PRIMARY KEY,
"title" varchar(100) NOT NULL,
"publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"),
"publication_date" date NOT NULL,
"num_pages" integer NULL
);
CREATE TABLE "books_book" ( "id" integer NOT NULL PRIMARY KEY, "title" varchar(100) NOT NULL, "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"), "publication_date" date, "num_pages" integer )
使用新方法咯,migrate,用来迁移数据库。 用法:migrate app makemigrations,用来检测数据库变更和生成数据库迁移文件。 用法:makemigratioins app sqlmigrate,用来把数据库迁移文件转换成数据库语言
新加的字段被这样表示:
"num_pages" integer NULL
接下来,我们要在开发环境上运行数据库客户端,如果是PostgreSQL,运行 psql,,然后,我执行如下语句。
ALTER TABLE books_book ADD COLUMN num_pages integer;
添加 非NULL 字段
这里有个微妙之处值得一提。 在我们添加字段num_pages的时候,我们使用了 blank=True 和 null=True 选项。 这是因为在我们第一次创建它的时候,这个数据库字段会含有空值。
然而,想要添加不能含有空值的字段也是可以的。 要想实现这样的效果,你必须先创建 NULL 型的字段,然后将该字段的值填充为某个默认值,然后再将该字段改为 NOT NULL 型。 例如:
BEGIN;
ALTER TABLE books_book ADD COLUMN num_pages integer;
UPDATE books_book SET num_pages=0;
ALTER TABLE books_book ALTER COLUMN num_pages SET NOT NULL;
COMMIT;
sqlite> ALTER TABLE books_book ALTER COLUMN num_pages SET NOT NULL; Error: near "ALTER": syntax error
@wyatt Only the RENAME TABLE and ADD COLUMN variants of the ALTER TABLE command are supported. Other kinds of ALTER TABLE operations such as DROP COLUMN, ALTER COLUMN, ADD CONSTRAINT, and so forth are omitted.
mysql添加不能含有空值的字段直接这样就行了:alter table books_book add column num_pages integer not null
如果你这样做,记得你不要在模型中添加 blank=True 和 null=True 选项。
执行ALTER TABLE之后,我们要验证一下修改结果是否正确。启动python并执行下面的代码:
>>> from mysite.books.models import Book
>>> Book.objects.all()[:5]
如果没有异常发生,我们将切换到生产服务器,然后在生产环境的数据库中执行命令ALTER TABLE 然后我们更新生产环境中的模型,最后重启web服务器。
好像没那么复杂啊,我就添加字段后,就用了'python manage.py makemigrations'和'python manage.py syncdb',就自动在数据库添加字段了,这段到底是什么意思?
删除字段
从Model中删除一个字段要比添加容易得多。 删除字段,仅仅只要以下几个步骤:
删除字段,然后重新启动你的web服务器。
用以下命令从数据库中删除字段:
ALTER TABLE books_book DROP COLUMN num_pages;
感觉这边不用执行drop这句,你在model里删除了,然后再用python manage.py sqlall books 查看。字段就删除了,所以不执行也是可以的把
请保证操作的顺序正确。 如果你先从数据库中删除字段,Django将会立即抛出异常。
删除多对多关联字段
由于多对多关联字段不同于普通字段,所以删除操作是不同的。
删除多对多字段后,使用 python manage.py makemigrations books 生成改动文件时报错:“<class 'books.admin.BookAdmin'>: (admin.E019) The value of 'filter_horizontal[0]' refers to 'authors', which is not an attribute of 'books.Book'.”
从你的模型中删除ManyToManyField,然后重启web服务器。
用下面的命令从数据库删除关联表:
DROP TABLE books_book_authors;
像上面一样,注意操作的顺序。
删除模型
从文件中删除你想要删除的模型,然后重启web 服务器models.py
然后用以下命令从数据库中删除表:
DROP TABLE books_book;
当你需要从数据库中删除任何有依赖的表时要注意(也就是任何与表books_book有外键的表 )。
这句话翻译的不对!原话是Note that you might need to remove any dependent tables from your database first — e.g., any tables that have foreign keys to books_book. 翻译后意思应该让人理解成是:注意,你在删除某个模型的时候,如果这个模型(相应的,模型从数据库角度而言就是一个数据库。)有外键,也就是有其他模型(数据库)与之关联,那么应该先删除相关的模型先,再删除本来想删除的模型。
我觉得应该是, 如果要删除一个模型,先注意是不是有其它的模型引用它作为外键,如果有的话,请先删除引用它的模型,再删除你想要删除的模型 。。。不过,我想应该很少有人这么做, production环境中这个就是灾难
正如在前面部分,一定要按这样的顺序做。
Managers
在语句Book.objects.all()中,objects是一个特殊的属性,需要通过它查询数据库。 在第5章,我们只是简要地说这是模块的manager 。现在是时候深入了解managers是什么和如何使用了。
总之,模块manager是一个对象,Django模块通过它进行数据库查询。 每个Django模块至少有一个manager,你可以创建自定义manager以定制数据库访问。
下面是你创建自定义manager的两个原因: 增加额外的manager方法,和/或修manager返回的初始QuerySet。
增加额外的Manager方法
增加额外的manager方法是为模块添加表级功能的首选办法。 (至于行级功能,也就是只作用于模型对象实例的函数,一会儿将在本章后面解释。)
例如,我们为Book模型定义了一个title_count()方法,它需要一个关键字,返回包含这个关键字的书的数量。 (这个例子有点牵强,不过它可以说明managers如何工作。)
# models.py
from django.db import models
# ... Author and Publisher models here ...
**class BookManager(models.Manager):**
**def title_count(self, keyword):**
**return self.filter(title__icontains=keyword).count()**
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank=True, null=True)
**objects = BookManager()**
def __unicode__(self):
return self.title
有了这个manager,我们现在可以这样做:
>>> Book.objects.title_count('django')
4
>>> Book.objects.title_count('python')
18
FieldError: Cannot resolve keyword 'title_icontains' into field. Choices are: authors, id, num_pages, publication_date, publisher, publisher_id, title
下面是编码该注意的一些地方:
- 我们建立了一个BookManager类,它继承了django.db.models.Manager。这个类只有一个title_count()方法,用来做统计。 注意,这个方法使用了self.filter(),此处self指manager本身。
- 我们把BookManager()赋值给模型的objects属性。 它将取代模型的默认manager(
objects)如果我们没有特别定义,它将会被自动创建。 我们把它命名为objects,这是为了与自动创建的manager保持一致。
为什么我们要添加一个title_count()方法呢?是为了将经常使用的查询进行封装,这样我们就不必重复编码了。
修改初始Manager QuerySets
manager的基本QuerySet返回系统中的所有对象。 例如,Book.objects.all() 返回数据库book中的所有书本。
A manager’s base QuerySet returns all objects in the system. For example, Book.objects.all() returns all books in the book database
“A manager’s base QuerySet returns all objects in the system. For example, Book.objects.all() returns all books in the book database" 这么翻译将更加容易理解: Manager类内置的查询游标(QuerySet)将会返回数据库系统中的所有结果。比如,Book.objects.all()这个游标将返回数据库中所有的书本。
Manager类内置的查询记录集(QuerySet)将会返回数据库系统中的所有结果。比如,Book.objects.all()这个游标将返回数据库中所有的书本。 【不好意思更正一下】
我们可以通过覆盖Manager.get_query_set()方法来重写manager的基本QuerySet。 get_query_set()按照你的要求返回一个QuerySet。
例如,下面的模型有 两个 manager。一个返回所有对像,另一个只返回作者是Roald Dahl的书。
from django.db import models
**# First, define the Manager subclass.**
**class DahlBookManager(models.Manager):**
**def get_query_set(self):**
**return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl')**
**# Then hook it into the Book model explicitly.**
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
# ...
**objects = models.Manager() # The default manager.**
**dahl_objects = DahlBookManager() # The Dahl-specific manager.**
注意,之前书里所用的Book model是这样的:authors = models.ManyToManyField(Author)。但现在例子里变成了 author = models.CharField(max_length=50)。所以调用DahlBookManager的all方法会报错。
还真没注意到楼上说的代码发生变化了。不过,照2楼说的改了,还是不行。报下面的错,没搞明白原因: >>> Book.dah1_objects.all() Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: type object 'Book' has no attribute 'dah1_objects'
1L的问题和get_query_set被废弃掉,改为get_queryset class DahlBookManager(models.Manager): def get_queryset(self): # 添加下面这句,其实就是先查出author实例 author=Author.objects.get(name='Roald Dahl') # 把author赋值给authors return super(DahlBookManager, self).get_query_set().filter(author=author)
super(DahlBookManager,self).get_query_set().filter(author='Roald Dahl'),这段代码意思:调用‘DahlBookManager’父类的get_query_set.filter方法
在这个示例模型中,Book.objects.all()返回了数据库中的所有书本,而Book.dahl_objects.all()只返回了一本. 注意我们明确地将objects设置成manager的实例,因为如果我们不这么做,那么唯一可用的manager就将是dah1_objects。
在Django2.2中,Book.dahl_objects.all()返回的与Book.objects.all()一样,而Book.dahl_objects.get_qurey_set()才仅返回dahl的书,无论DahlBookManager的过滤器改了好几种表达方式还是同样的结果,authors__id,authors__first_name
当然,由于get_query_set()返回的是一个QuerySet对象,所以我们可以使用filter(),exclude()和其他一切QuerySet的方法。 像这些语法都是正确的:
Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()
这个例子也指出了其他有趣的技术: 在同一个模型中使用多个manager。 只要你愿意,你可以为你的模型添加多个manager()实例。 这是一个为模型添加通用滤器的简单方法。
例如:
class MaleManager(models.Manager):
def get_query_set(self):
return super(MaleManager, self).get_query_set().filter(sex='M')
class FemaleManager(models.Manager):
def get_query_set(self):
return super(FemaleManager, self).get_query_set().filter(sex='F')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
people = models.Manager()
men = MaleManager()
women = FemaleManager()
此处get_query_set方法是覆盖了models.Manager默认方法吗?,为何调用men= MaleManager()就能执行get_query_set呢?
这个例子允许你执行Person.men.all() ,Person.women.all() ,Person.people.all() 查询,生成你想要的结果。
如果你使用自定义的Manager对象,请注意,Django遇到的第一个Manager(以它在模型中被定义的位置为准)会有一个特殊状态。 Django将会把第一个Manager 定义为默认Manager ,Django的许多部分(但是不包括admin应用)将会明确地为模型使用这个manager。 结论是,你应该小心地选择你的默认manager。因为覆盖get_query_set() 了,你可能接受到一个无用的返回对像,你必须避免这种情况。
**# First, define the Manager subclass.** **class DahlBookManager(models.Manager):** **def get_query_set(self):** **return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl')** 此处用super调用了父类的方法,同时使用get_query_set()返回一个queryset对象 queryset对象,可以使用filter(),exclude()等一系列针对queryset的方法。 .还有 all() 方法。这个方法返回返回数据库中所有的记录。 尽管这个对象 看起来 象一个列表(list),它实际是一个 QuerySet 对象, 这个对象是数据库中一些记录的集合。 附录C将详细描述QuerySet。 现在,我们就先当它是一个仿真列表对象好了。 问题: def get_query_set(self): """Returns a new QuerySet object. Subclasses can override this method to easily customize the behavior of the Manager. """ return QuerySet(self.model, using=self._db) 此处get_query_set方法是覆盖了models.Manager默认方法吗?,为何调用men= MaleManager()就能执行get_query_set呢? 根据以上这段取自django的源码可以发现,get_query_set方法是原本存在的 ,此处被覆盖了,调用类的行为被默认为调用了get_query_set方法,所以可以直接调用Person.men.all(),因为all()方法中包含了get_query_set的执行
模型方法
为了给你的对像添加一个行级功能,那就定义一个自定义方法。 有鉴于manager经常被用来用一些整表操作(table-wide),模型方法应该只对特殊模型实例起作用。
可以这样理解: 修改【Manager方法】是覆盖了基类中的get_query_set方法或者是对基类进行扩展,因此,所有继承自models.Model的类都会受到影响。 【模型方法】只是对当前类进行了修改和扩展,因此只会影响到当前模型。
django model的表级功能,增加额外的Manager方法,这个方法的操作属于是对整表的操作,比如查询,是传入参数给方法对整表查询得出结果。 django model的行级功能,增加模型方法,它是赋予在你的查询结果上面的,就如下面例子,根据查询条件得到结果,根据结果使用模型方法,这属于对行记录的操作。
这是一项在模型的一个地方集中业务逻辑的技术。
最好用例子来解释一下。 这个模型有一些自定义方法:
from django.contrib.localflavor.us.models import USStateField
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
address = models.CharField(max_length=100)
city = models.CharField(max_length=50)
state = USStateField() # Yes, this is U.S.-centric...
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31):
return "Baby boomer"
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
return "Post-boomer"
def is_midwestern(self):
"Returns True if this person is from the Midwest."
return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')
def _get_full_name(self):
"Returns the person's full name."
return u'%s %s' % (self.first_name, self.last_name)
full_name = property(_get_full_name)
django 1.6: django.contrib.localflavor 已经移除了,现在是作为一个独立的包 django-localflavor 参考: https://django-localflavor.readthedocs.org/en/latest/
>django1.6要更改代码 ubuntu:pip install django-localflavor,然后from localflavor.us.models import USStateField,下以代码才能运行,因为loalflavor独立成第三方开发包了
property 方法相当于将类中_get_full_name方法当作属性使用,Person实例化后,p=Person(),p.full_name就可获取_get_full_name值
例子中的最后一个方法是一个property。 想了解更多关于属性的信息请访问http://www.python.org/download/releases/2.2/descrintro/#property
这是用法的实例:
>>> p = Person.objects.get(first_name='Barack', last_name='Obama')
>>> p.birth_date
datetime.date(1961, 8, 4)
>>> p.baby_boomer_status()
'Baby boomer'
>>> p.is_midwestern()
True
>>> p.full_name # Note this isn't a method -- it's treated as an attribute
u'Barack Obama'
执行原始SQL查询
有时候你会发现Django数据库API带给你的也只有这么多,那你可以为你的数据库写一些自定义SQL查询。 你可以通过导入django.db.connection对像来轻松实现,它代表当前数据库连接。 要使用它,需要通过connection.cursor()得到一个游标对像。 然后,使用cursor.execute(sql, [params])来执行SQL语句,使用cursor.fetchone()或者cursor.fetchall()来返回记录集。 例如:
>>> from django.db import connection
>>> cursor = connection.cursor()
>>> cursor.execute("""
... SELECT DISTINCT first_name
... FROM people_person
... WHERE last_name = %s""", ['Lennon'])
>>> row = cursor.fetchone()
>>> print row
['John']
connection和cursor几乎实现了标准Python DB-API,你可以访问[http://www.python.org/peps/pep-0249.html](http://www.python.org/peps/pep-0249.html) <[http://www.python.org/peps/pep-0249.html](http://www.python.org/peps/pep-0249.html)>__来获取更多信息。 如果你对Python DB-API不熟悉,请注意在cursor.execute() 的SQL语句中使用“%s” ,而不要在SQL内直接添加参数。 如果你使用这项技术,数据库基础库将会自动添加引号,同时在必要的情况下转意你的参数。
不要把你的视图代码和django.db.connection语句混杂在一起,把它们放在自定义模型或者自定义manager方法中是个不错的主意。 比如,上面的例子可以被整合成一个自定义manager方法,就像这样:
from django.db import connection, models
class PersonManager(models.Manager):
def first_names(self, last_name):
cursor = connection.cursor()
cursor.execute("""
SELECT DISTINCT first_name
FROM people_person
WHERE last_name = %s""", [last_name])
return [row[0] for row in cursor.fetchone()]
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
objects = PersonManager()
cursor.execute(""" SELECT DISTINCT title FROM books_book WHERE title LIKE '%%%s%%'""" % keyword) 这样就可以实现模糊查询了
然后这样使用:
>>> Person.objects.first_names('Lennon')
['John', 'Cynthia']