您的位置:首页 > 编程语言 > Go语言

Django book2 模型 学习笔记

2014-02-25 15:31 344 查看
Djanjo采用模型的优点:

1 自省(运行时自动识别数据库)会导致过载和有数据完整性问题

2 把数据模型用代码的方式表述来让你可以容易对它们进行版本控制。 这样,你可以很容易了解数据层 的变

动情况。

3 SQL只能描述特定类型的数据字段。 例如,大多数数据库都没有专用的字段类型来描述Email地址

而用Django的模型可以做到这一点。 好处就是高级的数据类型带来更高的效率和更好的代码复用

4 SQL还有在不同数据库平台的兼容性问题。 发布Web应用的时候,使用Python模块描述数据库结构信息可

以避免为MySQL, PostgreSQL, and SQLite编写不同的CREATE TABLE。

要解决的问题:

Python代码和数据库表的同步问题。 如果你修改了一个Django模型, 你要自己来修改数据库来保证和模型同步

一 创建MSSQL数据库 bookdb

建立project

django-admin.py startproject mysite

二 配置settings.py的DATABASES配置

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.

'NAME': 'bookdb', # Or path to database file if using sqlite3.

'USER': 'root', # Not used with sqlite3.

'PASSWORD': '423423', # Not used with sqlite3.

'HOST': 'localhost', # Set to empty string for localhost. Not used with sqlite3.

'PORT': '', # Set to empty string for default. Not used with sqlite3.

}

}

