第五章 模型

第五章 模型

在第三章,咱们讲述了用 Django 建造网站的基本途径: 创建视图和 URLConf 。 正如咱们所阐述的,视图负责处理一些主观逻辑,而后返回响应结果。 做为例子之一,咱们的主观逻辑是要计算当前的日期和时间。html

在当代 Web 应用中,主观逻辑常常牵涉到与数据库的交互。 数据库驱动网站 在后台链接数据库服务器,从中取出一些数据,而后在 Web 页面用漂亮的格式展现这些数据。 这个网站也可能会向访问者提供修改数据库数据的方法。python

许多复杂的网站都提供了以上两个功能的某种结合。 例如 Amazon.com 就是一个数据库驱动站点的良好范例。 本质上,每一个产品页面都是数据库中数据以 HTML格式进行的展示,而当你发表客户评论时,该评论被插入评论数据库中。mysql

因为先天具有 Python 简单而强大的数据库查询执行方法,Django 很是适合开发数据库驱动网站。 本章深刻介绍了该功能: Django 数据库层。web

(注意: 尽管对 Django 数据库层的使用中并不特别强调这点,可是咱们仍是强烈建议您掌握一些数据库和 SQL 原理。 对这些概念的介绍超越了本书的范围,但就算你是数据库方面的菜鸟,咱们也建议你继续阅读。 你也许可以跟上进度,并在上下文学习过程当中掌握一些概念。)sql

在视图中进行数据库查询的笨方法

正如第三章详细介绍的那个在视图中输出 HTML 的笨方法(经过在视图里对文本直接硬编码HTML),在视图中也有笨方法能够从数据库中获取数据。 很简单: 用现有的任何 Python 类库执行一条 SQL 查询并对结果进行一些处理。shell

