From 5e1b1e707d407c9235850db510ceaf2d8c053c57 Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Tue, 15 Sep 2015 18:03:01 +0800 Subject: [PATCH] 9.15 --- 2_1_1_Model syntax.md | 795 ++++++++++++++++++++----------- 5_1_2_Form API.md | 956 ++++++++++++++++++++++++++++++++++++++ 5_1_4_Built-in widgets.md | 639 +++++++++++++++++++++++++ 6_5_1_Introduction.md | 24 + 4 files changed, 2132 insertions(+), 282 deletions(-) create mode 100644 5_1_2_Form API.md create mode 100644 5_1_4_Built-in widgets.md create mode 100644 6_5_1_Introduction.md diff --git a/2_1_1_Model syntax.md b/2_1_1_Model syntax.md index 9d40690..52a4948 100644 --- a/2_1_1_Model syntax.md +++ b/2_1_1_Model syntax.md @@ -1,20 +1,16 @@ - +# 模型 -# 模型 # - -模型是有关你的数据的,简单、确定的信息源。它包含了你所储存数据的一些必要的字段和行为。通常来说,每个模型都对应数据库中的一张表。 +模型是你的数据的唯一的、权威的信息源。它包含你所储存数据的必要字段和行为。通常,每个模型对应数据库中唯一的一张表。 基础: -+ 每个模型都是django.db.models.Model类的子类。 -+ 模型的每个属性都表示数据库中的一个字段。 -+ Django 会提供一套自动生成的用于数据库访问的API;详见执行查询。 +* 每个模型都是[`django.db.models.Model`](../../ref/models/instances.html#django.db.models.Model "django.db.models.Model") 的一个Python 子类。 +* 模型的每个属性都表示数据库中的一个字段。 +* Django 提供一套自动生成的用于数据库访问的API;详见[_执行查询_](queries.html)。 -## 简短的例子 ## +## 简短的例子 -这个例子定义了一个Person模型,它有 first_name和last_name两个属性 +这个例子定义一个`Person`模型,它有`first_name` 和`last_name` 两个属性: ``` from django.db import models @@ -22,11 +18,12 @@ from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) + ``` -first_name和last_name是模型的两个字段。每个字段都被指定成一个类属性,每个属性 都映射一个数据库的列。 +`first_name`和`last_name`是模型的两个[字段](#fields)。每个字段都被指定成一个类属性,每个属性映射到一个数据库的列。 -上面的Person模型会在数据库中创建这样一张表: +上面的`Person` 模型会在数据库中创建这样一张表: ``` CREATE TABLE myapp_person ( @@ -34,37 +31,41 @@ CREATE TABLE myapp_person ( "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL ); + ``` -一些技术上的注意事项:字段类型 +一些技术上的注意事项: -这个表的名称myapp_person,是根据 模型中的元数据自动生成的,也可以覆写为别的名称,详见Table names。 -id 字段是自动添加的,但这个行为可以被重写。详见Automatic primary key fields。 -这个例子使用 PostgreSQL 语法格式化CREATE TABLESQL 语句,要注意的是 Django 是根据settings file配置中指定的数据库类型来生成相应的 SQL 语句。 +* 这个表的名称`myapp_person`,是根据 模型中的元数据自动生成的,也可以覆写为别的名称,详见[_Table names_](../../ref/models/options.html#table-names)。 +* `id` 字段是自动添加的,但这个行为可以被重写。详见[_自增主键字段_](#automatic-primary-key-fields)。 +* 这个例子中的`CREATE TABLE` SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据[_设置文件_](../settings.html) 中指定的数据库类型来使用相应的SQL 语句。 -## 使用模型 ## +## 使用模型 -一旦你定义了模型,就要通知Django启用这些模型,你要做的就是修改配置文件中的INSTALLED_APPS 设置,在其中添加models.py所在应用的名称。 +定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的[`INSTALLED_APPS`](../../ref/settings.html#std:setting-INSTALLED_APPS) 设置,在其中添加`models.py`所在应用的名称。 -例如,假设你的 model 定义在 mysite.myapp.models 中 ( mysite 这个包是由 manage.py startapp 脚本创建的),那么 INSTALLED_APPS 就应该包含下面这行: +例如,如果你的应用的模型位于`myapp.models`模块([`manage.py startapp`](../../ref/django-admin.html#django-admin-startapp) 脚本为一个应用创建的包结构),[`INSTALLED_APPS`](../../ref/settings.html#std:setting-INSTALLED_APPS)部分看上去应该是: ``` INSTALLED_APPS = ( #... - 'mysite.myapp', + 'myapp', #... ) + ``` -在 INSTALLED_APPS 中添加新应用之后,要运行 manage.py syncdb 同步数据库。 +当你在[`INSTALLED_APPS`](../../ref/settings.html#std:setting-INSTALLED_APPS) 中添加新的应用名时,请确保运行命令[`manage.py migrate`](../../ref/django-admin.html#django-admin-migrate),可以首先使用[`manage.py makemigrations`](../../ref/django-admin.html#django-admin-makemigrations) 来为它们生成迁移脚本。 -## 字段 ## +## 字段 -模型 中不可或缺且最为重要的,就是字段集,它是一组数据库字段的列表。字段被指定为类属性。要注意选择字段名称的时候不要和models API 冲突,比如clean, save, 或者delete。 +模型中不可或缺且最为重要的,就是字段集,它是一组数据库字段的列表。字段被指定为类属性。 要注意选择的字段名称不要和[_模型 API_](../../ref/models/instances.html) 冲突,比如`clean`、`save` 或者`delete`。 例如: ``` +from django.db import models + class Musician(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) @@ -75,82 +76,91 @@ class Album(models.Model): name = models.CharField(max_length=100) release_date = models.DateField() num_stars = models.IntegerField() + ``` -## 字段类型 ## +### 字段类型 + +模型中的每个字段都是 [`Field`](../../ref/models/fields.html#django.db.models.Field "django.db.models.Field") 子类的某个实例。Django 根据字段类的类型确定以下信息: -model 中的每个字段都是 Field 子类的某个实例。Django 根据字段类的类型确定以下信息: +* 数据库当中的列类型 (比如, `INTEGER`, `VARCHAR`)。 +* 渲染表单时使用的默认HTML [_部件_](../../ref/forms/widgets.html)(例如,``, ``, ` +Url: +Comment: + +``` + +## 检查表单数据是否改变 + +`Form.``has_changed`() + +当你需要检查表单的数据是否从初始数据发生改变时,可以使用`表单`的`has_changed()` 方法。 + +``` +>>> data = {'subject': 'hello', +... 'message': 'Hi there', +... 'sender': 'foo@example.com', +... 'cc_myself': True} +>>> f = ContactForm(data, initial=data) +>>> f.has_changed() +False + +``` + +当提交表单时,我们可以重新构建表单并提供初始值,这样可以实现比较: + +``` +>>> f = ContactForm(request.POST, initial=data) +>>> f.has_changed() + +``` + +如果`request.POST` 中的数据与[`initial`](#django.forms.Form.initial "django.forms.Form.initial") 中的不同,`has_changed()` 将为`True`,否则为`False`。 计算的结果是通过调用表单每个字段的[`Field.has_changed()`](fields.html#django.forms.Field.has_changed "django.forms.Field.has_changed") 得到的。 + +## 从表单中访问字段 + +`Form.``fields` + +你可以从[`表单`](#django.forms.Form "django.forms.Form")实例的`fields`属性访问字段: + +``` +>>> for row in f.fields.values(): print(row) +... + + + +>>> f.fields['name'] + + +``` + +可你可以修改[`表单`](#django.forms.Form "django.forms.Form")实例的字段来改变字段在表单中的表示: + +``` +>>> f.as_table().split('\n')[0] +'Name:' +>>> f.fields['name'].label = "Username" +>>> f.as_table().split('\n')[0] +'Username:' + +``` + +注意不要改变`base_fields` 属性,因为一旦修改将影响同一个Python 进程中接下来所有的`ContactForm` 实例: + +``` +>>> f.base_fields['name'].label = "Username" +>>> another_f = CommentForm(auto_id=False) +>>> another_f.as_table().split('\n')[0] +'Username:' + +``` + +## 访问“清洁”的数据 + +`Form.``cleaned_data` + +[`表单`](#django.forms.Form "django.forms.Form")类中的每个字段不仅负责验证数据,还负责“清洁”它们 —— 将它们转换为正确的格式。这是个非常好用的功能,因为它允许字段以多种方式输入数据,并总能得到一致的输出。 + +例如,[`DateField`](fields.html#django.forms.DateField "django.forms.DateField") 将输入转换为Python 的 `datetime.date` 对象。无论你传递的是`'1994-07-15'` 格式的字符串、`datetime.date` 对象、还是其它格式的数字,`DateField` 将始终将它们转换成`datetime.date` 对象,只要它们是合法的。 + +一旦你创建一个[`表单`](#django.forms.Form "django.forms.Form")实例并通过验证后,你就可以通过它的`cleaned_data` 属性访问清洁的数据: + +``` +>>> data = {'subject': 'hello', +... 'message': 'Hi there', +... 'sender': 'foo@example.com', +... 'cc_myself': True} +>>> f = ContactForm(data) +>>> f.is_valid() +True +>>> f.cleaned_data +{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'} + +``` + +注意,文本字段 —— 例如,`CharField` 和`EmailField` —— 始终将输入转换为Unicode 字符串。我们将在这篇文档的后面将是编码的影响。 + +如果你的数据_没有_ 通过验证,`cleaned_data` 字典中只包含合法的字段: + +``` +>>> data = {'subject': '', +... 'message': 'Hi there', +... 'sender': 'invalid email address', +... 'cc_myself': True} +>>> f = ContactForm(data) +>>> f.is_valid() +False +>>> f.cleaned_data +{'cc_myself': True, 'message': 'Hi there'} + +``` + +`cleaned_data` 始终_只_ 包含`表单`中定义的字段,即使你在构建`表单` 时传递了额外的数据。在下面的例子中,我们传递一组额外的字段给`ContactForm` 构造函数,但是`cleaned_data` 将只包含表单的字段: + +``` +>>> data = {'subject': 'hello', +... 'message': 'Hi there', +... 'sender': 'foo@example.com', +... 'cc_myself': True, +... 'extra_field_1': 'foo', +... 'extra_field_2': 'bar', +... 'extra_field_3': 'baz'} +>>> f = ContactForm(data) +>>> f.is_valid() +True +>>> f.cleaned_data # Doesn't contain extra_field_1, etc. +{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'} + +``` + +当`表单`合法时,`cleaned_data` 将包含_所有_字段的键和值,即使传递的数据不包含某些可选字段的值。在下面的例子中,传递的数据字典不包含`nick_name` 字段的值,但是`cleaned_data` 任然包含它,只是值为空: + +``` +>>> from django.forms import Form +>>> class OptionalPersonForm(Form): +... first_name = CharField() +... last_name = CharField() +... nick_name = CharField(required=False) +>>> data = {'first_name': 'John', 'last_name': 'Lennon'} +>>> f = OptionalPersonForm(data) +>>> f.is_valid() +True +>>> f.cleaned_data +{'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'} + +``` + +在上面的例子中,`cleaned_data` 中`nick_name` 设置为一个空字符串,这是因为`nick_name` 是`CharField`而 `CharField` 将空值作为一个空字符串。每个字段都知道自己的“空”值 —— 例如,`DateField` 的空值是`None` 而不是一个空字符串。关于每个字段空值的完整细节,参见“内建的`Field` 类”一节中每个字段的“空值”提示。 + +你可以自己编写代码来对特定的字段(根据它们的名字)或者表单整体(考虑到不同字段的组合)进行验证。更多信息参见[_表单和字段验证_](validation.html)。 + +## 输出表单为HTML + +`表单`对象的第二个任务是将它渲染成HTML。很简单,`print` 它: + +``` +>>> f = ContactForm() +>>> print(f) + + + + + +``` + +如果表单是绑定的,输出的HTML 将包含数据。例如,如果字段是`` 的形式,其数据将位于`value` 属性中。如果字段是`` 的形式,HTML 将包含`checked="checked"`: + +``` +>>> data = {'subject': 'hello', +... 'message': 'Hi there', +... 'sender': 'foo@example.com', +... 'cc_myself': True} +>>> f = ContactForm(data) +>>> print(f) + + + + + +``` + +默认的输出时具有两个列的HTML 表格,每个字段对应一个``。注意事项: + +* 为了灵活性,输出_不_包含`` 和`
`、`
` 和`
` 以及`` 标签。你需要添加它们。 +* 每个字段类型有一个默认的HTML 表示。`CharField` 表示为一个``,`EmailField` 表示为一个``。`BooleanField` 表示为一个``。注意,这些只是默认的表示;你可以使用Widget 指定字段使用哪种HTML,我们将稍后解释。 +* 每个标签的HTML `name` 直接从`ContactForm` 类中获取。 +* 每个字段的文本标签 —— 例如`'Subject:'`、`'Message:'` 和`'Cc myself:'` 通过将所有的下划线转换成空格并大写第一个字母生成。再次提醒,这些只是默认的表示;你可以手工指定标签。 +* 每个文本标签周围有一个HTML `