一旦在输入了那些设置并保存之后应当测试一下你的配置。 我们可以在`` depot`` 项目目录下执行`

python manage.py shell

来进行测试。(是以正确Django配置启用Python交互解释器的一种方法。 这个方法在这里是很有必要的,

因为Django需要知道加载 哪个配置文件来获取数据库连接信息。)

输入下面这些命令来测试你的数据库配置:

>>> from django.db import connection

>>> cursor = connection.cursor()

三 创建模型

1 在项目中创建一个app

系统对app有一个约定: 如果你使用了Django的数据库层(模型),你 必须创建一个Django app。

模型必须存放在apps中。 因此,为了开始建造 我们的模型,我们必须创建一个新的app

python manage.py startapp books

2 创建module.py:

1)首先要注意的事是每个数据模型都是 django.db.models.Model 的子

类。它的父类 Model 包含了所有必要的和数据库交互的方法,并提供了一个简洁漂亮的定义数据库字段的语

法。

2)每个模型相当于单个数据库表,每个属性也是这个表中的一个字段。 属性名就是字段名,它的类型(例如

CharField )相当于数据库的字段类型 (例如 varchar )

3)“每个数据库表对应一个类”这条规则的例外情况是多对多关系。 在我们的范例模型中, Book 有一个

多对多字段 叫做 authors 。 该字段表明一本书籍有一个或多个作者,但 Book 数据库表却并没有 authors 字

段。 相反,Django创建了一个额外的表(多对多连接表)来处理书籍和作者之间的映射关系

###自定义表属性相关

4)我们并没有显式地为这些模型定义任何主键。 除非你单独指明,否则Django会自动为每

个模型生成一个自增长的整数主键字段每个Django模型都要求有单独的主键。id

5)默认的字段名与属性名相同,可以通过字段的db_column属性自定义,如

where = models.CharField(max_length=400, db_column='place')

6)除了ID字段,其他字段默认不建立索引,可以通过字段的db_index属性自定义,如

publish_date= models.DateField(db_index=True)

7)自定义ID

如果不指定ID,默认创建一个类型为IntegerField的id字段,可以通过字段的primary_key属性指定自己的ID字段。如:

key = models.IntegerField(primary_key=True)

8)自定义表名

默认的表名是appName_modelName,在Model类的Meta中可以通过db_table属性改变默认的表名。

9) 自定义表空间

对于索引字段,默认在settings的DEFAULT_INDEX_TABLESPACE设定的表空间中建立索引,可以通过字段的db_tablespace属性指定,如

publish_date= models.DateField(db_index=True,db_tablespace='another_tbs')

对于Model类,通过Meta中的db_tablespace属性指定特定的表空间。

10)自定义关联关系

1)外键默认关联到对方的主键字段,可以通过外键的to_field指定关联到的字段,如

to_model = models.ForeighKey(ToModel,to_field='key')

2)对于many-to-many关联,Django会创建一个关联表,默认表名是两个表名通过下划线连接起来。

可以通过ManyToManyField的db_table指定关联表的表名。

3)对于many-to-many关联,如果不想使用Django创建的关联表,可以通过ManyToManyField的through属性指定到一个已存在的Model类。

3 模型安装(生成数据库表)

完成这些代码之后,现在让我们来在数据库中创建这些表。 要完成该项工作,

1)第一步是在 Django 项目中 激活这些模型。 将 books app 添加到配置文件的已安装应用列表中即可完成此步骤。

2)验证模型的有效性:validate 命令检查你的模型的语法和逻辑是否正确。

python manage.py validate

3)运行下面的命令来生成 CREATE TABLE 语句

python manage.py sqlall books

sqlall 命令并没有在数据库中真正创建数据表,只是把SQL语句段打印出来,这样你可以看到Django究竟会做

些什么。 如果你想这么做的话,你可以把那些SQL语句复制到你的数据库客户端执行,或者通过Unix管道直接

进行操作(例如,`` python manager.py sqlall books | psql mydb`` )。

4)Django提供了一种更为简易的提交SQL语句至数据库的方法: `` syncdb`` 命令

python manage.py syncdb

执行这个命令后,将看到类似以下的内容:

Creating table books_publisher

Creating table books_author

Creating table books_book

Installing index for books.Book model

syncdb 命令是同步你的模型到数据库的一个简单方法。 它会根据 INSTALLED_APPS 里设置的app来检查数据库,

如果表不存在,它就会创建它。 需要注意的是, syncdb 并 不能将模型的修改或删除同步到数据库;

如果你修改或删除了一个模型,并想把它提交到数据库,syncdb并不会做出任何处理。

如果你再次运行 python manage.py syncdb ,什么也没发生,因为你没有添加新的表,运行python manage.py syncdb总是安全的,因为它不会重复执行SQL语句。

如果你有兴趣,花点时间用你的SQL客户端登录进数据库服务器看看刚才Django创建的数据表。 你可以手动启

动命令行客户端(例如,执行PostgreSQL的`` psql`` 命令),也可以执行 `` python manage.py dbshell`` ,

这个命令将依据`` DATABASE_SERVER`` 的里设置自动检测使用哪种命令行客户端。

5)基本数据访问

python manage.py shell 并输入

下面的内容试试看:

>>> from books.models import Publisher

>>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',

... city='Berkeley', state_province='CA', country='U.S.A.',

... website='http://www.apress.com/')

>>> p1.save()

>>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',

... city='Cambridge', state_province='MA', country='U.S.A.',

... website='http://www.oreilly.com/')

>>> p2.save()

>>> publisher_list = Publisher.objects.all()

>>> publisher_list

[<Publisher: Publisher object>, <Publisher: Publisher object>]

这短短几行代码干了不少的事。 这里简单的说一下:

1)首先,导入Publisher模型类, 通过这个类我们可以与包含 出版社 的数据表进行交互。

2)接着,创建一个`` Publisher`` 类的实例并设置了字段`` name, address`` 等的值。

3)调用该对象的 save() 方法,将对象保存到数据库中。 Django 会在后台执行一条 INSERT 语句。

4)最后,使用`` Publisher.objects`` 属性从数据库取出出版商的信息,这个属性可以认为是包含出版商的记

录集。 这个属性有许多方法, 这里先介绍调用`` Publisher.objects.all()`` 方法获取数据库中`` Publisher``

类的所有对象。这个操作的幕后,Django执行了一条SQL `` SELECT`` 语句。

这里有一个值得注意的地方,在这个例子可能并未清晰地展示。 当你使用Django modle API创建对象时

Django并未将对象保存至数据库内,除非你调用`` save()`` 方法:

如果需要一步完成对象的创建与存储至数据库,就使用`` objects.create()`` 方法

6)添加模块的字符串表现

为Publisher 对象添加一个方法 __unicode__() 。 __unicode__() 方法告

诉Python如何将对象以unicode的方式显示出来。

为以上三个模型添加__unicode__()方法后,就可以看到效

果了:

为了让我们的修改生效,先退出Python Shell,然后再次运行 python manage.py shell 进入。(这是保证代码

修改生效的最简单方法。)现在`` Publisher``对象列表容易理解多了。

>>> from books.models import Publisher

>>> publisher_list = Publisher.objects.all()

>>> publisher_list

[<Publisher: Apress>, <Publisher: O'Reilly>]

请确保你的每一个模型里都包含 __unicode__() 方法,这不只是为了交互时方便,也是因为 Django会在其他

一些地方用 __unicode__() 来显示对象。

最后, __unicode__() 也是一个很好的例子来演示我们怎么添加 行为 到模型里。 Django的模型不只是为对象

定义了数据库表的结构,还定义了对象的行为。 __unicode__() 就是一个例子来演示模型知道怎么显示它们自

己。

四 模型操作(一般在view.py进行)

1 插入数据

先使用一些关键参数创建对象实例,如下:

>>> p = Publisher(name='Apress',

... address='2855 Telegraph Ave.',

... city='Berkeley',

... state_province='CA',

... country='U.S.A.',

website='http://www.apress.com/')

>>> p.save()

因为 Publisher 模型有一个自动增加的主键 id ,所以第

并把它赋值给这个对象实例:

>>> p.id

52 # this will differ based on your own data

2 查询数据

1)filter:获取列表

你可以传递多个参数到 filter() 来缩小选取范围:

>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")

[<Publisher: Apress>]

在 name 和 contains 之间有双下划线。和Python一样,Django也使用双下划线来表明会进行一些魔术般的

操作。这里,contains部分会被Django翻译成LIKE语句:

>>> Publisher.objects.filter(name__contains="press")

[<Publisher: Apress>]

其他的一些查找类型有:icontains(大小写无关的LIKE),startswith和endswith, 还有range(SQLBETWEEN查

询)。

2)get:获取单个对象

上面的例子中`` filter()`` 函数返回一个记录集,这个记录集是一个列表。 相对列表来说,有些时候我们更需要

获取单个的对象, `` get()`` 方法就是在此时使用的:(一般是主键作为查询)

>>> Publisher.objects.get(name="Apress")

<Publisher: Apress>

这样,就返回了单个对象,而不是列表(更准确的说,QuerySet)。 所以,如果结果是多个对象,会导致抛出

异常:

3)数据排序

如果需要以多个字段为标准进行排序(第二个字段会在第一个字段的值相同的情况下被使用到),使用多个参

数就可以了,如下:

>>> Publisher.objects.order_by("state_province", "address")

[<Publisher: Apress>, <Publisher: O'Reilly>]

我们还可以指定逆向排序,在前面加一个减号 ‐ 前缀:

>>> Publisher.objects.order_by("-name")

[<Publisher: O'Reilly>, <Publisher: Apress>]

缺省排序

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 Meta:

ordering = ['name']

现在,让我们来接触一个新的概念。 class Meta,内嵌于 Publisher 这个类的定义中(如果 class Publisher

是顶格的,那么 class Meta 在它之下要缩进4个空格--按 Python 的传统 )。你可以在任意一个 模型 类中

使用 Meta 类,来设置一些与特定模型相关的选项。 在 附录B 中有 Meta 中所有可选项的完整参考,现在,我

关注 ordering 这个选项就够了。 如果你设置了这个选项,那么除非你检索时特意额外地使用了 order_by(),

否则,当你使用 Django 的数据库 API 去检索时,Publisher对象的相关返回值默认地都会按 name 字段排序。

4)我们已经知道如何对数据进行过滤和排序。 当然,通常我们需要同时进行过滤和排序查询的操作。 因此,你可

以简单地写成这种“链式”的形式:

>>> Publisher.objects.filter(country="U.S.A.").order_by("-name")

[<Publisher: O'Reilly>, <Publisher: Apress>]

5)限制返回的数据

另一个常用的需求就是取出固定数目的记录。 想象一下你有成千上万的出版商在你的数据库里, 但是你只想显

示第一个。 你可以使用标准的Python列表裁剪语句:

>>> Publisher.objects.order_by('name')[0]

<Publisher: Apress>

类似的,你可以用Python的range-slicing语法来取

>>> Publisher.objects.order_by('name')[0:2]

3 更新数据

我们可以看到Django的save()方法更新了不仅仅是name列的值,还有更新了所有的列。 若

name以外的列有可能会被其他的进程所改动的情况下,只更改name列显然是更加明智的。 更改某一指定的

列,我们可以调用结果集(QuerySet)对象的update()方法: 示例如下:

>>> Publisher.objects.filter(id=4).update(name='Apress Publishing')

与之等同的SQL语句变得更高效,并且不会引起竞态条件。

UPDATE books_publisher

SET name = 'Apress Publishing'

WHERE id = 52;

update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。 以下示例演示如何

将所有Publisher的country字段值由’U.S.A’更改为’USA’:

>>> Publisher.objects.all().update(country='USA')

2

update()方法会返回一个整型数值,表示受影响的记录条数。 在上面的例子中,这个值是2。

4 删除数据

>>> Publisher.objects.filter(name='Apress Publishing').delete()

>>> Publisher.objects.filter(country='USA').delete()

>>> Publisher.objects.all().delete()

>>> Publisher.objects.all()

[]

删除数据时要谨慎! 为了预防误删除掉某一个表内的所有数据,Django要求在删除表内所有数据时显示使用

all()。

五 数据模型高级进阶

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

1 访问外键(Foreign Key)值

当你获取一个ForeignKey 字段时,你会得到相关的数据模型对象。 例如:

>>> b = Book.objects.get(id=1)

>>> b.publisher

<Publisher: Apress Publishing>

>>> b.publisher.website

u'http://www.apress.com/'

对于用`` ForeignKey`` 来定义的关系来说,在关系的另一端也能反向的追溯回来,

。通过一个`` publisher`` 对象,直接获取 books ,用 publisher.book_set.all() ,如

>>> p = Publisher.objects.get(name='O\'Reilly')

>>> p.book_set.all()

[<Book: The Django Book>, <Book: Dive Into Python>, ...]

实际上,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组成的。

2 访问多对多值(Many-to-Many Values)

多对多和外键工作方式相同,只不过我们处理的是QuerySet而不是模型实例。

>>> b = Book.objects.get(id=1)

>>> b.authors.all()

[<Author: Apress Holovaty>]

>>> b.authors.filter(first_name='Apress')

[<Author: Adrian Holovaty>]

>>> b.authors.filter(first_name='Adam')

[]

反向查询也可以。 要查看一个作者的所有书籍,使用author.book_set ,就如这样:

>>> a = Author.objects.get(first_name='Apress', last_name='Holovaty')

>>> a.book_set.all()

[<Book: The Django Book>]

这里,就像使用 ForeignKey字段一样,属性名book_set是在数据模型(model)名后追加_set。

3 更改数据库模式(Database Schema)

在我们在第5章介绍 syncdb 这个命令时, 我们注意到 syncdb仅仅创建数据库里还没有的表,它 并不 对你数据模

型的修改进行同步,也不处理数据模型的删除。 如果你新增或修改数据模型里的字段,或是删除了一个数据模型,

你需要手动在数据库里进行相应的修改。

当处理模型修改的时候,将Django的数据库层的工作流程铭记于心是很重要的。

1)如果模型包含一个未曾在数据库里建立的字段,Django会报出错信息。 当你第一次用Django的数据库

API请求表中不存在的字段时会导致错误(就是说,它会在运行时出错,而不是编译时)。

2) Django不关心数据库表中是否存在未在模型中定义的列。

3)Django不关心数据库中是否存在未被模型表示的表格。

改变模型的模式架构意味着需要按照顺序更改Python代码和数据库。

A 添加字段

当要向一个产品设置表(或者说是model)添加一个字段的时候,要使用的技巧是利用Django不关心表里是否包

含model里所没有的列的特性。 策略就是现在数据库里加入字段,然后同步Django的模型以包含新字段。

首先,进入开发环境(也就是说,不是在发布环境里):

1. 在你的模型里添加字段。

2. 运行 manage.py sqlall [yourapp] 来测试模型新的 CREATE TABLE 语句。 注意为新字段的列定义。

3. 开启你的数据库的交互命令界面(比如, psql 或mysql , 或者可以使用 manage.py dbshell )。 执行

ALTER TABLE 语句来添加新列。

4. 使用Python的manage.py shell,通过导入模型和选中表单(例如, MyModel.objects.all()[:5] )来验证

新的字段是否被正确的添加 ,如果一切顺利,所有的语句都不会报错。

然后在你的产品服务器上再实施一遍这些步骤。

1. 启动数据库的交互界面。

2. 执行在开发环境步骤中,第三步的ALTER TABLE语句。

3. 将新的字段加入到模型中。 如果你使用了某种版本控制工具,并且在第一步中,已经提交了你在开发环境

上的修改,现在,可以在生产环境中更新你的代码了(例如,如果你使用Subversion,执行svn update。

4. 重新启动Web server,使修改生效。

让我们实践下,比如添加一个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

);

新加的字段被这样表示:

"num_pages" integer NULL

接下来,我们要在开发环境上运行数据库客户端,如果是PostgreSQL,运行 psql,,然后,我执行如下语句。

ALTER TABLE books_book ADD COLUMN num_pages integer NULL;

添加 NULL 字段

>>> from mysite.books.models import Book

>>> Book.objects.all()[:5]

如果没有异常发生,我们将切换到生产服务器,然后在生产环境的数据库中执行命令ALTER TABLE 然后我们更新

生产环境中的模型,最后重启web服务器。

B 删除字段

删除字段,然后重新启动你的web服务器。

用以下命令从数据库中删除字段:

ALTER TABLE books_book DROP COLUMN num_pages;

C 删除多对多关联字段

从你的模型中删除ManyToManyField,然后重启web服务器。

用下面的命令从数据库删除关联表:

DROP TABLE books_book_authors;

D 删除模型

从文件中删除你想要删除的模型,然后重启web 服务器models.py

然后用以下命令从数据库中删除表:

DROP TABLE books_book;

当你需要从数据库中删除任何有依赖的表时要注意(也就是任何与表books_book有外键的表 )。

4 Managers

在语句Book.objects.all()中,objects是一个特殊的属性,需要通过它查询数据库。在第5章,我们只是简要地说这是模块的manager 。

模块manager是一个对象,Django模块通过它进行数据库查询。

每个Django模块至少有一个manager,你可以创建自定义manager以定制数据库访问。

下面是你创建自定义manager的两个原因: 增加额外的manager方法,和/或修改manager返回的初始QuerySet。

1)增加额外的Manager方法

增加额外的manager方法是为模块添加表级功能的首选办法。 (至于行级功能,也就是只作用于模型对象实例

的函数,一会儿将在本章后面解释。)

例如,我们为Book模型定义了一个title_count()方法,它需要一个关键字,返回包含这个关键字的书的数量。

# 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

下面是编码该注意的一些地方:

A 我们建立了一个BookManager类,它继承了django.db.models.Manager。这个类只有一个title_count()方法,用来做统计。

注意,这个方法使用了self.filter(),此处self指manager本身。

B 我们把BookManager()赋值给模型的objects属性。 它将取代模型的默认manager(objects)如果我们

没有特别定义,它将会被自动创建。 我们把它命名为objects,这是为了与自动创建的manager保持一致。

C 为什么我们要添加一个title_count()方法呢?是为了将经常使用的查询进行封装,这样我们就不必重复编码了。

2)修改初始Manager QuerySets

manager的基本QuerySet返回系统中的所有对象。 例如,`` Book.objects.all()`` 返回数据库book中的所有书本。

我们可以通过覆盖Manager.get_query_set()方法来重写manager的基本QuerySet。 get_query_set()按照你的要求返回一个QuerySet。

例如,下面的模型有两个manager。一个返回所有对像,另一个只返回作者标题The Django Book的书。

class BookManager(models.Manager):

def title_count(self, keyword):

return self.filter(title__icontains=keyword).count()

class DahlBookManager(models.Manager):

def get_query_set(self):

return super(DahlBookManager, self).get_query_set().filter(title='The Django 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)

objects = BookManager()

dahl_objects = DahlBookManager()

def __unicode__(self):

return self.title

在这个示例模型中,Book.objects.all()返回了数据库中的所有书本,而Book.dahl_objects.all()只返回了一本.

注意我们明确地将objects设置成manager的实例,因为如果我们不这么做,那么唯一可用的manager就将是dah1_objects。

当然,由于get_query_set()返回的是一个QuerySet对象,所以我们可以使用filter(),exclude()和其他一切

QuerySet的方法。 像这些语法都是正确的:

Book.dahl_objects.all()

Book.dahl_objects.filter(title='The Django Book')

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

这个例子允许你执行`` Person.men.all()`` ,`` Person.women.all()`` ,`` Person.people.all()`` 查询,生成你

想要的结果。

如果你使用自定义的Manager对象,请注意,Django遇到的第一个Manager(以它在模型中被定义的位置为准)

会有一个特殊状态。 Django将会把第一个Manager 定义为默认Manager ,Django的许多部分(但是不包括

admin应用)将会明确地为模型使用这个manager。

结论是,你应该小心地选择你的默认manager。因为覆盖get_query_set() 了,你可能接受到一个无用的返回对像,你必须避免这种情况。

5 模型方法

为了给你的对像添加一个行级功能,那就定义一个自定义方法。

有鉴于manager经常被用来用一些整表操作(table-wide),模型方法应该只对特殊模型实例起作用。

这是一项在模型的一个地方集中业务逻辑的技术。

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

例子中的最后一个方法是一个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'

6 执行原始SQL查询

有时候你会发现Django数据库API带给你的也只有这么多,那你可以为你的数据库写一些自定义SQL查询。

你可以通过导入django.db.connection对像来轻松实现,它代表当前数据库连接。

要使用它,需要通过connection.cursor()得到一个游标对像。 然后,使用cursor.execute(sql, [params])来执行SQL语句,使

用cursor.fetchone()或者cursor.fetchall()来返回记录集。

connection和cursor几乎实现了标准Python DB-API,

你可以访问` 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