在本例的视图中,咱们使用了 MySQLdb 类库(能够从 http://www.djangoproject.com/r/python-mysql/ 得到)来链接 MySQL 数据库,取回一些记录,将它们提供给模板以显示一个网页:数据库

from django.shortcuts import render_to_response
import MySQLdb

def book_list(request):
    db = MySQLdb.connect(user='me', db='mydb', passwd='secret', host='localhost')
    cursor = db.cursor()
    cursor.execute('SELECT name FROM books ORDER BY name')
    names = [row[0] for row in cursor.fetchall()]
    db.close()
    return render_to_response('book_list.html', {'names': names})

这个方法可用,但很快一些问题将出如今你面前:django

  • 咱们将数据库链接参数硬行编码于代码之中。 理想状况下,这些参数应当保存在 Django 配置中。编程

  • 咱们不得不重复一样的代码: 建立数据库链接、建立数据库游标、执行某个语句、而后关闭数据库。 理想状况下,咱们所须要应该只是指定所需的结果。安全

  • 它把咱们栓死在 MySQL 之上。 若是过段时间,咱们要从 MySQL 换到 PostgreSQL,就不得不使用不一样的数据库适配器(例如 psycopg 而不是 MySQLdb ),改变链接参数,根据 SQL 语句的类型可能还要修改SQL 。 理想状况下,应对所使用的数据库服务器进行抽象,这样一来只在一处修改便可变换数据库服务器。 (若是你正在创建一个开源的Django应用程序来尽量让更多人使用的话,这个特性是很是适当的。)

正如你所期待的,Django数据库层正是致力于解决这些问题。 如下提早揭示了如何使用 Django 数据库 API 重写以前那个视图。

from django.shortcuts import render_to_response
from mysite.books.models import Book

def book_list(request):
    books = Book.objects.order_by('name')
    return render_to_response('book_list.html', {'books': books})

咱们将在本章稍后的地方解释这段代码。 目前而言,仅需对它有个大体的认识。

MTV 开发模式

在钻研更多代码以前,让咱们先花点时间考虑下 Django 数据驱动 Web 应用的整体设计。

咱们在前面章节提到过,Django 的设计鼓励松耦合及对应用程序中不一样部分的严格分割。 遵循这个理念的话,要想修改应用的某部分而不影响其它部分就比较容易了。 在视图函数中,咱们已经讨论了经过模板系统把业务逻辑和表现逻辑分隔开的重要性。 在数据库层中,咱们对数据访问逻辑也应用了一样的理念。

把数据存取逻辑、业务逻辑和表现逻辑组合在一块儿的概念有时被称为软件架构的 Model-View-Controller (MVC)模式。 在这个模式中, Model 表明数据存取层,View 表明的是系统中选择显示什么和怎么显示的部分,Controller 指的是系统中根据用户输入并视须要访问模型,以决定使用哪一个视图的那部分。

为何用缩写?

像 MVC 这样的明肯定义模式的主要用于改善开发人员之间的沟通。 比起告诉同事,“让咱们采用抽象的数据存取方式,而后单独划分一层来显示数据,而且在中间加上一个控制它的层”,一个通用的说法会让你收益,你只须要说:“咱们在这里使用MVC模式吧。”。

Django 牢牢地遵循这种 MVC 模式,能够称得上是一种 MVC 框架。 如下是 Django 中 M、V 和 C 各自的含义:

  • M ,数据存取部分,由django数据库层处理,本章要讲述的内容。

  • V ,选择显示哪些数据要显示以及怎样显示的部分,由视图和模板处理。

  • C ,根据用户输入委派视图的部分,由 Django 框架根据 URLconf 设置,对给定 URL 调用适当的 Python 函数。

因为 C 由框架自行处理,而 Django 里更关注的是模型(Model)、模板(Template)和视图(Views),Django 也被称为 MTV 框架 。在 MTV 开发模式中:

  • M 表明模型(Model),即数据存取层。 该层处理与数据相关的全部事务: 如何存取、如何验证有效性、包含哪些行为以及数据之间的关系等。

  • T 表明模板(Template),即表现层。 该层处理与表现相关的决定: 如何在页面或其余类型文档中进行显示。

  • V 表明视图(View),即业务逻辑层。 该层包含存取模型及调取恰当模板的相关逻辑。 你能够把它看做模型与模板之间的桥梁。

若是你熟悉其它的 MVC Web开发框架,比方说 Ruby on Rails,你可能会认为 Django 视图是控制器,而 Django 模板是视图。 很不幸,这是对 MVC 不一样诠释所引发的错误认识。 在 Django 对 MVC 的诠释中,视图用来描述要展示给用户的数据;不是数据 如何展示 ,并且展示 哪些 数据。 相比之下,Ruby on Rails 及一些同类框架提倡控制器负责决定向用户展示哪些数据,而视图则仅决定 如何 展示数据,而不是展示 哪些 数据。

两种诠释中没有哪一个更加正确一些。 重要的是要理解底层概念。

数据库配置

记住这些理念以后,让咱们来开始 Django 数据库层的探索。 首先,咱们须要作些初始配置;咱们须要告诉Django使用什么数据库以及如何链接数据库。

咱们假定你已经完成了数据库服务器的安装和激活,而且已经在其中建立了数据库(例如,用 CREATE DATABASE 语句)。 若是你使用SQLite,不须要这步安装,由于SQLite使用文件系统上的独立文件来存储数据。

象前面章节提到的 TEMPLATE_DIRS 同样,数据库配置也是在Django的配置文件里,缺省 是 settings.py 。 打开这个文件并查找数据库配置:

DATABASE_ENGINE = ''
DATABASE_NAME = ''
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_HOST = ''
DATABASE_PORT = ''

配置纲要以下。

DATABASE_ENGINE 告诉Django使用哪一个数据库引擎。 若是你在 Django 中使用数据库, DATABASE_ENGINE 必须是 Table 5-1 中所列出的值。

表 5-1. 数据库引擎设置
设置 数据库 所需适配器
`` postgresql`` PostgreSQL psycopg 1.x版, http://www.djangoproject.com/r/python-pgsql/1/
postgresql_psycopg2 PostgreSQL psycopg 2.x版, http://www.djangoproject.com/r/python-pgsql/
mysql MySQL MySQLdb , http://www.djangoproject.com/r/python-mysql/.
sqlite3 SQLite 若是使用Python 2.5+则不须要适配器。 不然就使用 pysqlite , http://www.djangoproject.com/r/python-sqlite/
oracle Oracle cx_Oracle , http://www.djangoproject.com/r/python-oracle/.

要注意的是不管选择使用哪一个数据库服务器,都必须下载和安装对应的数据库适配器。 访问表 5-1 中“所需适配器”一栏中的连接,可经过互联网免费获取这些适配器。 若是你使用Linux,你的发布包管理系统会提供合适的包。 好比说查找`` python-postgresql`` 或者`` python-psycopg`` 的软件包。

配置示例:

DATABASE_ENGINE = 'postgresql_psycopg2'

DATABASE_NAME 将数据库名称告知 Django 。 例如:

DATABASE_NAME = 'mydb'

若是使用 SQLite,请对数据库文件指定完整的文件系统路径。 例如:

DATABASE_NAME = '/home/django/mydata.db'

在这个例子中,咱们将SQLite数据库放在/home/django目录下,你能够任意选用最合适你的目录。

DATABASE_USER 告诉 Django 用哪一个用户链接数据库。 例如: 若是用SQLite,空白便可。

DATABASE_PASSWORD 告诉Django链接用户的密码。 SQLite 用空密码便可。

DATABASE_HOST 告诉 Django 链接哪一台主机的数据库服务器。 若是数据库与 Django 安装于同一台计算机(即本机),可将此项保留空白。 若是你使用SQLite,此项留空。

此处的 MySQL 是一个特例。 若是使用的是 MySQL 且该项设置值由斜杠( '/' )开头,MySQL 将经过 Unix socket 来链接指定的套接字,例如:

DATABASE_HOST = '/var/run/mysql'

一旦在输入了那些设置并保存以后应当测试一下你的配置。 咱们能够在`` mysite`` 项目目录下执行上章所提到的`` python manage.py shell`` 来进行测试。 (咱们上一章提到过在,`` manager.py shell`` 命令是以正确Django配置启用Python交互解释器的一种方法。 这个方法在这里是颇有必要的,由于Django须要知道加载哪一个配置文件来获取数据库链接信息。)

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

>>> from django.db import connection
>>> cursor = connection.cursor()

若是没有显示什么错误信息,那么你的数据库配置是正确的。 不然,你就得 查看错误信息来纠正错误。 表 5-2 是一些常见错误。

表 5-2. 数据库配置错误信息
错误信息 解决方法
You haven’t set the DATABASE_ENGINE setting yet. 不要以空字符串配置`` DATABASE_ENGINE`` 的值。 表格 5-1 列出可用的值。
Environment variable DJANGO_SETTINGS_MODULE is undefined. 使用`` python manager.py shell`` 命令启动交互解释器,不要以`` python`` 命令直接启动交互解释器。
Error loading _____ module: No module named _____. 未安装合适的数据库适配器 (例如, psycopg 或 MySQLdb )。Django并不自带适配器,因此你得本身下载安装。
_____ isn’t an available database backend. DATABASE_ENGINE 配置成前面提到的合法的数据库引擎。 也许是拼写错误?
database _____ does not exist 设置`` DATABASE_NAME`` 指向存在的数据库,或者先在数据库客户端中执行合适的`` CREATE DATABASE`` 语句建立数据库。
role _____ does not exist 设置`` DATABASE_USER`` 指向存在的用户,或者先在数据库客户端中执建立用户。
could not connect to server 查看DATABASE_HOST和DATABASE_PORT是否已正确配置,并确认数据库服务器是否已正常运行。

第一个应用程序

你如今已经确认数据库链接正常工做了,让咱们来建立一个 Django app-一个包含模型,视图和Django代码,而且形式为独立Python包的完整Django应用。

在这里要先解释一些术语,初学者可能会混淆它们。 在第二章咱们已经建立了 project , 那么 project 和 app 之间到底有什么不一样呢?它们的区别就是一个是配置另外一个是 代码:

一个project包含不少个Django app以及对它们的配置。

技术上,project的做用是提供配置文件,比方说哪里定义数据库链接信息, 安装的app列表, TEMPLATE_DIRS ,等等。

一个app是一套Django功能的集合,一般包括模型和视图,按Python的包结构的方式存在。

例如,Django自己内建有一些app,例如注释系统和自动管理界面。 app的一个关键点是它们是很容易移植到其余project和被多个project复用。

对于如何架构Django代码并无快速成套的规则。 若是你只是建造一个简单的Web站点,那么可能你只须要一个app就能够了; 但若是是一个包含许多不相关的模块的复杂的网站,例如电子商务和社区之类的站点,那么你可能须要把这些模块划分红不一样的app,以便之后复用。

不错,你能够不用建立app,这一点应经被咱们以前编写的视图函数的例子证实了 。 在那些例子中,咱们只是简单的建立了一个称为views.py的文件,编写了一些函数并在URLconf中设置了各个函数的映射。 这些状况都不须要使用apps。

可是,系统对app有一个约定: 若是你使用了Django的数据库层(模型),你 必须建立一个Django app。 模型必须存放在apps中。 所以,为了开始建造 咱们的模型,咱们必须建立一个新的app。

在`` mysite`` 项目文件下输入下面的命令来建立`` books`` app:

python manage.py startapp books

这个命令并无输出什么,它只在 mysite 的目录里建立了一个 books 目录。 让咱们来看看这个目录的内容:

books/
    __init__.py
    models.py
    tests.py
    views.py

这个目录包含了这个app的模型和视图。

使用你最喜欢的文本编辑器查看一下 models.py 和 views.py 文件的内容。 它们都是空的,除了 models.py 里有一个 import。这就是你Django app的基础。

在Python代码里定义模型

咱们早些时候谈到。MTV里的M表明模型。 Django模型是用Python代码形式表述的数据在数据库中的定义。 对数据层来讲它等同于 CREATE TABLE 语句,只不过执行的是Python代码而不是 SQL,并且还包含了比数据库字段定义更多的含义。 Django用模型在后台执行SQL代码并把结果用Python的数据结构来描述。 Django也使用模型来呈现SQL没法处理的高级概念。

若是你对数据库很熟悉,你可能立刻就会想到,用Python  SQL来定义数据模型是否是有点多余? Django这样作是有下面几个缘由的:

自省(运行时自动识别数据库)会致使过载和有数据完整性问题。 为了提供方便的数据访问API, Django须要以 某种方式 知道数据库层内部信息,有两种实现方式。 第一种方式是用Python明确地定义数据模型,第二种方式是经过自省来自动侦测识别数据模型。

第二种方式看起来更清晰,由于数据表信息只存放在一个地方-数据库里,可是会带来一些问题。 首先,运行时扫描数据库会带来严重的系统过载。 若是每一个请求都要扫描数据库的表结构,或者即使是 服务启动时作一次都是会带来不能接受的系统过载。 (有人认为这个程度的系统过载是能够接受的,而Django开发者的目标是尽量地下降框架的系统过载)。第二,某些数据库,尤为是老版本的MySQL,并未完整存储那些精确的自省元数据。

编写Python代码是很是有趣的,保持用Python的方式思考会避免你的大脑在不一样领域来回切换。 尽量的保持在单一的编程环境/思想状态下能够帮助你提升生产率。 不得不去重复写SQL,再写Python代码,再写SQL,…,会让你头都要裂了。

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

SQL只能描述特定类型的数据字段。 例如,大多数数据库都没有专用的字段类型来描述Email地址、URL。 而用Django的模型能够作到这一点。 好处就是高级的数据类型带来更高的效率和更好的代码复用。

SQL还有在不一样数据库平台的兼容性问题。 发布Web应用的时候,使用Python模块描述数据库结构信息能够避免为MySQL, PostgreSQL, and SQLite编写不一样的CREATE TABLE

固然,这个方法也有一个缺点,就是Python代码和数据库表的同步问题。 若是你修改了一个Django模型, 你要本身来修改数据库来保证和模型同步。 咱们将在稍后讲解解决这个问题的几种策略。

最后,咱们要提醒你Django提供了实用工具来从现有的数据库表中自动扫描生成模型。 这对已有的数据库来讲是很是快捷有用的。 咱们将在第18章中对此进行讨论。

第一个模型

在本章和后续章节里,咱们把注意力放在一个基本的 书籍/做者/出版商 数据库结构上。 咱们这样作是由于 这是一个众所周知的例子,不少SQL有关的书籍也经常使用这个举例。 你如今看的这本书也是由做者 创做再由出版商出版的哦!

咱们来假定下面的这些概念、字段和关系:

  • 一个做者有姓,有名及email地址。

  • 出版商有名称,地址,所在城市、省,国家,网站。

  • 书籍有书名和出版日期。 它有一个或多个做者(和做者是多对多的关联关系[many-to-many]), 只有一个出版商(和出版商是一对多的关联关系[one-to-many],也被称做外键[foreign key])

第一步是用Python代码来描述它们。 打开由`` startapp`` 命令建立的models.py 并输入下面的内容:

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

class Author(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=40)
    email = models.EmailField()

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()

让咱们来快速讲解一下这些代码的含义。 首先要注意的事是每一个数据模型都是 django.db.models.Model 的子类。它的父类 Model 包含了全部必要的和数据库交互的方法,并提供了一个简洁漂亮的定义数据库字段的语法。 信不信由你,这些就是咱们须要编写的经过Django存取基本数据的全部代码。

每一个模型至关于单个数据库表,每一个属性也是这个表中的一个字段。 属性名就是字段名,它的类型(例如 CharField )至关于数据库的字段类型 (例如 varchar )。例如, Publisher 模块等同于下面这张表(用PostgreSQL的 CREATE TABLE 语法描述):

CREATE TABLE "books_publisher" (
    "id" serial 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
);

事实上,正如过一下子咱们所要展现的,Django 能够自动生成这些 CREATE TABLE 语句。

“每一个数据库表对应一个类”这条规则的例外状况是多对多关系。 在咱们的范例模型中, Book 有一个 多对多字段 叫作 authors 。 该字段代表一本书籍有一个或多个做者,但 Book 数据库表却并无 authors 字段。 相反,Django建立了一个额外的表(多对多链接表)来处理书籍和做者之间的映射关系。

请查看附录 B 了解全部的字段类型和模型语法选项。

最后须要注意的是,咱们并无显式地为这些模型定义任何主键。 除非你单独指明,不然Django会自动为每一个模型生成一个自增加的整数主键字段每一个Django模型都要求有单独的主键。id

模型安装

完成这些代码以后,如今让咱们来在数据库中建立这些表。 要完成该项工做,第一步是在 Django 项目中 激活 这些模型。 将 books app 添加到配置文件的已安装应用列表中便可完成此步骤。

再次编辑 settings.py 文件, 找到 INSTALLED_APPS 设置。 INSTALLED_APPS 告诉 Django 项目哪些 app 处于激活状态。 缺省状况下以下所示:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
)

