In Chapter 3, we covered the fundamentals of building dynamic Web sites with Django: setting up views and URLconfs. As we explained, a view is responsible for doing some arbitrary logic , and then returning a response. In the example, our arbitrary logic was to calculate the current date and time.
在第三章,我们讲述了用 Django 建造网站的基本途径:建立视图和 URLConf 。正如我们所阐述的,视图负责处理 一些任意逻辑 ,然后返回响应结果。在范例中,我们的任意逻辑就是计算当前的日期和时间。
In modern Web applications, the arbitrary logic often involves interacting with a database. Behind the scenes, a database-driven Web site connects to a database server, retrieves some data out of it, and displays that data, nicely formatted, on a Web page. Or, similarly, the site could provide functionality that lets site visitors populate the database on their own.
在当代 Web 应用中,任意逻辑经常牵涉到与数据库的交互。 数据库驱动网站 在后台连接数据库服务器,从中取出一些数据,然后在 Web 页面用漂亮的格式展示这些数据。或者,站点也提供让访问者自行填充数据库的功能。
Many complex Web sites provide some combination of the two. Amazon.com, for instance, is a great example of a database-driven site. Each product page is essentially a query into Amazons product database formatted as HTML, and when you post a customer review, it gets inserted into the database of reviews.
许多复杂的网站都提供了以上两个功能的某种结合。例如 Amazon.com 就是一个数据库驱动站点的良好范例。本质上,每个产品页都是从数据库中取出的数据被格式化为 HTML,而当你发表客户评论时,该评论被插入评论数据库中。
Django is well suited for making database-driven Web sites, as it comes with easy yet powerful ways of performing database queries using Python. This chapter explains that functionality: Djangos database layer.
由于先天具备 Python 简单而强大的数据库查询执行方法,Django 非常适合开发数据库驱动网站。本章深入介绍了该功能:Django 数据库层。
(Note: While its not strictly necessary to know basic database theory and SQL in order to use Djangos database layer, its highly recommended. An introduction to those concepts is beyond the scope of this book, but keep reading even if youre a database newbie. Youll probably be able to follow along and grasp concepts based on the context.)
(注意:尽管对 Django 数据库层的使用中并不特别强调,我们还是强烈建议掌握一些数据库和 SQL 原理。对这些概念的介绍超越了本书的范围,但就算你是数据库方面的菜鸟,我们也建议你继续阅读。你也许能够跟上进度,并在上下文学习过程中掌握一些概念。)
Just as Chapter 3 detailed a dumb way to produce output within a view (by hard-coding the text directly within the view), theres a dumb way to retrieve data from a database in a view. Its simple: just use any existing Python library to execute an SQL query and do something with the results.
正如第三章详细介绍的那个在视图中输出 HTML 的笨方法(通过在视图里对文本直接硬编码HTML),在视图中也有笨方法可以从数据库中获取数据。很简单:用现有的任何 Python 类库执行一条 SQL 查询并对结果进行一些处理。
In this example view, we use the MySQLdb library (available at http://www.djangoproject.com/r/python-mysql/) to connect to a MySQL database, retrieve some records, and feed them to a template for display as a Web page:
在本例的视图中,我们使用了 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})
This approach works, but some problems should jump out at you immediately:
这个方法可用,但很快一些问题将出现在你面前:
Were hard-coding the database connection parameters. Ideally, these parameters would be stored in the Django configuration.
我们将数据库连接参数硬行编码于代码之中。理想情况下,这些参数应当保存在 Django 配置中。
Were having to write a fair bit of boilerplate code: creating a connection, creating a cursor, executing a statement, and closing the connection. Ideally, all wed have to do is specify which results we wanted.
我们不得不重复同样的代码:创建数据库连接、创建数据库游标、执行某个语句、然后关闭数据库。理想情况下,我们所需要应该只是指定所需的结果。
It ties us to MySQL. If, down the road, we switch from MySQL to PostgreSQL, well have to use a different database adapter (e.g., psycopg rather than MySQLdb ), alter the connection parameters, and depending on the nature of the SQL statement possibly rewrite the SQL. Ideally, the database server were using would be abstracted, so that a database server change could be made in a single place.
它把我们栓死在 MySQL 之上。如果过段时间,我们要从 MySQL 换到 PostgreSQL,就不得不使用不同的数据库适配器(例如 psycopg 而不是 MySQLdb ),改变连接参数,根据 SQL 语句的类型可能还要修改SQL 。理想情况下,应对所使用的数据库服务器进行抽象,这样一来只在一处修改即可变换数据库服务器。
As you might expect, Djangos database layer aims to solve these problems. Heres a sneak preview of how the previous view can be rewritten using Djangos database API:
正如你所期待的,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})
Well explain this code a little later in the chapter. For now, just get a feel for how it looks.
我们将在本章稍后的地方解释这段代码。目前而言,仅需对它有个大致的认识。
Before we delve into any more code, lets take a moment to consider the overall design of a database-driven Django Web application.
在钻研更多代码之前,让我们先花点时间考虑下 Django 数据驱动 Web 应用的总体设计。
As we mentioned in previous chapters, Django is designed to encourage loose coupling and strict separation between pieces of an application. If you follow this philosophy, its easy to make changes to one particular piece of the application without affecting the other pieces. In view functions, for instance, we discussed the importance of separating the business logic from the presentation logic by using a template system. With the database layer, were applying that same philosophy to data access logic.
我们在前面章节提到过,Django 的设计鼓励松耦合及对应用程序中不同部分的严格分割。遵循这个理念的话,要想修改应用的某部分而不影响其它部分就比较容易了。在视图函数中,我们已经讨论了通过模板系统把业务逻辑和表现逻辑分隔开的重要性。在数据库层中,我们对数据访问逻辑也应用了同样的理念。
Those three pieces together data access logic, business logic, and presentation logic comprise a concept thats sometimes called the Model-View-Controller (MVC) pattern of software architecture. In this pattern, Model refers to the data access layer, View refers to the part of the system that selects what to display and how to display it, and Controller refers to the part of the system that decides which view to use, depending on user input, accessing the model as needed.
把数据存取逻辑、业务逻辑和表现逻辑组合在一起的概念有时被称为软件架构的 Model-View-Controller (MVC)模式。在这个模式中, Model 代表数据存取层,View 代表的是系统中选择显示什么和怎么显示的部分,Controller 指的是系统中根据用户输入并视需要访问模型,以决定使用哪个视图的那部分。
Why the Acronym?
为什么使用缩写?
The goal of explicitly defining patterns such as MVC is mostly to streamline communication among developers. Instead of having to tell your coworkers, Lets make an abstraction of the data access, then lets have a separate layer that handles data display, and lets put a layer in the middle that regulates this, you can take advantage of a shared vocabulary and say, Lets use the MVC pattern here.
像 MVC 这样的明确定义模式的主要用于改善开发人员之间的沟通。与其告诉同事:“让我们对数据存取进行抽象,用单独一层负责数据显示,然后在中间放置一层来进行控制”,还不如利用通用的词汇告诉他们:“让我们在这里使用 MVC 模式吧”。
Django follows this MVC pattern closely enough that it can be called an MVC framework. Heres roughly how the M, V, and C break down in Django:
Django 紧紧地遵循这种 MVC 模式,可以称得上是一种 MVC 框架。以下是 Django 中 M、V 和 C 各自的含义:
M , the data-access portion, is handled by Djangos database layer, which is described in this chapter.
M ,数据存取部分,由django数据库层处理,本章要讲述的内容。
V , the portion that selects which data to display and how to display it, is handled by views and templates.
V ,选择显示哪些数据要及怎样显示的部分,由视图和模板处理。
C , the portion that delegates to a view depending on user input, is handled by the framework itself by following your URLconf and calling the appropriate Python function for the given URL.
C ,根据用户输入委派视图的部分,由 Django 框架通过按照 URLconf 设置,对给定 URL 调用合适的 python 函数来自行处理。
Because the C is handled by the framework itself and most of the excitement in Django happens in models, templates, and views, Django has been referred to as an MTV framework . In the MTV development pattern,
由于 C 由框架自行处理,而 Django 里更关注的是模型(Model)、模板(Template)和视图(Views),Django 也被称为 MTV 框架 。在 MTV 开发模式中:
M stands for Model, the data access layer. This layer contains anything and everything about the data: how to access it, how to validate it, which behaviors it has, and the relationships between the data.
M 代表模型(Model),即数据存取层。该层处理与数据相关的所有事务:如何存取、如何确认有效性、包含哪些行为以及数据之间的关系等。
T stands for Template, the presentation layer. This layer contains presentation-related decisions: how something should be displayed on a Web page or other type of document.
T 代表模板(Template),即表现层。该层处理与表现相关的决定:如何在页面或其他类型文档中进行显示。
V stands for View, the business logic layer. This layer contains the logic that access the model and defers to the appropriate template(s). You can think of it as the bridge between models and templates.
V代表View,业务逻辑层。这一层包含访问模型的逻辑和按照模板显示。你可以认为它是模型和模板的桥梁。
If youre familiar with other MVC Web-development frameworks, such as Ruby on Rails, you may consider Django views to be the controllers and Django templates to be the views. This is an unfortunate confusion brought about by differing interpretations of MVC. In Djangos interpretation of MVC, the view describes the data that gets presented to the user; its not necessarily just how the data looks, but which data is presented. In contrast, Ruby on Rails and similar frameworks suggest that the controllers job includes deciding which data gets presented to the user, whereas the view is strictly how the data looks, not which data is presented.
如果你熟悉其它的 MVC Web开发框架,比方说 Ruby on Rails,你可能会认为 Django 视图是控制器,而 Django 模板是视图。很不幸,这是对 MVC 不同诠释所引起的错误认识。在 Django 对 MVC 的诠释中,视图用来描述要展现给用户的数据;不是数据看起来 怎么样 ,而是要呈现 哪些 数据。相比之下,Ruby on Rails 及一些同类框架提倡控制器负责决定向用户展现哪些数据,而视图则仅决定 如何 展现数据,而不是展现 哪些 数据。
Neither interpretation is more correct than the other. The important thing is to understand the underlying concepts.
两种诠释中没有哪个更加正确一些。重要的是要理解底层概念。
With all of that philosophy in mind, lets start exploring Djangos database layer. First, we need to take care of some initial configuration: we need to tell Django which database server to use and how to connect to it.
记住这些理念之后,让我们来开始 Django 数据库层的探索。首先,我们需要搞定一些初始化设置:我们必须告诉 Django 要用哪个数据库服务器及如何连接上它。
Well assume youve set up a database server, activated it, and created a database within it (e.g., using a CREATE DATABASE statement). SQLite is a special case; in that case, theres no database to create, because SQLite uses standalone files on the filesystem to store its data.
我们将假定你已经完成了数据库服务器的安装和激活,并且已经在其中创建了数据库(例如,用 CREATE DATABASE 语句)。SQLite 数据库有点特别,用它的话不需要创建数据库,因为 SQLite 使用文件系统中的单个文件来保存数据。
As with TEMPLATE_DIRS in the previous chapter, database configuration lives in the Django settings file, called settings.py by default. Edit that file and look for the database settings:
象前面章节提到的 TEMPLATE_DIRS 一样,数据库配置也是在Django的配置文件里,缺省 是 settings.py 。编辑打开这个文件并查找数据库配置:
DATABASE_ENGINE = '' DATABASE_NAME = '' DATABASE_USER = '' DATABASE_PASSWORD = '' DATABASE_HOST = '' DATABASE_PORT = ''
Heres a rundown of each setting.
配置纲要如下。
DATABASE_ENGINE tells Django which database engine to use. If youre using a database with Django, DATABASE_ENGINE must be set to one of the strings shown in Table 5-1.
DATABASE_ENGINE 告诉Django使用哪个数据库引擎。如果你在 Django 中使用数据库, DATABASE_ENGINE 必须是 Table 5-1 中所列出的值。
Table 5-1. Database Engine Settings Setting Database Required Adapter postgresql PostgreSQL psycopg version 1.x, http://www.djangoproject.com/r/python-pgsql/1/. postgresql_psycopg2 PostgreSQL psycopg version 2.x, http://www.djangoproject.com/r/python-pgsql/. mysql MySQL MySQLdb , http://www.djangoproject.com/r/python-mysql/. sqlite3 SQLite No adapter needed if using Python 2.5+. Otherwise, pysqlite , http://www.djangoproject.com/r/python-sqlite/. ado_mssql Microsoft SQL Server adodbapi version 2.0.1+, http://www.djangoproject.com/r/python-ado/. oracle Oracle cx_Oracle , http://www.djangoproject.com/r/python-oracle/.
表 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/. ado_mssql Microsoft SQL Server adodbapi 版本 2.0.1+, http://www.djangoproject.com/r/python-ado/. oracle Oracle cx_Oracle , http://www.djangoproject.com/r/python-oracle/. Note that for whichever database back-end you use, youll need to download and install the appropriate database adapter. Each one is available for free on the Web; just follow the links in the Required Adapter column in Table 5-1.
要注意的是无论选择使用哪个数据库服务器,都必须下载和安装对应的数据库适配器。访问表 5-1 中“所需适配器”一栏中的链接,可通过互联网免费获取这些适配器。
DATABASE_NAME tells Django the name of your database. If youre using SQLite, specify the full filesystem path to the database file on your filesystem (e.g., '/home/django/mydata.db' ).
DATABASE_NAME 将数据库名称告知 Django 。如果使用 SQLite,请对数据库文件指定完整的文件系统路径。(例如 '/home/django/mydata.db' )。
DATABASE_USER tells Django which username to use when connecting to your database. If youre using SQLite, leave this blank.
DATABASE_USER 告诉 Django 用哪个用户连接数据库。如果用SQLite,空白即可。
DATABASE_PASSWORD tells Django which password to use when connecting to your database. If youre using SQLite or have an empty password, leave this blank.
DATABASE_PASSWORD 告诉Django连接用户的密码。SQLite 用空密码即可。
DATABASE_HOST tells Django which host to use when connecting to your database. If your database is on the same computer as your Django installation (i.e., localhost), leave this blank. If youre using SQLite, leave this blank.
DATABASE_HOST 告诉 Django 连接哪一台主机的数据库服务器。如果数据库与 Django 安装于同一台计算机(即本机),可将此项保留空白。使用 SQLite ,也可保留空白。
MySQL is a special case here. If this value starts with a forward slash ('/' ) and youre using MySQL, MySQL will connect via a Unix socket to the specified socket, for example:
此处的 MySQL 是一个特例。如果使用的是 MySQL 且该项设置值由斜杠( '/' )开头,MySQL 将通过 Unix socket 来连接指定的套接字,例如:
DATABASE_HOST = '/var/run/mysql'
If youre using MySQL and this value doesnt start with a forward slash, then this value is assumed to be the host.
如果用 MySQL 而该项设置的值 不是 以正斜线开始的,系统将假定该项值是主机名。
DATABASE_PORT tells Django which port to use when connecting to your database. If youre using SQLite, leave this blank. Otherwise, if you leave this blank, the underlying database adapter will use whichever port is default for your given database server. In most cases, the default port is fine, so you can leave this blank.
DATABASE_PORT 告诉 Django 连接数据库时使用哪个端口。如果用SQLite,空白即可。其他情况下,如果将该项设置保留空白,底层数据库适配器将会连接所给定数据库服务器的缺省端口。在多数情况下,使用缺省端口就可以了,因此你可以将该项设置保留空白。
Once youve entered those settings, test your configuration. First, from within the mysite project directory you created in Chapter 2, run the command python manage.py shell .
输入完设置后,测试一下配置情况。首先,转到在第二章创建的 mysite 项目目录,运行 python manage.py shell 命令。
Youll notice this starts a Python interactive interpreter. Looks can be deceiving, though! Theres an important difference between running the command python manage.py shell within your Django project directory and the more generic python . The latter is the basic Python shell, but the former tells Django which settings file to use before it starts the shell. This is a key requirement for doing database queries: Django needs to know which settings file to use in order to get your database connection information.
你会看到该命令启动了一个 Python 交互界面。运行命令 python manage.py shell 启动的交互界面和标准的 python 交互界面有很大的区别。看起来都是基本的python外壳(shell),但是前者告诉Django使用哪个配置文件启动。这对数据库操作来说很关键:Django需要知道使用哪个配置文件来获得数据库连接信息。
Behind the scenes, python manage.py shell simply assumes that your settings file is in the same directory as manage.py . There are other ways to tell Django which settings module to use, but these subtleties will be covered later. For now, use python manage.py shell whenever you need to drop into the Python interpreter to do Django-specific tinkering.
python manage.py shell 假定你的配置文件就在和 manage.py 一样的目录中。 以后将会讲到使用其他的方式来告诉Django使用其他的配置文件。
Once youve entered the shell, type these commands to test your database configuration:
输入下面这些命令来测试你的数据库配置:
>>> from django.db import connection >>> cursor = connection.cursor()
If nothing happens, then your database is configured properly. Otherwise, check the error message for clues about whats wrong. Table 5-2 shows some common errors.
如果没有显示什么错误信息,那么你的数据库配置是正确的。否则,你就得 查看错误信息来纠正错误。表 5-2 是一些常见错误。
Error Message | Solution |
---|---|
You havent set the DATABASE_ENGINE setting yet. | Set the DATABASE_ENGINE setting to something other than an empty string. |
Environment variable DJANGO_SETTINGS_MODULE is undefined. | Run the command python manage.py shell rather than python . |
Error loading _____ module: No module named _____. | You havent installed the appropriate database-specific adapter (e.g., psycopg or MySQLdb ). |
_____ isnt an available database backend. | Set your DATABASE_ENGINE setting to one of the valid engine settings described previously. Perhaps you made a typo? |
database _____ does not exist | Change the DATABASE_NAME setting to point to a database that exists, or execute the appropriate CREATE DATABASE statement in order to create it. |
role _____ does not exist | Change the DATABASE_USER setting to point to a user that exists, or create the user in your database. |
could not connect to server | Make sure DATABASE_HOST and DATABASE_PORT are set correctly, and make sure the server is running. |
错误信息 | 解决方案 |
---|---|
You havent set the DATABASE_ENGINE setting yet. | 设置正确的 DATABASE_ENGINE 配置 |
Environment variable DJANGO_SETTINGS_MODULE is undefined. | 运行命令行 python manage.py shell 而不是 python . |
Error loading _____ module: No module named _____. | 你没有安装相关的数据库适配器 (例如, psycopg 或 MySQLdb ). |
_____ isnt 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 设置是正确的,并 确认服务器是在运行的。 |
Now that youve verified the connection is working, its time to create a Django app a bundle of Django code, including models and views, that lives together in a single Python package and represents a full Django application.
你现在已经确认数据库连接正常工作了,让我们来创建一个 Django app ,开始编码模型和视图。这些文件放置在同一个包中并且形成为一个完整的Django应用程序。
Its worth explaining the terminology here, because this tends to trip up beginners. Wed already created a project , in Chapter 2, so whats the difference between a project and an app ? The difference is that of configuration vs. code:
在这里要先解释一些术语,初学者可能会混淆它们。在第二章我们已经创建了 project ,
那么 project 和 app 之间到底有什么不同呢? 它们的区别就是一个是配置另一个是代码:
A project is an instance of a certain set of Django apps, plus the configuration for those apps.
一个project包含很多个Django app以及对它们的配置。
Technically, the only requirement of a project is that it supplies a settings file, which defines the database connection information, the list of installed apps, the TEMPLATE_DIRS , and so forth.
技术上,project的作用是提供配置文件,比方说哪里定义数据库连接信息, 安装的app列表, TEMPLATE_DIRS ,等等。
An app is a portable set of Django functionality, usually including models and views, that lives together in a single Python package.
一个app是一套Django功能的集合,通常包括模型和视图,按Python的包结构的方式存在。
For example, Django comes with a number of apps, such as a commenting system and an automatic admin interface. A key thing to note about these apps is that theyre portable and reusable across multiple projects.
例如,Django本身内建有一些app,如注释系统和自动管理界面。值得关注的是,这些app很容易移植到其他project和被多个project重用。
There are very few hard-and-fast rules about how you fit your Django code into this scheme; its flexible. If youre building a simple Web site, you may use only a single app. If youre building a complex Web site with several unrelated pieces such as an e-commerce system and a message board, youll probably want to split those into separate apps so that youll be able to reuse them individually in the future.
如果你只是建造一个简单的web站点,那么可能你只需要一个app就可以了。如果是复杂的象 电子商务之类的Web站点,你可能需要把这些功能划分成不同的app,以便以后重用。
Indeed, you dont necessarily need to create apps at all, as evidenced by the example view functions weve created so far in this book. In those cases, we simply created a file called views.py , filled it with view functions, and pointed our URLconf at those functions. No apps were needed.
确实,你还可以不用创建app,例如以前写的视图,只是简单的放在 views.py ,不需要app。
However, theres one requirement regarding the app convention: if youre using Djangos database layer (models), you must create a Django app. Models must live within apps. Thus, in order to start writing our models, well need to create a new app.
当然,系统对app有一个约定:如果你使用了Django的数据库层(模型),你 必须创建一个django app。模型必须在这个app中存在。因此,为了开始建造 我们的模型,我们必须创建一个新的app。
Within the mysite project directory you created in Chapter 2, type this command to create a new app named books:
转到 mysite 项目目录,执行下面的命令来创建一个新app叫做books:
python manage.py startapp books
This command does not produce any output, but it does create a books directory within the mysite directory. Lets look at the contents of that directory:
这段命令行代码运行时并不输出任何结果,但是它在 mysite 目录下创建了一个 books 的文件夹。我们现在来看看这个文件夹里的内容
books/ __init__.py models.py views.py
These files will contain the models and views for this app.
这些文件里面就包含了这个app的模型和视图。
Have a look at models.py and views.py in your favorite text editor. Both files are empty, except for an import in models.py . This is the blank slate for your Django app.
看一下 models.py 和 views.py 文件。它们都是空的,除了 models.py 里有一个 import。
As we discussed earlier in this chapter, the M in MTV stands for Model. A Django model is a description of the data in your database, represented as Python code. Its your data layout the equivalent of your SQL CREATE TABLE statements except its in Python instead of SQL, and it includes more than just database column definitions. Django uses a model to execute SQL code behind the scenes and return convenient Python data structures representing the rows in your database tables. Django also uses models to represent higher-level concepts that SQL cant necessarily handle.
我们早些时候谈到。MTV里的M代表模型。Django模型是用Python代码形式表述的数据在数据库中的定义。对数据层来说它等同于 CREATE TABLE 语句,只不过执行的是Python代码而不是SQL,而且还包含了比数据库字段定义更多的含义。Django用模型在后台执行SQL代码并把结果用Python的数据结构来描述,这样你可以很方便的使用这些数据。Django还用模型来描述SQL不能处理的高级概念。
If youre familiar with databases, your immediate thought might be, Isnt it redundant to define data models in Python and in SQL? Django works the way it does for several reasons:
如果你对数据库很熟悉,你可能马上就会想到,用Python 和 SQL来定义数据模型是不是有点多余? Django这样做是有下面几个原因的:
Introspection requires overhead and is imperfect. In order to provide convenient data-access APIs, Django needs to know the database layout somehow , and there are two ways of accomplishing this. The first way would be to explicitly describe the data in Python, and the second way would be to introspect the database at runtime to determine the data models.
自省(运行时自动识别数据库)会导致过载和有数据完整性问题。为了提供方便的数据访问API,Django需要以 某种方式 知道数据库层内部信息,有两种实现方式。第一种方式是用Python明确的定义数据模型,第二种方式是通过运行时扫描数据库来自动侦测识别数据模型。
This second way seems cleaner, because the metadata about your tables lives in only one place, but it introduces a few problems. First, introspecting a database at runtime obviously requires overhead. If the framework had to introspect the database each time it processed a request, or even when the Web server was initialized, this would incur an unacceptable level of overhead. (While some believe that level of overhead is acceptable, Djangos developers aim to trim as much framework overhead as possible, and this approach has succeeded in making Django faster than its high-level framework competitors in benchmarks.) Second, some databases, notably older versions of MySQL, do not store sufficient metadata for accurate and complete introspection.
第二种方式看起来更清晰,因为数据表信息只存放在一个地方-数据库里,但是会带来一些问题。首先,运行时扫描数据库会带来严重的系统过载。如果每个请求都要扫描数据库的表结构,或者即便是服务启动时做一次都是会带来不能接受的系统过载。(Django尽力避免过载,而且成功做到了这一点)其次,有些数据库,例如老版本的MySQL,没有提供足够的元数据来完整地重构数据表。
Writing Python is fun, and keeping everything in Python limits the number of times your brain has to do a context switch. It helps productivity if you keep yourself in a single programming environment/mentality for as long as possible. Having to write SQL, then Python, and then SQL again is disruptive.
编写Python代码是非常有趣的,保持用Python的方式思考会避免你的大脑在不同领域来回切换。这可以帮助你提高生产率。不得不去重复写SQL,再写Python代码,再写SQL,…,会让你头都要裂了。
Having data models stored as code rather than in your database makes it easier to keep your models under version control. This way, you can easily keep track of changes to your data layouts.
把数据模型用代码的方式表述来让你可以容易对它们进行版本控制。这样,你可以很容易了解数据层的变动情况。
SQL allows for only a certain level of metadata about a data layout. Most database systems, for example, do not provide a specialized data type for representing email addresses or URLs. Django models do. The advantage of higher-level data types is higher productivity and more reusable code.
SQL只能描述特定类型的数据字段。例如,大多数数据库都没有数据字段类型描述Email地址、URL。而用Django的模型可以做到这一点。好处就是高级的数据类型带来高生产力和更好的代码重用。
SQL is inconsistent across database platforms. If youre distributing a Web application, for example, its much more pragmatic to distribute a Python module that describes your data layout than separate sets of CREATE TABLE statements for MySQL, PostgreSQL, and SQLite.
SQL还有在不同数据库平台的兼容性问题。你必须为不同的数据库编写不同的SQL脚本, 而Python的模块就不会有这个问题。
A drawback of this approach, however, is that its possible for the Python code to get out of sync with whats actually in the database. If you make changes to a Django model, youll need to make the same changes inside your database to keep your database consistent with the model. Well detail some strategies for handling this problem later in this chapter.
当然,这个方法也有一个缺点,就是Python代码和数据库表的同步问题。如果你修改了一个Django模型, 你要自己做工作来保证数据库和模型同步。我们将在稍后讲解解决这个问题的几种策略。
Finally, we should note that Django includes a utility that can generate models by introspecting an existing database. This is useful for quickly getting up and running with legacy data.
最后,我们要提醒你Django提供了实用工具来从现有的数据库表中自动扫描生成模型。 这对已有的数据库来说是非常快捷有用的。
As an ongoing example in this chapter and the next chapter, well focus on a basic book/author/publisher data layout. We use this as our example because the conceptual relationships between books, authors, and publishers are well known, and this is a common data layout used in introductory SQL textbooks. Youre also reading a book that was written by authors and produced by a publisher!
在本章和后续章节里,我们将集中到一个基本的 书籍/作者/出版商 数据层上。我们这样做是因为 这是一个众所周知的例子,很多SQL有关的书籍也常用这个举例。你现在看的这本书也是由作者 创作再由出版商出版的哦!
Well suppose the following concepts, fields, and relationships:
我们来假定下面的这些概念、字段和关系:
An author has a salutation (e.g., Mr. or Mrs.), a first name, a last name, an email address, and a headshot photo.
A publisher has a name, a street address, a city, a state/province, a country, and a Web site.
出版商有名称,地址,所在城市、省,国家,网站。
A book has a title and a publication date. It also has one or more authors (a many-to-many relationship with authors) and a single publisher (a one-to-many relationship aka foreign key to publishers).
书籍有书名和出版日期。它有一个或多个作者(和作者是多对多的关联关系[many-to-many]), 只有一个出版商(和出版商是一对多的关联关系[one-to-many],也被称作外键[foreign key])
The first step in using this database layout with Django is to express it as Python code. In the models.py file that was created by the startapp command, enter the following:
在Django中使用该数据库布局的第一步是将其表述为Python代码。在通过“startapp”命令创建的“models.py”文件中,输入如下代码:
from django.db import models class Publisher(models.Model): name = models.CharField(maxlength=30) address = models.CharField(maxlength=50) city = models.CharField(maxlength=60) state_province = models.CharField(maxlength=30) country = models.CharField(maxlength=50) website = models.URLField() class Author(models.Model): salutation = models.CharField(maxlength=10) first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=40) email = models.EmailField() headshot = models.ImageField(upload_to='/tmp') class Book(models.Model): title = models.CharField(maxlength=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
Lets quickly examine this code to cover the basics. The first thing to notice is that each model is represented by a Python class that is a subclass of django.db.models.Model . The parent class, Model , contains all the machinery necessary to make these objects capable of interacting with a database and that leaves our models responsible solely for defining their fields, in a nice and compact syntax. Believe it or not, this is all the code we need to write to have basic data access with Django.
让我们来快速讲解一下这些代码的含义。首先要注意的事是每个数据模型都是 django.db.models.Model 的子类。它的父类 Model 包含了所有和数据库 打交道的方法,并提供了一个简洁漂亮的定义语法。不管你相信还是不相信, 这就是我们用Django写的数据基本存取功能的全部代码。
Each model generally corresponds to a single database table, and each attribute on a model generally corresponds to a column in that database table. The attribute name corresponds to the columns name, and the type of field (e.g., CharField ) corresponds to the database column type (e.g., varchar ). For example, the Publisher model is equivalent to the following table (assuming PostgreSQL CREATE TABLE syntax):
每个模型通常对应于单个数据库表,每个属性模型 通常对应于数据库表的列。属性名称对应 列名称和类型的字段(如。”、“CharField”)对应于数据库列 类型(如。”、“varchar””)。例如,“出版商“模型相当于以下 表(假设PostgreSQL‘创建表的语法):
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 );
Indeed, Django can generate that CREATE TABLE statement automatically, as well show in a moment.
事实上,正如过一会儿我们所要展示的,Django 可以自动生成这些 CREATE TABLE 语句。
The exception to the one-class-per-database-table rule is the case of many-to-many relationships. In our example models, Book has a ManyToManyField called authors . This designates that a book has one or many authors, but the Book database table doesnt get an authors column. Rather, Django creates an additional table a many-to-many join table that handles the mapping of books to authors.
“每个数据库表对应一个类”这条规则的例外情况是多对多关系。在我们的范例模型中, Book 有一个 多对多字段 叫做 authors 。 该字段表明一本书籍有一个或多个作者,但 Book 数据库表却并没有 authors 字段。相反,Django创建了一个额外的表(多对多连接表)来处理书籍和作者之间的映射关系。
For a full list of field types and model syntax options, see Appendix B.
请查看附录 B 了解所有的字段类型和模型语法选项。
Finally, note we havent explicitly defined a primary key in any of these models. Unless you instruct it otherwise, Django automatically gives every model an integer primary key field called id . Each Django model is required to have a single-column primary key.
最后需要注意的是:我们并没有显式地为这些模型定义任何主键。除非你指定,否则 Django 会自动为每个模型创建一个叫做 id 的主键。每个 Django 模型必须要有一个单列主键。
Weve written the code; now lets create the tables in our database. In order to do that, the first step is to activate these models in our Django project. We do that by adding the books app to the list of installed apps in the settings file.
完成这些代码之后,现在让我们来在数据库中创建这些表。要完成该项工作,第一步是在 Django 项目中 激活 这些模型。将 books app 添加到配置文件的已 installed apps 列表中即可完成此步骤。
Edit the settings.py file again, and look for the INSTALLED_APPS setting. INSTALLED_APPS tells Django which apps are activated for a given project. By default, it looks something like this:
再次编辑 settings.py 文件, 找到 INSTALLED_APPS 设置。 INSTALLED_APPS 告诉 Django 项目哪些 app 处于激活状态。缺省情况下如下所示:
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', )
Temporarily comment out all four of those strings by putting a hash character (# ) in front of them. (Theyre included by default as a common-case convenience, but well activate and discuss them later.) While youre at it, modify the default MIDDLEWARE_CLASSES and TEMPLATE_CONTEXT_PROCESSORS settings. These depend on some of the apps we just commented out. Then, add 'mysite.books' to the INSTALLED_APPS list, so the setting ends up looking like this:
把这四个设置前面加#临时注释起来。(它们是一些缺省的公用设置,现在先不管 它们,以后再来讨论)同样的,修改缺省的 MIDDLEWARE_CLASSES 和 TEMPLATE_CONTEXT_PROCESSORS 设置,都注释起来。 然后添加 'mysite.books' 到 INSTALLED_APPS 列表,现在看起来是这样:
MIDDLEWARE_CLASSES = ( # 'django.middleware.common.CommonMiddleware', # 'django.contrib.sessions.middleware.SessionMiddleware', # 'django.contrib.auth.middleware.AuthenticationMiddleware', # 'django.middleware.doc.XViewMiddleware', ) TEMPLATE_CONTEXT_PROCESSORS = () #... INSTALLED_APPS = ( #'django.contrib.auth', #'django.contrib.contenttypes', #'django.contrib.sessions', #'django.contrib.sites', 'mysite.books', )
(As were dealing with a single-element tuple here, dont forget the trailing comma. By the way, this books authors prefer to put a comma after every element of a tuple, regardless of whether the tuple has only a single element. This avoids the issue of forgetting commas, and theres no penalty for using that extra comma.)
(尽管这是单个tuple元素,我们也不要忘了结尾的逗号[,]。 另外,本书的作者喜欢在 每一个 tuple元素后面加一个逗号,不管它是不是只有一个元素。这是为了避免忘了加逗号)
'mysite.books' refers to the books app were working on. Each app in INSTALLED_APPS is represented by its full Python path that is, the path of packages, separated by dots, leading to the app package.
'mysite.books' 标识 books app。 INSTALLED_APPS 中的每个app都用 Python的路径描述,包的路径,用小数点(.)区分。
Now that the Django app has been activated in the settings file, we can create the database tables in our database. First, lets validate the models by running this command:
现在我们可以创建数据库表了。首先,用下面的命令对校验模型的有效性:
python manage.py validate
The validate command checks whether your models syntax and logic are correct. If all is well, youll see the message 0 errors found . If you dont, make sure you typed in the model code correctly. The error output should give you helpful information about what was wrong with the code.
validate 命令检查你的模型的语法和逻辑是否正确。如果一切正常,你会看到 0 errors found 消息。如果有问题,它会给出非常有用的错误信息来帮助你 修正你的模型。
Any time you think you have problems with your models, run python manage.py validate . It tends to catch all the common model problems.
一旦你觉得你的模型可能有问题,运行 python manage.py validate 。 它可以帮助你捕获一些常见的模型定义错误。
If your models are valid, run the following command for Django to generate CREATE TABLE statements for your models in the books app (with colorful syntax highlighting available if youre using Unix):
模型确认没问题了,运行下面的命令来生成 CREATE TABLE 语句:
python manage.py sqlall books
In this command, books is the name of the app. Its what you specified when you ran the command manage.py startapp . When you run the command, you should see something like this:
在这个命令行中, 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_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 ); CREATE TABLE "books_author" ( "id" serial NOT NULL PRIMARY KEY, "salutation" varchar(10) NOT NULL, "first_name" varchar(30) NOT NULL, "last_name" varchar(40) NOT NULL, "email" varchar(75) NOT NULL, "headshot" varchar(100) NOT NULL ); CREATE TABLE "books_book_authors" ( "id" serial NOT NULL PRIMARY KEY, "book_id" integer NOT NULL REFERENCES "books_book" ("id"), "author_id" integer NOT NULL REFERENCES "books_author" ("id"), UNIQUE ("book_id", "author_id") ); CREATE INDEX books_book_publisher_id ON "books_book" ("publisher_id"); COMMIT;
Note the following:
注意:
Table names are automatically generated by combining the name of the app (books ) and the lowercase name of the model (publisher , book , and author ). You can override this behavior, as detailed in Appendix B.
自动生成的表名是app名称( books )和模型的小写名称 ( publisher , book , author )的组合。 你可以指定不同的表名,详情请看附录 B。
As we mentioned earlier, Django adds a primary key for each table automatically the id fields. You can override this, too.
我们前面已经提到,Django为自动加了一个 id 主键,你一样可以修改它。
By convention, Django appends "_id" to the foreign key field name. As you might have guessed, you can override this behavior, too.
按约定,Django添加 "_id" 后缀到外键字段名。这个同样也是可自定义的。
The foreign key relationship is made explicit by a REFERENCES statement.
外键是用 REFERENCES 语句明确定义的。
These CREATE TABLE statements are tailored to the database youre using, so database-specific field types such as auto_increment (MySQL), serial (PostgreSQL), or integer primary key (SQLite) are handled for you automatically. The same goes for quoting of column names (e.g., using double quotes or single quotes). This example output is in PostgreSQL syntax.
这些 CREATE TABLE 语句会根据你的数据库而作调整,这样象数据库特定的一些字段例如: auto_increment (MySQL), serial (PostgreSQL), integer primary key (SQLite) 可以自动处理。 同样的,字段名称的引号也是自动处理(例如单引号还是双引号)。 这个给出的例子是Postgresql的语法。
The sqlall command doesnt actually create the tables or otherwise touch your database it just prints output to the screen so you can see what SQL Django would execute if you asked it. If you wanted to, you could copy and paste this SQL into your database client, or use Unix pipes to pass it directly. However, Django provides an easier way of committing the SQL to the database. Run the syncdb command, like so:
sqlall 命令并没有在数据库中真正创建数据表,只是把SQL语句段打印出来。 你可以把这些语句段拷贝到你的SQL客户端去执行它。当然,Django提供了更简单的 方法来执行这些SQL语句。运行 syncdb 命令:
python manage.py syncdb
Youll see something like this:
你将会看到这样的内容:
Creating table books_publisher Creating table books_book Creating table books_author Installing index for books.Book model
The syncdb command is a simple sync of your models to your database. It looks at all of the models in each app in your INSTALLED_APPS setting, checks the database to see whether the appropriate tables exist yet, and creates the tables if they dont yet exist. Note that syncdb does not sync changes in models or deletions of models; if you make a change to a model or delete a model, and you want to update the database, syncdb will not handle that. (More on this later.)
syncdb 命令是同步你的模型到数据库的一个简单方法。它会根据 INSTALLED_APPS 里设置的app来检查数据库, 如果表不存在,它就会创建它。 需要注意的是, syncdb 并 不能 同步模型的修改到数据库。如果你修改了模型,然后你想更新 数据库, syncdb 是帮不了你的。(稍后我们再讲这些。)
If you run python manage.py syncdb again, nothing happens, because you havent added any models to the books app or added any apps to INSTALLED_APPS . Ergo, its always safe to run python manage.py syncdb it wont clobber things.
如果你再次运行 python manage.py syncdb ,什么也没发生,因为你没有添加新的模型或者 添加新的app。所以,运行 python manage.py syncdb 总是安全的,它不会把事情搞砸。
If youre interested, take a moment to dive into your database servers command-line client and see the database tables Django created. You can manually run the command-line client (e.g., psql for PostgreSQL) or you can run the command python manage.py dbshell , which will figure out which command-line client to run, depending on your DATABASE_SERVER setting. The latter is almost always more convenient.
如果你有兴趣,花点时间用你的SQL客户端登录进数据库服务器看看刚才Django创建的数据表。 Django带有一个命令行工具, python manage.py dbshell 。
Once youve created a model, Django automatically provides a high-level Python API for working with those models. Try it out by running python manage.py shell and typing the following:
一旦你创建了模型,Django自动为这些模型提供了高级的Pyhton API。 运行 python manage.py shell 并输入下面的内容试试看:
>>> from books.models import Publisher >>> p1 = Publisher(name='Addison-Wesley', address='75 Arlington Street', ... city='Boston', state_province='MA', 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>]
These few lines of code accomplish quite a bit. Here are the highlights:
这短短几行代码干了不少的事。这里简单的说一下:
To create an object, just import the appropriate model class and instantiate it by passing in values for each field.
要创建对象,只需 import 相应模型类,并传入每个字段值将其实例化。
To save the object to the database, call the save() method on the object. Behind the scenes, Django executes an SQL INSERT statement here.
调用该对象的 save() 方法,将对象保存到数据库中。Django 会在后台执行一条 INSERT 语句。
To retrieve objects from the database, use the attribute Publisher.objects . Fetch a list of all Publisher objects in the database with the statement Publisher.objects.all() . Behind the scenes, Django executes an SQL SELECT statement here.
使用属性 Publisher.objects 从数据库中获取对象。调用 Publisher.objects.all() 获取数据库中所有的 Publisher 对象。此时,Django 在后台执行一条 SELECT SQL语句。
Naturally, you can do quite a lot with the Django database API but first, lets take care of a small annoyance.
自然,你肯定想执行更多的Django数据库API试试看,不过,还是让我们先解决一点烦人的小问题。
When we printed out the list of publishers, all we got was this unhelpful display that makes it difficult to tell the Publisher objects apart:
当我们打印整个publisher列表时,我们没有得到想要的有用的信息:
[<Publisher: Publisher object>, <Publisher: Publisher object>]
We can fix this easily by adding a method called __str__() to our Publisher object. A __str__() method tells Python how to display the string representation of an object. You can see this in action by adding a __str__() method to the three models:
我们可以简单解决这个问题,只需要添加一个方法 __str__() 到 Publisher 对象。 __str__() 方法告诉Python要怎样把对象当作字符串来使用。 请看下面:
from django.db import models class Publisher(models.Model): name = models.CharField(maxlength=30) address = models.CharField(maxlength=50) city = models.CharField(maxlength=60) state_province = models.CharField(maxlength=30) country = models.CharField(maxlength=50) website = models.URLField() **def __str__(self):** **return self.name** class Author(models.Model): salutation = models.CharField(maxlength=10) first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=40) email = models.EmailField() headshot = models.ImageField(upload_to='/tmp') **def __str__(self):** **return '%s %s' % (self.first_name, self.last_name)** class Book(models.Model): title = models.CharField(maxlength=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() **def __str__(self):** **return self.title**
As you can see, a __str__() method can do whatever it needs to do in order to return a string representation. Here, the __str__() methods for Publisher and Book simply return the objects name and title, respectively, but the __str__() for Author is slightly more complex it pieces together the first_name and last_name fields. The only requirement for __str__() is that it return a string. If __str__() doesnt return a string if it returns, say, an integer then Python will raise a TypeError with a message like "__str__ returned non-string" .
就象你看到的一样, __str__() 方法返回一个字符串。 __str__() 必须返回字符串, 如果是其他类型,Python将会抛出 TypeError 错误消息 "__str__ returned non-string" 出来。
For the changes to take effect, exit out of the Python shell and enter it again with python manage.py shell . (This is the simplest way to make code changes take effect.) Now the list of Publisher objects is much easier to understand:
为了让我们的修改生效,先退出Python Shell,然后再次运行 python manage.py shell 进入。 现在列出 Publisher 对象就很容易理解了:
>>> from books.models import Publisher >>> publisher_list = Publisher.objects.all() >>> publisher_list [<Publisher: Addison-Wesley>, <Publisher: O'Reilly>]
Make sure any model you define has a __str__() method not only for your own convenience when using the interactive interpreter, but also because Django uses the output of __str__() in several places when it needs to display objects.
请确保你的每一个模型里都包含 __str__() 方法,这不只是为了交互时方便,也是因为 Django会在其他一些地方用 __str__() 来显示对象。
Finally, note that __str__() is a good example of adding behavior to models. A Django model describes more than the database table layout for an object; it also describes any functionality that object knows how to do. __str__() is one example of such functionality a model knows how to display itself.
最后, __str()__ 也是一个很好的例子来演示我们怎么添加 行为 到模型里。 Django的模型不只是为对象定义了数据库表的结构,还定义了对象的行为。 __str__() 就是一个例子来演示模型知道怎么显示它们自己。
Youve already seen this done: to insert a row into your database, first create an instance of your model using keyword arguments, like so:
你已经知道怎么做了:先使用一些关键参数创建对象实例,如下:
>>> p = Publisher(name='Apress', ... address='2855 Telegraph Ave.', ... city='Berkeley', ... state_province='CA', ... country='U.S.A.', ... website='http://www.apress.com/')
This act of instantiating a model class does not touch the database.
这个对象实例并 没有 对数据库做修改。
To save the record into the database (i.e., to perform the SQL INSERT statement), call the objects save() method:
要保存这个记录到数据库里(也就是执行 INSERT SQL 语句),调用对象的 save() 方法:
>>> p.save()
In SQL, this can roughly be translated into the following:
在SQL里,这大致可以转换成这样:
INSERT INTO book_publisher (name, address, city, state_province, country, website) VALUES ('Apress', '2855 Telegraph Ave.', 'Berkeley', 'CA', 'U.S.A.', 'http://www.apress.com/');
Because the Publisher model uses an autoincrementing primary key id , the initial call to save() does one more thing: it calculates the primary key value for the record and sets it to the id attribute on the instance:
因为 Publisher 模型有一个自动增加的主键 id ,所以第一次调用 save() 还多做了一件事: 计算这个主键的值并把它赋值给这个对象实例:
>>> p.id 52 # this will differ based on your own data
Subsequent calls to save() will save the record in place, without creating a new record (i.e., performing an SQL UPDATE statement instead of an INSERT ):
接下来再调用 save() 将不会创建新的记录,而只是修改记录内容(也就是 执行 UPDATE SQL语句,而不是 INSERT 语句):
>>> p.name = 'Apress Publishing' >>> p.save()
The preceding save() statement will result in roughly the following SQL:
前面执行的 save() 相当于下面的SQL语句:
UPDATE book_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;
Creating and updating data sure is fun, but it is also useless without a way to sift through that data. Weve already seen a way to look up all the data for a certain model:
我们已经知道查找所有数据的方法了:
>>> Publisher.objects.all() [<Publisher: Addison-Wesley>, <Publisher: O'Reilly>, <Publisher: Apress Publishing>]
This roughly translates to this SQL:
这相当于这个SQL语句:
SELECT id, name, address, city, state_province, country, website FROM book_publisher;
Note
注意
Notice that Django doesnt use SELECT * when looking up data and instead lists all fields explicitly. This is by design: in certain circumstances SELECT * can be slower, and (more important) listing fields more closely follows one tenet of the Zen of Python: Explicit is better than implicit.
注意到Django在选择所有数据时并没有使用 SELECT* ,而是显式列出了所有字段。 就是这样设计的: SELECT* 会更慢,而且最重要的是列出所有字段遵循了Python 界的一个信条:明确比不明确好。
For more on the Zen of Python, try typing import this at a Python prompt.
有关Python之禅(戒律) :-),在Python提示行输入 import this 试试看。
Lets take a close look at each part of this Publisher.objects.all() line:
让我们来仔细看看 Publisher.objects.all() 这行的每个部分:
First, we have the model we defined, Publisher . No surprise here: when you want to look up data, you use the model for that data.
首先,我们有一个已定义的模型 Publisher 。没什么好奇怪的:你想要查找数据, 你就用模型来获得数据。
Next, we have this objects business. Technically, this is a manager . Managers are discussed in detail in Appendix B. For now, all you need to know is that managers take care of all table-level operations on data including, most important, data lookup.
其次, objects 是干什么的?技术上,它是一个 管理器(manager) 。 管理器 将在附录B详细描述,在这里你只要知道它处理有关数据表的操作,特别是数据查找。
All models automatically get a objects manager; youll use it any time you want to look up model instances.
所有的模型都自动拥有一个 objects 管理器;你可以在想要查找数据时是使用它。
Finally, we have all() . This is a method on the objects manager that returns all the rows in the database. Though this object looks like a list, its actually a QuerySet an object that represents some set of rows from the database. Appendix C deals with QuerySets in detail. For the rest of this chapter, well just treat them like the lists they emulate.
最后,还有 all() 方法。这是 objects 管理器返回所有记录的一个方法。 尽管这个对象 看起来 象一个列表(list),它实际是一个 QuerySet 对象, 这个对象是数据库中一些记录的集合。附录C将详细描述QuerySet,现在,我们 就先当它是一个仿真列表对象好了。
Any database lookup is going to follow this general pattern well call methods on the manager attached to the model we want to query against.
所有的数据库查找都遵循一个通用模式:调用模型的管理器来查找数据。
While fetching all objects certainly has its uses, most of the time were going to want to deal with a subset of the data. Well do this with the filter() method:
如果想要获得数据的一个子集,我们可以使用 filter() 方法:
>>> Publisher.objects.filter(name="Apress Publishing") [<Publisher: Apress Publishing>]
filter() takes keyword arguments that get translated into the appropriate SQL WHERE clauses. The preceding example would get translated into something like this:
filter() 根据关键字参数来转换成 WHERE SQL语句。前面这个例子 相当于这样:
SELECT id, name, address, city, state_province, country, website FROM book_publisher WHERE name = 'Apress Publishing';
You can pass multiple arguments into filter() to narrow down things further:
你可以传递多个参数到 filter() 来缩小选取范围:
>>> Publisher.objects.filter(country="U.S.A.", state_province="CA") [<Publisher: Apress Publishing>]
Those multiple arguments get translated into SQL AND clauses. Thus, the example in the code snippet translates into the following:
多个参数会被转换成 AND SQL语句,例如象下面这样:
SELECT id, name, address, city, state_province, country, website FROM book_publisher WHERE country = 'U.S.A.' AND state_province = 'CA';
Notice that by default the lookups use the SQL = operator to do exact match lookups. Other lookup types are available:
注意,SQL缺省的 = 操作符是精确匹配的,其他的查找类型如下:
>>> Publisher.objects.filter(name__contains="press") [<Publisher: Apress Publishing>]
Thats a double underscore there between name and contains . Like Python itself, Django uses the double underscore to signal that something magic is happening here, the __contains part gets translated by Django into a SQL LIKE statement:
在 name 和 contains 之间有双下划线。象Python自己一样,Django也使用 双下划线来做一些小魔法,这个 __contains 部分会被Django转换成 LIKE SQL语句:
SELECT id, name, address, city, state_province, country, website FROM book_publisher WHERE name LIKE '%press%';
Many other types of lookups are available, including icontains (case-insensitive LIKE ), startswith and endswith , and range (SQL BETWEEN queries). Appendix C describes all of these lookup types in detail.
其他的一些查找类型有: icontains (大小写无关的 LIKE ), startswith 和 endswith , 还有 range (SQL BETWEEN 查询)。 附录C详细列出了这些类型的详细资料。
Sometimes you want to fetch only a single object. Thats what the get() method is for:
有时你只想获取单个对象,这个时候使用 get() 方法:
>>> Publisher.objects.get(name="Apress Publishing") <Publisher: Apress Publishing>
Instead of a list (rather, QuerySet), only a single object is returned. Because of that, a query resulting in multiple objects will cause an exception:
这样,就返回了单个对象,而不是列表(更准确的说,QuerySet)。 所以,如果结果是多个对象,会导致抛出异常:
>>> Publisher.objects.get(country="U.S.A.") Traceback (most recent call last): ... AssertionError: get() returned more than one Publisher -- it returned 2!
A query that returns no objects also causes an exception:
如果查询没有返回结果也会抛出异常:
>>> Publisher.objects.get(name="Penguin") Traceback (most recent call last): ... DoesNotExist: Publisher matching query does not exist.
As you play around with the previous examples, you might discover that the objects are being returned in a seemingly random order. You arent imagining things; so far we havent told the database how to order its results, so were simply getting back data in some arbitrary order chosen by the database.
在运行前面的例子中,你可能已经注意到返回的结果是无序的。我们还没有告诉数据库 怎样对结果进行排序,所以我们返回的结果是无序的。
Thats obviously a bit silly; we wouldnt want a Web page listing publishers to be ordered randomly. So, in practice, well probably want to use order_by() to reorder our data into a useful list:
当然,我们不希望在页面上列出的出版商的列表是杂乱无章的。我们用 order_by() 来 排列返回的数据:
>>> Publisher.objects.order_by("name") [<Publisher: Apress Publishing>, <Publisher: Addison-Wesley>, <Publisher: O'Reilly>]
This doesnt look much different from the earlier all() example, but the SQL now includes a specific ordering:
跟以前的 all() 例子差不多,SQL语句里多了指定排序的部分:
SELECT id, name, address, city, state_province, country, website FROM book_publisher ORDER BY name;
We can order by any field we like:
我们可以对任意字段进行排序:
>>> Publisher.objects.order_by("address") [<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Addison-Wesley>] >>> Publisher.objects.order_by("state_province") [<Publisher: Apress Publishing>, <Publisher: Addison-Wesley>, <Publisher: O'Reilly>]
and by multiple fields:
多个字段也没问题:
>>> Publisher.objects.order_by("state_provice", "address") [<Publisher: Apress Publishing>, <Publisher: O'Reilly>, <Publisher: Addison-Wesley>]
We can also specify reverse ordering by prefixing the field name with a - (thats a minus character):
我们还可以指定逆向排序,在前面加一个减号 - 前缀:
>>> Publisher.objects.order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Addison-Wesley>]
While this flexibility is useful, using order_by() all the time can be quite repetitive. Most of the time youll have a particular field you usually want to order by. In these cases, Django lets you attach a default ordering to the model:
每次都要用 order_by() 显得有点啰嗦。 大多数时间你通常只会对某些 字段进行排序。在这种情况下,Django让你可以指定模型的缺省排序方式:
class Publisher(models.Model): name = models.CharField(maxlength=30) address = models.CharField(maxlength=50) city = models.CharField(maxlength=60) state_province = models.CharField(maxlength=30) country = models.CharField(maxlength=50) website = models.URLField() def __str__(self): return self.name **class Meta:** **ordering = ["name"]**
This ordering = ["name"] bit tells Django that unless an ordering is given explicitly with order_by() , all publishers should be ordered by name.
这个 ordering = ["name"] 告诉Django如果没有显示提供 order_by() , 就缺省按名称排序。
Whats This Meta Thing?
Meta是什么?
Django uses this internal class Meta as a place to specify additional metadata about a model. Its completely optional, but it can do some very useful things. See Appendix B for the options you can put under Meta .
Django使用内部类Meta存放用于附加描述该模型的元数据。 这个类完全可以不实现,不过他能做很多非常有用的事情。查看附录B,在Meta项下面,获得更多选项信息,
Youve seen how you can filter data, and youve seen how you can order it. At times, of course, youre going to want to do both. In these cases, you simply chain the lookups together:
你已经知道怎么过滤数据了,现在让我们来排序它们。你可以同时做这 过滤和排序,很简单,就象这样:
>>> Publisher.objects.filter(country="U.S.A.").order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Addison-Wesley>]
As you might expect, this translates to a SQL query with both a WHERE and an ORDER BY :
你应该没猜错,转换成SQL查询就是 WHERE 和 ORDER BY 的组合:
SELECT id, name, address, city, state_province, country, website FROM book_publisher WHERE country = 'U.S.A' ORDER BY name DESC;
You can keep chaining queries as long as you like. Theres no limit.
你可以任意把它们串起来,多长都可以,这里没有限制。
Another common need is to look up only a fixed number of rows. Imagine you have thousands of publishers in your database, but you want to display only the first one. You can do this using Pythons standard list slicing syntax:
另一个常用的需求就是取出固定数目的记录。想象一下你有成千上万的出版商在你的数据库里, 但是你只想显示第一个。你可以这样做:
>>> Publisher.objects.all()[0] <Publisher: Addison-Wesley>
This translates roughly to:
这相当于:
SELECT id, name, address, city, state_province, country, website FROM book_publisher ORDER BY name LIMIT 1;
And More
还有更多
Weve only just scratched the surface of dealing with models, but you should now know enough to understand all the examples in the rest of the book. When youre ready to learn the complete details behind object lookups, turn to Appendix C.
我们只是刚接触到模型的皮毛,你还必须了解更多的内容以便理解以后的范例。 具体请看附录C。
To delete objects, simply call the delete() method on your object:
要删除对象,只需简单的调用对象的 delete() 方法:
>>> p = Publisher.objects.get(name="Addison-Wesley") >>> p.delete() >>> Publisher.objects.all() [<Publisher: Apress Publishing>, <Publisher: O'Reilly>]
You can also delete objects in bulk by calling delete() on the result of some lookup:
你还可以批量删除对象,通过对查询的结果调用 delete() 方法:
>>> publishers = Publisher.objects.all() >>> publishers.delete() >>> Publisher.objects.all() []
Note
注意
Deletions are permanent , so be careful! In fact, its usually a good idea to avoid deleting objects unless you absolutely have to relational databases dont do undo so well, and restoring from backups is painful.
删除是 不可恢复 的,所以要小心操作!事实上,应该尽量避免删除对象,除非你 确实需要删除它。数据库的数据恢复的功能通常不太好,而从备份数据恢复是很痛苦的。
Its often a good idea to add active flags to your data models. You can look up only active objects, and simply set the active field to False instead of deleting the object. Then, if you realize youve made a mistake, you can simply flip the flag back.
通常更好的方法是给你的数据模型添加激活标志。你可以只在激活的对象中查找, 对于不需要的对象,将激活字段值设为 False , 而不是删除对象。这样, 如果一旦你认为做错了的话,只需把标志重设回来就可以了。
When we introduced the syncdb command earlier in this chapter, we noted that syncdb merely creates tables that dont yet exist in your database it does not sync changes in models or perform deletions of models. If you add or change a models field, or if you delete a model, youll need to make the change in your database manually. This section explains how to do that.
当我们在这一章的前面介绍 syncdb 命令的时候,我们强调 syncdb 仅仅创建数据库中不存在的表,而不会同步模型的修改或者删除到数据库。如果你添加或者修改了模型的一个字段,或者删除一个模型,你必须手动改变你的数据库。下面我们看看怎么来做。
When dealing with schema changes, its important to keep a few things in mind about how Djangos database layer works:
当我们处理表结构的修改时,要时刻想着 Django 的数据库层是如何工作的:
Django will complain loudly if a model contains a field that has not yet been created in the database table. This will cause an error the first time you use the Django database API to query the given table (i.e., it will happen at code execution time, not at compilation time).
如果模型中包含一个在数据库中并不存在的字段,Django会大声抱怨的。这样当你第一次调用Django的数据库API来查询给定的表时就会出错(也就是说,它会在执行的时候出错,而不是编译的时候)
Django does not care if a database table contains columns that are not defined in the model.
Django并不关心数据库表中是否存在没有在模型中定义的列
Django does not care if a database contains a table that is not represented by a model.
Django并不关心数据库中是否包含没有被模型描述的表
Making schema changes is a matter of changing the various pieces the Python code and the database itself in the right order.
修改表结构也就是按照正确的顺序修改各种Python代码和数据库本身
When adding a field to a table/model in a production setting, the trick is to take advantage of the fact that Django doesnt care if a table contains columns that arent defined in the model. The strategy is to add the column in the database, and then update the Django model to include the new field.
当按照产品需求向一个表/模型添加字段时,Django不关心一个表的列是否在模型中定义,我们可以利用这个小技巧,先在数据库中添加列,然后再改变模型中对应的字段。
However, theres a bit of a chicken-and-egg problem here, because in order to know how the new database column should be expressed in SQL, you need to look at the output of Djangos manage.py sqlall command, which requires that the field exist in the model. (Note that youre not required to create your column with exactly the same SQL that Django would, but its a good idea to do so, just to be sure everythings in sync.)
然而,这里总是存在先有鸡还是先有蛋的问题,为了弄清新的数据列怎么用SQL描述,你需要查看 manage.py sqlall 的执行结果,它列出了模型中已经存在的字段。(注意:你不需要像Django中的SQL一模一样的创建你的列,但是这确实是一个好主意,从而保证所有都是同步的)
The solution to the chicken-and-egg problem is to use a development environment instead of making the changes on a production server. (You are using a testing/development environment, right?) Here are the detailed steps to take.
解决鸡和蛋的问题的方法就是先在开发环境而不是发布服务器上修改。(你现在用的就是测试/开发环境,不是吗?)下面是详细的步骤。
First, take these steps in the development environment (i.e., not on the production server):
首先,在开发环境中执行下面的步骤(也就是说,不是在发布服务器上):
Add the field to your model.
把这个字段添加到你的模型中.
Run manage.py sqlall [yourapp] to see the new CREATE TABLE statement for the model. Note the column definition for the new field.
运行 manage.py sqlall [yourapp] 会看到模型的新的 CREATE TABLE 语句。 注意新的字段的列定义。
Start your databases interactive shell (e.g., psql or mysql , or you can use manage.py dbshell ). Execute an ALTER TABLE statement that adds your new column.
启动您的数据库交互shell(也就是 psql 或 mysql , 或者您也可以使用 manage.py dbshell )。 执行一个 ALTER TABLE 语句,添加您的新列。
(Optional.) Launch the Python interactive shell with manage.py shell and verify that the new field was added properly by importing the model and selecting from the table (e.g., MyModel.objects.all()[:5] ).
4. (可选)用 manage.py shell 启动Python交互式shell,并通过引入模型并选择表 验证新的字段已被正确添加(比如, MyModel.objects.all()[:5] )。
Then on the production server perform these steps:
然后在发布服务器上执行下面的步骤:
Start your databases interactive shell.
启动你的数据库的交互式命令行;
Execute the ALTER TABLE statement you used in step 3 of the development environment steps.
执行 ALTER TABLE 语句,也就是在开发环境中第3步执行的语句;
Add the field to your model. If youre using source-code revision control and you checked in your change in development environment step 1, now is the time to update the code (e.g., svn update , with Subversion) on the production server.
添加字段到你的模型中。如果你在开发时使用了版本控制系统并checkin了你的修改,现在可以更新 代码到发布服务器上了(例如,使用Subverison的话就是 svn update )。
Restart the Web server for the code changes to take effect.
重启Web服务器以使代码修改生效。
For example, lets walk through what wed do if we added a num_pages field to the Book model described earlier in this chapter. First, wed alter the model in our development environment to look like this:
例如,让我们通过给 Book 模型添加一个 num_pages 字段来演示一下。 首先,我们在开发环境中这样修改模型:
class Book(models.Model): title = models.CharField(maxlength=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() **num_pages = models.IntegerField(blank=True, null=True)** def __str__(self): return self.title
(Note: Read the Adding NOT NULL Columns sidebar for important details on why we included blank=True and null=True .)
(注意:我们这里为什么写 blank=True 和 null=True 呢?阅读题为“添加非空字段”的侧边栏获取更多信息。)
Then wed run the command manage.py sqlall books to see the CREATE TABLE statement. It would look something like this:
然后我们运行命令 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 );
The new column is represented like this:
新加的字段SQL描述是这样的:
"num_pages" integer NULL
Next, wed start the databases interactive shell for our development database by typing psql (for PostgreSQL), and wed execute the following statements:
接下来,我们启动数据库交互命令界面,例如Postgresql是执行 psql , 并执行下面的语句:
ALTER TABLE books_book ADD COLUMN num_pages integer;
Adding NOT NULL Columns
添加非空字段(NOT NULL)
Theres a subtlety here that deserves mention. When we added the num_pages field to our model, we included the blank=True and null=True options. We did this because a database column will contain NULL values when you first create it.
这里有一个要注意的地方。在添加 num_pages 字段时我们使用了 blank=True 和 null=True 可选项。 我们之所以这么做是因为在数据库创建时我们想允许字段值为NULL。
However, its also possible to add columns that cannot contain NULL values. To do this, you have to create the column as NULL , then populate the columns values using some default(s), and then alter the column to set the NOT NULL modifier. For example:
当然,也可以在添加字段时设置值不能为NULL。要实现这个,你不得不先创建一个 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;
If you go down this path, remember that you should leave off blank=True and null=True in your model.
如果你这样做了, 记得要把 blank=True 和 null=True 从你的模型中拿掉。
After the ALTER TABLE statement, wed verify that the change worked properly by starting the Python shell and running this code:
执行完 ALTER TABLE 语句, 我们确认一下修改是否正确,启动Python交互界面并执行下面语句:
>>> from mysite.books.models import Book >>> Book.objects.all()[:5]
If that code didnt cause errors, wed switch to our production server and execute the ALTER TABLE statement on the production database. Then, wed update the model in the production environment and restart the Web server.
如果没有错误,我们就可以转到发布服务器来在数据库上执行 ALTER TABLE 语句了。然后, 再更新模型并重启WEB服务器。
Removing a field from a model is a lot easier than adding one. To remove a field, just follow these steps:
从模型里删除一个字段可要比增加它简单多了。删除一个字段仅需要做如下操作:
Remove the field from your model and restart the Web server.
从你的模型里删除这个字段,并重启Web服务器。
Remove the column from your database, using a command like this:
使用如下面所示的命令,从你的数据库中删掉该列:
ALTER TABLE books_book DROP COLUMN num_pages;
Because many-to-many fields are different than normal fields, the removal process is different:
因为many-to-many字段同普通字段有些不同,它的删除过程也不一样:
Remove the ManyToManyField from your model and restart the Web server.
删除掉你的模型里的 ManyToManyField ,并且重启Web服务器。
Remove the many-to-many table from your database, using a command like this:
使用如下面所示的命令,删除掉你数据库里的many-to-many表:
DROP TABLE books_books_publishers;
Removing a model entirely is as easy as removing a field. To remove a model, just follow these steps:
完全删除一个模型就像删除一个字段一样简单。删除模型仅需要做如下步骤:
Remove the model from your models.py file and restart the Web server.
将此模型从你的 models.py 文件里删除,并且重启Web服务器。
Remove the table from your database, using a command like this:
使用如下的命令,将此表从你的数据库中删除:
DROP TABLE books_book;
Once youve defined your models, the next step is to populate your database with data. You might have legacy data, in which case Chapter 16 will give you advice about integrating with legacy databases. You might rely on site users to supply your data, in which case Chapter 7 will teach you how to process user-submitted form data.
一旦你定义了你的模型,接下来就是要把数据导入数据库里了。你可能已经有现成的数据了,请看第十六章,如何集成现有的数据库。也可能数据是用户提供的,第七章中还会教你怎么处理用户提交的数据。
But in some cases, you or your team might need to enter data manually, in which case it would be helpful to have a Web-based interface for entering and managing data. The next chapter covers Djangos admin interface, which exists precisely for that reason.
有时候,你和你的团队成员也需要手工输入数据,这时候如果能有一个基于Web的数据输入和管理的界面 就很有帮助。下一章将讲述Django的管理界面,它就是专门干这个活的。
关于本评注系统
本站使用上下文关联的评注系统来收集反馈信息。不同于一般对整章做评注的做法, 我们允许你对每一个独立的“文本块”做评注。一个“文本块”看起来是这样的:
一个“文本块”是一个段落,一个列表项,一段代码,或者其他一小段内容。 你选中它会高亮度显示:
要对文本块做评注,你只需要点击它旁边的标识块:
我们会仔细阅读每个评论,如果可能的话我们也会把评注考虑到未来的版本中去:
如果你愿意你的评注被采用,请确保留下你的全名 (注意不是昵称或简称)
Many, many thanks to Jack Slocum; the inspiration and much of the code for the comment system comes from Jack's blog, and this site couldn't have been built without his wonderful
YAHOO.ext
library. Thanks also to Yahoo for YUI itself.