def get_first_name(self, last_name):

cursor = connection.cursor()

cursor.execute("""

SELECT DISTINCT first_name

FROM books_author

WHERE last_name = %s""", [last_name])

return [cursor.fetchone()]

然后这样使用:

>>> Author.objects.get_first_name('Holovaty')

[(u'Apress',)]

7 与遗留数据库整合

Django的数据库层从Python代码生成SQL schemas—但是对于遗留数据库,你已经拥有SQL schemas.

这种情况,你需要为已经存在的数据表创建model. 为此,Django自带了一个可以通过读取您的数据表结构来生成

model的工具. 该辅助工具称为inspectdb,你可以通过执行manage.py inspectdb来调用它.

使用 inspectdb

python manage.py inspectdb

inspectdb工具自省你配置文件指向的数据库,针对每一个表生成一个Django模型,然后将这些Python模型的

代码显示在系统的标准输出里面。

下面是一个从头开始的针对一个典型的遗留数据库的整合过程。 两个前提条件是安装了Django和一个传统数

据库。

1 通过运行django-admin.py startproject mysite (这里 mysite 是你的项目的名字)建立一个Django项目。

2 编辑项目中的配置文件, mysite/settings.py ,告诉Django你的数据库连接参数和数据库名。

3 通过运行 python mysite/manage.py startapp myapp (这里 myapp 是你的应用的名字)创建一个Django应