把这四个设置前面加#临时注释起来。 (这四个app是常用到的,咱们将在后续章节里讨论如何使用它们)。同时,注释掉MIDDLEWARE_CLASSES的默认设置条目,由于这些条目是依赖于刚才咱们刚在INSTALLED_APPS注释掉的apps。 而后,添加`` ‘mysite.books’`` 到`` INSTALLED_APPS`` 的末尾,此时设置的内容看起来应该是这样的:

MIDDLEWARE_CLASSES = (
    # 'django.middleware.common.CommonMiddleware',
    # 'django.contrib.sessions.middleware.SessionMiddleware',
    # 'django.contrib.auth.middleware.AuthenticationMiddleware',
)

INSTALLED_APPS = (
    # 'django.contrib.auth',
    # 'django.contrib.contenttypes',
    # 'django.contrib.sessions',
    # 'django.contrib.sites',
    'mysite.books',
)

(就像咱们在上一章设置TEMPLATE_DIRS所提到的逗号,一样在INSTALLED_APPS的末尾也需添加一个逗号,由于这是个单元素的元组。 另外,本书的做者喜欢在 每个 tuple元素后面加一个逗号,无论它是否是 只有一个元素。 这是为了不忘了加逗号,并且也没什么坏处。)

'mysite.books'指示咱们正在编写的books app。 INSTALLED_APPS 中的每一个app都使用 Python的路径描述,包的路径,用小数点“.”间隔。

如今咱们能够建立数据库表了。 首先,用下面的命令验证模型的有效性:

python manage.py validate

validate 命令检查你的模型的语法和逻辑是否正确。 若是一切正常,你会看到 0 errors found 消息。若是出错,请检查你输入的模型代码。 错误输出会给出很是有用的错误信息来帮助你修正你的模型。

一旦你以为你的模型可能有问题,运行 python manage.py validate 。 它能够帮助你捕获一些常见的模型定义错误。

模型确认没问题了,运行下面的命令来生成 CREATE TABLE 语句(若是你使用的是Unix,那么能够启用语法高亮):

python manage.py sqlall books

在这个命令行中, books 是app的名称。 和你运行 manage.py startapp 中的同样。执行以后,输出以下:

BEGIN;
CREATE TABLE "books_publisher" (
    "id" serial 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" serial 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" (
    "id" serial NOT NULL PRIMARY KEY,
    "title" varchar(100) NOT NULL,
    "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id") DEFERRABLE INITIALLY DEFERRED,
    "publication_date" date NOT NULL
)
;
CREATE TABLE "books_book_authors" (
    "id" serial NOT NULL PRIMARY KEY,
    "book_id" integer NOT NULL REFERENCES "books_book" ("id") DEFERRABLE INITIALLY DEFERRED,
    "author_id" integer NOT NULL REFERENCES "books_author" ("id") DEFERRABLE INITIALLY DEFERRED,
    UNIQUE ("book_id", "author_id")
)
;
CREATE INDEX "books_book_publisher_id" ON "books_book" ("publisher_id");
COMMIT;