用。 这里我们使用myapp 做为应用名。

4 运行命令 python mysite/manage.py inspectdb。这将检查DATABASE_NAME 数据库中所有的表并打印出为每

张表生成的模型类。 看一看输出结果以了解inspectdb能做些什么。

5 将标准shell的输出重定向,保存输出到你的应用的 models.py 文件里:

python mysite/manage.py inspectdb > mysite/myapp/models.py

6 编辑 mysite/myapp/models.py 文件以清理生成的 models 并且做一些必要的自定义。

清理生成的Models

如你可能会预料到的,数据库自省不是完美的,你需要对产生的模型代码做些许清理。 这里提醒一点关于处理生成 models 的要点:

1) 数据库的每一个表都会被转化为一个model类 (也就是说,数据库的表和model 类之间是一对一的映射)。

这意味着你需要为多对多连接的表,重构其models 为 ManyToManyField 的对象。

2)所生成的每一个model中的每个字段都拥有自己的属性,包括id主键字段。 但是,请注意,如果某个model

没有主键的话,那么Django会自动为其增加一个id主键字段。 这样一来,你也许希望移除这样的代码行。

id = models.IntegerField(primary_key=True)

这样做并不是仅仅因为这些行是冗余的,而且如果当你的应用需要向这些表中增加新记录时,这些行会导

致某些问题。

3)每一个字段类型,如CharField、DateField, 是通过查找数据库列类型如VARCHAR,DATE来确定的。如果

inspectdb无法把某个数据库字段映射到model字段上,它会使用TextField字段进行代替,并且会在所生成

model字段后面加入Python注释“该字段类型是猜的”。 对这要当心,如果必要的话,更改字段类型。

4)如果你的数据库中的某个字段在Django中找不到合适的对应物,你可以放心的略过它。

Django模型层不要求必须导入你数据库表中的每个列。

5)如果数据库中某个列的名字是Python的保留字(比如pass、class或者for等),inspectdb会在每个属性名

后附加上_field,并将db_column属性设置为真实的字段名(也就是pass,class或者for等)。

例如,某张表中包含一个INT类型的列,其列名为for,那么所生成的model将会包含如下所示的一个字段:

for_field = models.IntegerField(db_column='for')

inspectdb 会在该字段后加注 ‘字段重命名,因为它是一个Python保留字’ 。

6)如果数据库中某张表引用了其他表(正如大多数数据库系统所做的那样),你需要适当的修改所生成model

的顺序,以使得这种引用能够正确映射。

例如,model Book拥有一个针对于model Author的外键,那么后

者应该先于前者被定义。如果你想创建一个指向尚未定义的model的关系,那么可以使用包含model名的字