注意:

  • 自动生成的表名是app名称( books )和模型的小写名称 ( publisher , book , author )的组合。你能够参考附录B重写这个规则。

  • 咱们前面已经提到,Django为每一个表格自动添加加了一个 id 主键, 你能够从新设置它。

  • 按约定,Django添加 "_id" 后缀到外键字段名。 你猜对了,这个一样是能够自定义的。

  • 外键是用 REFERENCES 语句明肯定义的。

  • 这些 CREATE TABLE 语句会根据你的数据库而做调整,这样象数据库特定的一些字段例如:(MySQL),auto_increment(PostgreSQL),serial(SQLite),都会自动生成。integer primary key 一样的,字段名称也是自动处理(例如单引号还好是双引号)。 例子中的输出是基于PostgreSQL语法的。

sqlall 命令并无在数据库中真正建立数据表,只是把SQL语句段打印出来,这样你能够看到Django究竟会作些什么。 若是你想这么作的话,你能够把那些SQL语句复制到你的数据库客户端执行,或者经过Unix管道直接进行操做(例如,`` python manager.py sqlall books | psql mydb`` )。不过,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 ,什么也没发生,由于你没有添加新的模型或者 添加新的app。所以,运行python manage.py syncdb老是安全的,由于它不会重复执行SQL语句。

若是你有兴趣,花点时间用你的SQL客户端登陆进数据库服务器看看刚才Django建立的数据表。 你能够手动启动命令行客户端(例如,执行PostgreSQL的`` psql`` 命令),也能够执行 `` python manage.py dbshell``  ,这个命令将依据`` DATABASE_SERVER`` 的里设置自动检测使用哪一种命令行客户端。 常言说,后来者居上。

基本数据访问