符串,而不是model对象本身。

7)对于PostgreSQL,MySQL和SQLite数据库系统,inspectdb能够自动检测出主键关系。 也就是说,它会在合

适的位置插入primary_key=True。

而对于其他数据库系统,你必须为每一个model中至少一个字段插入这样的语句,

因为Django的model要求必须拥有一个primary_key=True的字段。

8)外键检测仅对PostgreSQL,还有MySQL表中的某些特定类型生效。

至于其他数据库,外键字段将在假定其为INT列的情况下被自动生成为IntegerField。

附录:利用MYSQL数据库的实验结果

Microsoft Windows XP [版本 5.1.2600]

(C) 版权所有 1985-2001 Microsoft Corp.

E:\ChromeDown\depot>E:\ChromeDown\depot>cd E:\CODE_SVN\pysrc

E:\CODE_SVN\pysrc>django-admin.py startproject mysite

E:\CODE_SVN\pysrc\mysite>python manage.py shell

Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32

Type "help", "copyright", "credits" or "license" for more information.

(InteractiveConsole)

>>> from django.db import connection

>>> cursor = connection.cursor()

>>> exit()

E:\CODE_SVN\pysrc\mysite>python manage.py startapp books

E:\CODE_SVN\pysrc\mysite>python manage.py validate

0 errors found

E:\CODE_SVN\pysrc\mysite>python manage.py sqlall books

BEGIN;

CREATE TABLE `books_publisher` (

`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,

`name` varchar(30) NOT NULL,

`address` varchar(50) NOT NULL,

`city` varchar(60) NOT NULL,

`state_province` varchar(30) NOT NULL,

`country` varchar(50) NOT NULL,

`website` varchar(200) NOT NULL

)

;

CREATE TABLE `books_author` (

`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,

`first_name` varchar(30) NOT NULL,

`last_name` varchar(40) NOT NULL,

`email` varchar(75) NOT NULL

)

;

CREATE TABLE `books_book_authors` (

`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,

`book_id` integer NOT NULL,

`author_id` integer NOT NULL,

UNIQUE (`book_id`, `author_id`)

)

;

ALTER TABLE `books_book_authors` ADD CONSTRAINT `author_id_refs_id_9e7e386` FOREIGN KEY (`author_id`) REFERENCES `books_author` (`id`);

CREATE TABLE `books_book` (

`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,

`title` varchar(100) NOT NULL,

`publisher_id` integer NOT NULL,

`publication_date` date NOT NULL

)

;

ALTER TABLE `books_book` ADD CONSTRAINT `publisher_id_refs_id_3a4d8b45` FOREIGN KEY (`publisher_id`) REFERENCES `books_publisher` (`id`);

ALTER TABLE `books_book_authors` ADD CONSTRAINT `book_id_refs_id_30430d9e` FOREIGN KEY (`book_id`) REFERENCES `books_book` (`id`);

CREATE INDEX `books_book_22dd9c39` ON `books_book` (`publisher_id`);

COMMIT;

E:\CODE_SVN\pysrc\mysite>python manage.py syncdb

Creating tables ...

Creating table auth_permission

Creating table auth_group_permissions

Creating table auth_group

Creating table auth_user_user_permissions

Creating table auth_user_groups

Creating table auth_user

Creating table auth_message

Creating table django_content_type

Creating table django_session

Creating table django_site

Creating table books_publisher

Creating table books_author