一旦你建立了模型,Django自动为这些模型提供了高级的Python API。 运行 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>]

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

  • 首先,导入Publisher模型类, 经过这个类咱们能够与包含 出版社 的数据表进行交互。

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

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

  • 最后,使用`` Publisher.objects`` 属性从数据库取出出版商的信息,这个属性能够认为是包含出版商的记录集。 这个属性有许多方法, 这里先介绍调用`` Publisher.objects.all()`` 方法获取数据库中`` Publisher`` 类的全部对象。这个操做的幕后,Django执行了一条SQL `` SELECT`` 语句。

这里有一个值得注意的地方,在这个例子可能并未清晰地展现。 当你使用Django modle API建立对象时Django并未将对象保存至数据库内,除非你调用`` save()`` 方法:

p1 = Publisher(...)
# At this point, p1 is not saved to the database yet!
p1.save()
# Now it is.

若是须要一步完成对象的建立与存储至数据库,就使用`` objects.create()`` 方法。 下面的例子与以前的例子等价:

>>> p1 = Publisher.objects.create(name='Apress',
...     address='2855 Telegraph Avenue',
...     city='Berkeley', state_province='CA', country='U.S.A.',
...     website='http://www.apress.com/')
>>> p2 = Publisher.objects.create(name="O'Reilly",
...     address='10 Fawcett St.', city='Cambridge',
...     state_province='MA', country='U.S.A.',
...     website='http://www.oreilly.com/')
>>> publisher_list = Publisher.objects.all()
>>> publisher_list

固然,你确定想执行更多的Django数据库API试试看,不过,仍是让咱们先解决一点烦人的小问题。

添加模块的字符串表现

当咱们打印整个publisher列表时,咱们没有获得想要的有用信息,没法把````对象区分开来:

System Message: WARNING/2 (<string>, line 872); backlink

Inline literal start-string without end-string.

System Message: WARNING/2 (<string>, line 872); backlink

Inline literal start-string without end-string.

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

咱们能够简单解决这个问题,只须要为Publisher 对象添加一个方法 __unicode__() 。 __unicode__() 方法告诉Python如何将对象以unicode的方式显示出来。 为以上三个模型添加__unicode__()方法后,就能够看到效果了:

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

就象你看到的同样, __unicode__() 方法能够进行任何处理来返回对一个对象的字符串表示。 PublisherBook对象的__unicode__()方法简单地返回各自的名称和标题,Author对象的__unicode__()方法则稍微复杂一些,它将first_namelast_name字段值以空格链接后再返回。

对__unicode__()的惟一要求就是它要返回一个unicode对象 若是`` __unicode__()`` 方法未返回一个Unicode对象,而返回好比说一个整型数字,那么Python将抛出一个`` TypeError`` 错误,并提示:”coercing to Unicode: need string or buffer, int found” 。

Unicode对象

什么是Unicode对象呢?

你能够认为unicode对象就是一个Python字符串,它能够处理上百万不一样类别的字符——从古老版本的Latin字符到非Latin字符,再到曲折的引用和艰涩的符号。

普通的python字符串是通过编码的,意思就是它们使用了某种编码方式(如 ASCII,ISO-8859-1或者UTF-8)来编码。 若是你把奇特的字符(其它任何超出标准128个如0-9和A-Z之类的ASCII字符)保存在一个普通的Python字符串里,你必定要跟踪你的字符串是 用什么编码的,不然这些奇特的字符可能会在显示或者打印的时候出现乱码。 当你尝试要将用某种编码保存的数据结合到另一种编码的数据中,或者你想要把它显示在已经假定了某种编码的程序中的时候,问题就会发生。 咱们都已经见到过网页和邮件被???弄得乱七八糟。 ?????? 或者其它出如今奇怪位置的字符:这通常来讲就是存在编码问题了。

可是Unicode对象并无编码。它们使用Unicode,一个一致的,通用的字符编码集。 当你在Python中处理Unicode对象的时候,你能够直接将它们混合使用和互相匹配而没必要去考虑编码细节。

Django 在其内部的各个方面都使用到了 Unicode 对象。 模型 对象中,检索匹配方面的操做使用的是 Unicode 对象,视图 函数之间的交互使用的是 Unicode 对象,模板的渲染也是用的 Unicode 对象。 一般,咱们没必要担忧编码是否正确,后台会处理的很好。

注意,咱们这里只是对Unicode对象进行很是浅显的概述,若要深刻了解你可能须要查阅相关的资料。 这是一个很好的起点:http://www.joelonsoftware.com/articles/Unicode.html。

为了让咱们的修改生效,先退出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__() 就是一个例子来演示模型知道怎么显示它们本身。

插入和更新数据

你已经知道怎么作了: 先使用一些关键参数建立对象实例,以下:

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

这个对象实例并 没有 对数据库作修改。 在调用`` save()`` 方法以前,记录并无保存至数据库,像这样:

>>> p.save()

在SQL里,这大体能够转换成这样:

INSERT INTO books_publisher
    (name, address, city, state_province, country, website)
VALUES
    ('Apress', '2855 Telegraph Ave.', 'Berkeley', 'CA',
     'U.S.A.', 'http://www.apress.com/');

由于 Publisher 模型有一个自动增长的主键 id ,因此第一次调用 save() 还多作了一件事: 计算这个主键的值并把它赋值给这个对象实例:

>>> p.id
52    # this will differ based on your own data

接下来再调用 save() 将不会建立新的记录,而只是修改记录内容(也就是 执行 UPDATE SQL语句,而不是 INSERT 语句):

>>> p.name = 'Apress Publishing'
>>> p.save()

前面执行的 save() 至关于下面的SQL语句:

UPDATE books_publisher SET
    name = 'Apress Publishing',
    address = '2855 Telegraph Ave.',
    city = 'Berkeley',
    state_province = 'CA',
    country = 'U.S.A.',
    website = 'http://www.apress.com'
WHERE id = 52;

注意,并非只更新修改过的那个字段,全部的字段都会被更新。 这个操做有可能引发竞态条件,这取决于你的应用程序。 请参阅后面的“更新多个对象”小节以了解如何实现这种轻量的修改(只修改对象的部分字段)。

UPDATE books_publisher SET
    name = 'Apress Publishing'
WHERE id=52;

选择对象

固然,建立新的数据库,并更新之中的数据是必要的,可是,对于 Web 应用程序来讲,更多的时候是在检索查询数据库。 咱们已经知道如何从一个给定的模型中取出全部记录:

>>> Publisher.objects.all()
[<Publisher: Apress>, <Publisher: O'Reilly>]

这至关于这个SQL语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher;

注意

注意到Django在选择全部数据时并无使用 SELECT* ,而是显式列出了全部字段。 设计的时候就是这样: SELECT* 会更慢,并且最重要的是列出全部字段遵循了Python 界的一个信条: 明言胜于暗示。

有关Python之禅(戒律) :-),在Python提示行输入 import this 试试看。

让咱们来仔细看看 Publisher.objects.all() 这行的每一个部分:

首先,咱们有一个已定义的模型 Publisher 。没什么好奇怪的: 你想要查找数据, 你就用模型来得到数据。

而后,是objects属性。 它被称为管理器,咱们将在第10章中详细讨论它。 目前,咱们只需了解管理器管理着全部针对数据包含、还有最重要的数据查询的表格级操做。

全部的模型都自动拥有一个 objects 管理器;你能够在想要查找数据时使用它。

最后,还有 all() 方法。这个方法返回返回数据库中全部的记录。 尽管这个对象 看起来 象一个列表(list),它实际是一个 QuerySet 对象, 这个对象是数据库中一些记录的集合。 附录C将详细描述QuerySet。 如今,咱们就先当它是一个仿真列表对象好了。

全部的数据库查找都遵循一个通用模式:

数据过滤

咱们不多会一次性从数据库中取出全部的数据;一般都只针对一部分数据进行操做。 在Django API中,咱们可使用`` filter()`` 方法对数据进行过滤:

>>> Publisher.objects.filter(name='Apress')
[<Publisher: Apress>]

filter() 根据关键字参数来转换成 WHERE SQL语句。 前面这个例子 至关于这样:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';

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

>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")
[<Publisher: Apress>]

多个参数会被转换成 AND SQL从句, 所以上面的代码能够转化成这样:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE country = 'U.S.A.'
AND state_province = 'CA';

注意,SQL缺省的 = 操做符是精确匹配的, 其余类型的查找也可使用:

>>> Publisher.objects.filter(name__contains="press")
[<Publisher: Apress>]

在 name 和 contains 之间有双下划线。和Python同样,Django也使用双下划线来代表会进行一些魔术般的操做。这里,contains部分会被Django翻译成LIKE语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name LIKE '%press%';

其余的一些查找类型有:icontains(大小写无关的LIKE),startswithendswith, 还有range(SQLBETWEEN查询)。 附录C详细描述了全部的查找类型。

获取单个对象

上面的例子中`` filter()`` 函数返回一个记录集,这个记录集是一个列表。 相对列表来讲,有些时候咱们更须要获取单个的对象, `` get()`` 方法就是在此时使用的:

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

这样,就返回了单个对象,而不是列表(更准确的说,QuerySet)。 因此,若是结果是多个对象,会致使抛出异常:

>>> Publisher.objects.get(country="U.S.A.")
Traceback (most recent call last):
    ...
MultipleObjectsReturned: get() returned more than one Publisher --
    it returned 2! Lookup parameters were {'country': 'U.S.A.'}

若是查询没有返回结果也会抛出异常:

>>> Publisher.objects.get(name="Penguin")
Traceback (most recent call last):
    ...
DoesNotExist: Publisher matching query does not exist.

这个 DoesNotExist 异常 是 Publisher 这个 model 类的一个属性,即 Publisher.DoesNotExist。在你的应用中,你能够捕获并处理这个异常,像这样:

try:
    p = Publisher.objects.get(name='Apress')
except Publisher.DoesNotExist:
    print "Apress isn't in the database yet."
else:
    print "Apress is in the database."

数据排序

在运行前面的例子中,你可能已经注意到返回的结果是无序的。 咱们尚未告诉数据库 怎样对结果进行排序,因此咱们返回的结果是无序的。

在你的 Django 应用中,你或许但愿根据某字段的值对检索结果排序,好比说,按字母顺序。 那么,使用 order_by() 这个方法就能够搞定了。

>>> Publisher.objects.order_by("name")
[<Publisher: Apress>, <Publisher: O'Reilly>]

跟之前的 all() 例子差很少,SQL语句里多了指定排序的部分:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name;

咱们能够对任意字段进行排序:

>>> Publisher.objects.order_by("address")
[<Publisher: O'Reilly>, <Publisher: Apress>]

>>> Publisher.objects.order_by("state_province")
[<Publisher: Apress>, <Publisher: O'Reilly>]

若是须要以多个字段为标准进行排序(第二个字段会在第一个字段的值相同的状况下被使用到),使用多个参数就能够了,以下:

>>> Publisher.objects.order_by("state_province", "address")
 [<Publisher: Apress>, <Publisher: O'Reilly>]

咱们还能够指定逆向排序,在前面加一个减号 - 前缀:

>>> Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]

尽管很灵活,可是每次都要用 order_by() 显得有点啰嗦。 大多数时间你一般只会对某些 字段进行排序。 在这种状况下,Django让你能够指定模型的缺省排序方式:

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 字段排序。

连锁查询

咱们已经知道如何对数据进行过滤和排序。 固然,一般咱们须要同时进行过滤和排序查询的操做。 所以,你能够简单地写成这种“链式”的形式:

>>> Publisher.objects.filter(country="U.S.A.").order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]

你应该没猜错,转换成SQL查询就是 WHERE 和 ORDER BY 的组合:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE country = 'U.S.A'
ORDER BY name DESC;

限制返回的数据

另外一个经常使用的需求就是取出固定数目的记录。 想象一下你有成千上万的出版商在你的数据库里, 可是你只想显示第一个。 你可使用标准的Python列表裁剪语句:

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

这至关于:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name
LIMIT 1;

相似的,你能够用Python的range-slicing语法来取出数据的特定子集:

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

这个例子返回两个对象,等同于如下的SQL语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name
OFFSET 0 LIMIT 2;

注意,不支持Python的负索引(negative slicing):

>>> Publisher.objects.order_by('name')[-1]
Traceback (most recent call last):
  ...
AssertionError: Negative indexing is not supported.

虽然不支持负索引,可是咱们可使用其余的方法。 好比,稍微修改 order_by() 语句来实现:

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

更新多个对象

在“插入和更新数据”小节中,咱们有提到模型的save()方法,这个方法会更新一行里的全部列。 而某些状况下,咱们只须要更新行里的某几列。

例如说咱们如今想要将Apress Publisher的名称由原来的”Apress”更改成”Apress Publishing”。若使用save()方法,如:

>>> p = Publisher.objects.get(name='Apress')
>>> p.name = 'Apress Publishing'
>>> p.save()

这等同于以下SQL语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';

UPDATE books_publisher SET
    name = 'Apress Publishing',
    address = '2855 Telegraph Ave.',
    city = 'Berkeley',
    state_province = 'CA',
    country = 'U.S.A.',
    website = 'http://www.apress.com'
WHERE id = 52;

(注意在这里咱们假设Apress的ID为52)

在这个例子里咱们能够看到Django的save()方法更新了不只仅是name列的值,还有更新了 全部的列。 若name之外的列有可能会被其余的进程所改动的状况下,只更改name列显然是更加明智的。 更改某一指定的列,咱们能够调用结果集(QuerySet)对象的update()方法: 示例以下:

>>> Publisher.objects.filter(id=52).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。

删除对象

删除数据库中的对象只需调用该对象的delete()方法便可:

>>> p = Publisher.objects.get(name="O'Reilly")
>>> p.delete()
>>> Publisher.objects.all()
[<Publisher: Apress Publishing>]

一样咱们能够在结果集上调用delete()方法同时删除多条记录。这一点与咱们上一小节提到的update()方法类似:

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

删除数据时要谨慎! 为了预防误删除掉某一个表内的全部数据,Django要求在删除表内全部数据时显示使用all()。 好比,下面的操做将会出错:

>>> Publisher.objects.delete()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'Manager' object has no attribute 'delete'

而一旦使用all()方法,全部数据将会被删除:

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

若是只须要删除部分的数据,就不须要调用all()方法。再看一下以前的例子:

>>> Publisher.objects.filter(country='USA').delete()
相关文章
相关标签/搜索