Creating table books_book_authors

Creating table books_book

You just installed Django's auth system, which means you don't have any superusers defined.

Would you like to create one now? (yes/no): no

Installing custom SQL ...

Installing indexes ...

No fixtures found.

E:\CODE_SVN\pysrc\mysite>python manage.py shell

Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32

Type "help", "copyright", "credits" or "license" for more information.

(InteractiveConsole)

>>> from books.models import Publisher

>>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',city='Berkeley', state_province='CA', country='U.S.A.',website='http://www.apress.com/')

>>> p1.save()

>>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',

... city='Cambridge', state_province='MA', country='U.S.A.',

... website='http://www.oreilly.com/')

>>> >>> p2.save()

>>> publisher_list = Publisher.objects.all()

>>> publisher_list

[<Publisher: Publisher object>, <Publisher: Publisher object>]

>>> exit()

E:\CODE_SVN\pysrc\mysite>python manage.py shell

Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32

Type "help", "copyright", "credits" or "license" for more information.

(InteractiveConsole)

>>> from books.models import Publisher

>>> publisher_list = Publisher.objects.all()

>>> publisher_list

[<Publisher: Apress>, <Publisher: O'Reilly>]

>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")

[<Publisher: Apress>]

>>> Publisher.objects.get(name="Apress")

<Publisher: Apress>

>>> Publisher.objects.order_by("state_province", "address")

[<Publisher: Apress>, <Publisher: O'Reilly>]

>>> Publisher.objects.order_by("-name")

[<Publisher: O'Reilly>, <Publisher: Apress>]

>>> Publisher.objects.filter(country="U.S.A.").order_by("-name")

[<Publisher: O'Reilly>, <Publisher: Apress>]

>>> Publisher.objects.order_by('name')[0:2]

[<Publisher: Apress>, <Publisher: O'Reilly>]

>>> Publisher.objects.order_by('name')[0]

<Publisher: Apress>

>>> Publisher.objects.filter(id=4).update(name='Apress Publishing')

1L

>>> Publisher.objects.all().update(country='USA')

2L

>>> Publisher.objects.filter(name='Apress Publishing').delete()

>>> Publisher.objects.all()

[<Publisher: O'Reilly>]

>>> from books.models import Publisher

>>> from books.models import Author

>>> p=Author(first_name='Apress', last_name='Holovaty',email='Apress@sina.com')

>>> p.save()

>>> p.id

1L

>>> p1 = Author(first_name='Jacob', last_name='Kaplan-Moss',email='Jacob@sina.com')

>>> p1.save()

>>> p1.id

2L

>>> Author.objects.all()

[<Author: Apress Holovaty>, <Author: Jacob Kaplan-Moss>]

>>> from books.models import Book

>>> p1=Publisher.objects.get(id=2)

>>> a1=Author.objects.get(first_name='Apress')

>>> b1=Book(title='The Django Book',publisher=p1,publication_date='2010-5-5')

>>> b1.save()

>>> b1.authors.add(a1)

>>> b1.save()

>>> p1=Publisher.objects.get(id=2)

>>> a1=Author.objects.get(first_name='Jacob')

>>> b1=Book(title='Dive into Python',publisher=p1,publication_date='2010-5-5')

>>> b1.save()

>>> b1.authors.add(a1)

>>> b1.save()

>>> b = Book.objects.get(id=1)

>>> b.publisher

<Publisher: O'Reilly>

>>> p = Publisher.objects.get(name='O\'Reilly')

>>> p.book_set.all()

[<Book: The Django Book>, <Book: Dive into Python>]

>>> b = Book.objects.get(id=1)

>>> b.authors.all()

[<Author: Apress Holovaty>]

>>> b.authors.filter(first_name='Adrian')

[]

>>> b.authors.filter(first_name='Apress')

[<Author: Apress Holovaty>]

>>> a = Author.objects.get(first_name='Apress', last_name='Holovaty')

>>> a.book_set.all()

[<Book: The Django Book>]

>>> exit()

E:\CODE_SVN\pysrc\mysite>python manage.py shell

Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32

Type "help", "copyright", "credits" or "license" for more information.

(InteractiveConsole)

>>> from books.models import Author

>>> Author.objects.get_first_name('Apress')

[None]

>>> Author.objects.get_first_name('Holovaty')

[(u'Apress',)]

>>>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: