官方文档链接

模型是数据信息的唯一并明确的来源。它包含了我们储存的数据的基本字段和行为。通常,每个模型映射到一张数据库表。
基本概念:
  • 每个模型都是django.db.models.Model的一个子类
  • 每个属性代表数据库中的一个字段
  • 在这些基础上,Django为我们提供了一个自动生成的数据库访问API。

简单示例

下面的示例模型定义了一个Person,其拥有一个first_name和一个last_name属性。

 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是模型(model)的字段(fields).每个字段指定为类的一个属性,每个属性映射到数据库的一列(column)。

上面的Person模型将建立类似下面这样一个数据库:

 CREATE TABLE myapp_person (
"id" serial NOT NULL primary_key,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);

关于上面代码的一些技术注释:

  • 上面数据表的名称"myapp_person",是从模型的元数据(metadata)自动导入的,但是可以覆写(overridden)。具体参见文档的Table names章节。
  • 自动添加了一个id字段,该行为也可以被覆写。具体参见文档的Automatic primary_key fields章节
  • 这是一段运用PostgreSQL语法建立数据表的SQL语句,但我们不用为此操心,针对后台settings file中设定好的数据库,Django都有量身定做的SQL。

运用模型

一旦定义好模型之后,需要告诉Django我们将使用这些模型。方法是通过编辑setting.py文件,在INSTALLED_APPS设定中添加包含了我们models.py的模块的名称。

例如,如果我们应用程序的模型存放在myapp.models模块中(该包结构在通过manage.py startapp命令创建应用程序时形成的),INSTALLED_APP应该一部分看起来如下:

 INSTALLED_APPS = [
#...
'myapp',
#...

当添加apps到INSTALLED_APPS以后,确保要运行mangae.py migrate指令,有时候还需要先用manage.py makemigrations进行迁移。

字段(Field)

一个模型最重要也是唯一要求的部分,就是定义数据库的字段。字段是由类的属性指定的。注意不要选择与模型API冲突的字段名,如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)
instrument = models.CharField(max_length=100) class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()

字段类型(Field types)

Each field in your model should be an instance of the appropriate Field class. Django uses the field class types to determine a few things:

模型的每一个字段都是相应字段类的一个实例。Django用字段类的类型来决定一些东西:

  • 列类型(column type),告诉数据库储存什么样的数据(比如INTERGER, VARCHAR, TEXT 等)。
  • 渲染表单字段时用默认的HTML部件(比如<input type="text">, <select>等)
  • Django的管理系统(admin)和自动生成的表单中,运用最低的验证要求。

Django拥有许多内置的字段类型;完整的清单参见model field reference。如果内置的字段满足不了要求,我们也可以方便的编写自己的字段,具体参见Writing custom model fields


字段选项(Field options)

每个字段都有一些特定的参数(参见 model field reference),例如,CharField(及其子类)要求一个最大长度参数来规定数据库VARCHAR字段的大小。

也有一些每种字段都通用的参数,都是可选的。在reference中有完整的解释,这里对最常用的一些做个快速的概览:

null

如果值为True,在数据库中Django将把空值储存为Null。默认值为False。

blank

如果值为True,字段允许为空。默认值为False。

注意它与null是不同的。null是纯粹数据库相关的,而blank是验证相关的。如果一个字段设置了blank=True, 表单验证将允许输入空值。如果设置了blank=False,该字段则是必需的。

choices

一个包含了二维元组的可迭代对象(比如,列表或者元组)作为字段的选项,默认的表单部件将从标准的文本换成选择框,选项限定为choice参数。

chiices列表看起来像这样:

YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)

每个元组中的第一个元素是将储存在数据库中的值。第二个元素将通过默认的表单部件显示,或者放在ModelChoiceField中。给出一个模型实例,可以通过get_FOO_display()方法来访问choices field正在显示的值。

示例:

from django.db import models

class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
default
字段的默认值。可以是一个值或者一个可调用的对象。如果是后者,每次新对象创建时都会调用。
help text
表单部件显示附加的帮助信息。如果字段不是用在表单上,该参数对文档还是很有用的。
primary_key
如果值为True,该字段设为模型的主键。
如果没有指定任何字段为主键,Django会自动添加一个IntegerField做为主键。所以如果不想覆写默认的主键,可以不设定任何字段的primary_key=True。
主键字段是只读的,如果你改写了一个原有主键的值然后储存,旧对象的旁边将会建立一个新对象。示例如下:
from django.db import models

class Fruit(models.Model):
name = models.CharField(max_length=100, primary_key=True)
>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
<QuerySet ['Apple', 'Pear']>

unique

如果为True,该字段在表中必须是唯一的。
再说一次,这些只是常用字段选项的简短描述,完整的信息请查看
common model field option reference

自动主键字段(Automatic primary_key fields)

默认情况下,Django中每个模型都有以下字段:
id = models.AutoField(primary_key=True)

这是一个自动增长的主键。

如果你想指定一个自定义的主键,只要将某个字段的选项设置primary_key=True。如果Django发现你已经明确了字段的主键(Field.primary_key),它就不会再添加自动的id列。

每个模型要求必须有一个字段设定为primary_key=True(明确指定的或者自动添加的都可以)。


详细字段名称(Verbose field names)

除ForeignKey, ManyToManyField 和 OneToOneField以外,每种字段都有一个第一位置参数-详细名称。如果该详细名称没有给出,Django会自动把字段的属性名称(下划线替换成空格)作为详细名称。

下面这个例子,详细名称是"person's first name":

first_name = models.CharField("person's first name", max_length=30)

下面这个例子,详细名称是"first name":

first_name = models.CharField(max_length=30)

ForeignKey, ManyToManyField 和 OneToOneField 要求第一位置参数为模型类,所以使用verbose_name关键字属性:

poll = models.ForeignKey(
Poll,
on_delete=models.CASCADE,
verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
verbose_name="related place",
)

有个惯例是不要将verbose_name的首字母大写,Django会再需要的时候自动将其首字母大写。


关系(Relationships)

很明显,关系型数据库的能力来源于相互关联的表。Django提供了方法定义三种最常见的数据库关系:many-to-one, many-to-many and one-to-one。

一对多关系(Many-to-one relationships)

通过django.db.models.ForeignKey来定义一对多关系。我们可以像使用其他字段类型一样:将其作为类属性包含在我们的模型中。

外键(ForeignKey)要求一个位置参数:该模型关联到哪个类。

比如,一个厂家制造了很多汽车,但是每辆汽车只有一个厂家,可以如下定义:

from django.db import models

class Manufacturer(models.Model):
# ...
pass class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
# ...

你也可以创建一个递归关系recursive relationships(一个对象有一个指向自身的外键)以及与尚未定义的模型的关系relationships to models not yet defined,具体参见he model field reference

建议,但没规定,将关联模型的小写名称作为外键字段的名称(上一个示例中的manufacturer)。当然,你可以叫外键字段任何名字,比如{

class Car(models.Model):
company_that_makes_it = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
)
# ...

参考信息

外键字段接收多种其他参数,具体参见the model field reference。这些选项帮助定义关系怎么工作,都是可选的。

关于访问向后相关(backwards-related)对象的细节,参见Following relationships backward example

关于示例代码,参见Many-to-one relationship model example


多对多关系(Many-to-many relationships)

要定义一个多对多关系,用MangToManyField。

我们可以像使用其他字段类型一样:将其作为类属性包含在我们的模型中。

MangToManyField要求一个位置参数:该模型关联到哪个类。

比如,一个披萨有多种配料,一种配料也可以用在多个披萨上。可以这样描述:

from django.db import models

class Topping(models.Model):
# ...
pass class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
跟外键一样,你也可以创建递归关系以及与尚未定义的模型的关系。
建议,但未规定,用关联模型对象的复数描述作为ManyToManyField的名称(上面示例中的toppings)。
多对多的两个模型中哪一个设置ManyToManyField都可以,但是只能设定一个,不能都设定。
通常,ManyToManyField实例会通过表单填写。在上面的示例中,tappings在Pizza中(而不是Topping有一个pizzas ManyToManyField)。因为一个有多种配料的披萨要比一种用在多个披萨上的配料要自然些。上面示例中,披萨表单中让用户可以选择配料。
参考信息
ManyToManyField接收多种其他参数,具体参见the model field reference。这些选项帮助定义关系怎么工作,都是可选的。

多对多关系中的额外字段

当你只需要处理简单的多对多关系时,比如混合搭配披萨和配料,标准的多对多字段就够了。然而,有时候你需要两个模型之间关系的辅助数据。

例如,设想有一种程序用来追踪音乐家属于哪个乐队。人和乐队之间是一种多对多的关系,所以我们可以用一个多对多字段来描述该关系。然而,还有很多其他相关信息我们也想收集,比如某人加入某个乐队的日期。

针对这种情况,Django允许我们指定模型,用来管理该多对多关系。这样我们可以在中间模型中设置额外的字段。通过设置through参数来指出哪个模型作为媒介,将中间模型与多对多字段联合起来。我们的乐队示例,代码应该差不多像这样:

from django.db import models

class Person(models.Model):
name = models.CharField(max_length=128) def __str__(self): # __unicode__ on Python 2
return self.name class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership') def __str__(self): # __unicode__ on Python 2
return self.name class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)

当我们建立中间模型时,我们明确指定了到该多对多关系相关模型的外键。此明确声明定义了这两个模型时怎么关联的。

中间模型中有一些约束:

  • 中间模型必须有且只有一个到源模型(我们示例中的Group)的外键, 或者明确指定ManyToManyField.through_fields。如果有超过一个外键而且没有指定through_fields,会引发验证错误。到目标模型(我们示例中的Person)的外键也有同样的限制。
  • 当一个模型通过中间模型到自身有多对多关系,指向同一个模型的两个外键是允许的,但是它们要按多对多关系的不同侧来处理。如果存在超过两个外键,也必须跟上面一样指定through_fields,否则会引发验证错误。
  • 当使用中间模型定义模型到自身的多对多关系时,必须使用symmetrical=False (参照 the model field reference)。

现在我们已经设置好ManyToMany字段来使用我们的中间模型(示例中的Membership),已为建立一些多对多关系做好准备。可以通过创建中间模型的实例来实行:

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
... date_joined=date(1960, 8, 1),
... invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>

不像普通的多对多字段,我们不可以用add(), create(), 或者 set() 去创建关系:

>>> # The following statements will not work
>>> beatles.members.add(john)
>>> beatles.members.create(name="George Harrison")
>>> beatles.members.set([john, paul, ringo, george])

为什么?我们不能只创建人和乐队之间的关系,我们还需要指定Membership模型要求的关系的所有信息。简单的add, create无法指定额外的信息。所以,运用中间模型的多对多关系是禁用它们的。创建这种类型关系的唯一方法是创建中间模型的实例。

出于类似的原因, remove()方法也被禁用了。因为有时候remove()方法无法提供足够的信息来确认该删除哪一个中间模型实例:

>>> Membership.objects.create(person=ringo, group=beatles,
... date_joined=date(1968, 9, 4),
... invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This will not work because it cannot tell which membership to remove
>>> beatles.members.remove(ringo)

然而,clear()方法可以删除一个实例的所有多对多关系:

>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>

一旦通过创建中间模型实例建立了多对多关系,我们就可以进行查询了。就像普通多对多关系一样,我们可以通过相关模型的属性进行查询:

# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>

也可以通过中间模型的属性进行查询:

# Find all the members of the Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(
... group__name='The Beatles',
... membership__date_joined__gt=date(1961,1,1))
<QuerySet [<Person: Ringo Starr]>

如果需要访问中间模型的信息,我们可以直接对中间模型进行查询:

>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

另一种访问相同信息的方法是从Person对象查询many-to-many reverse relationship

>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

一对一关系(One-to-one relationships)

用OneToOneField定义一对一关系。像任何其他字段类型一样使用:作为属性包含在模型中。

如果一个对象继承自其他对象,一对一关系最适合用于它的主键。

OneToOneField要求一个位置参数:哪个模型是相关的。

例如,我们要建立一个关于“处所”的数据库,可能要在数据库中建立很多标准的东西,如地址、电话号码等。这时,如果你还想在这些处所的基础上再建立一个餐厅的数据库,不必在餐厅模型中再重复指定这些字段,只需要在餐厅模型中建立一个指向处所模型的一对一字段。(因为一间餐厅也是一个处所。实际上,这种情况我们通常使用继承inheritance,它是毫无疑问的一对一关系。)

跟外键一样,你也可以创建递归关系以及与尚未定义的模型的关系。

参考信息

完整实例请参见One-to-one relationship model example 。

一对一字段也接收一个可选的 parent_link 参数。

一对一字段类以前会自动成为模型的主键,现在不是这样了(尽管可以手动设置primary_key参数,如果我们想的话)。因此,现在同一个模型中可以有多个一对一字段。

 

跨文件的模型

访问其他应用的模型是非常容易的。 在文件顶部我们定义模型的地方,导入相关的模型就可以了。然后,无论在哪里需要的话,都可以引用它。例如:

from django.db import models
from geography.models import ZipCode class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(ZipCode)

字段命名的限制

Django 对字段的命名只有两个限制:

  1. 字段的名称不能是Python 保留的关键字,因为这将导致一个Python 语法错误。例如:

    class Example(models.Model):
    pass = models.IntegerField() # 'pass' is a reserved word!
  2. 由于Django 查询语法的工作方式,字段名称中连续的下划线不能超过一个。例如:

    class Example(models.Model):
    foo__bar = models.IntegerField() # 'foo__bar' has two underscores!

这些限制有变通的方法,因为没有要求字段名称必须与数据库的列名匹配。参 见db_column选项。

SQL 的保留字例如joinwhere 和select,可以用作模型的字段名,因为Django 会对底层的SQL 查询语句中的数据库表名和列名进行转义。 它根据你的数据库引擎使用不同的引用语法。


自定义字段类型

如果已有的模型字段都不合适,或者你想用到一些很少见的数据库列类型的优点,你可以创建你自己的字段类型。创建你自己的字段在编写自定义的模型字段中有完整讲述。

Django-1.11中文文档-模型Models(一)的更多相关文章

  1. Django 1.10中文文档-模型参考

    模型字段 本文档包含了Django提供的全部模型 Field 包括 字段选项 和 字段类型 的API参考. 参见 如果内建的字段不能满足你的需求, 你可以蚕食 django-localflavor ( ...

  2. Django中文文档-模型Models(二):Meta选项、模型属性、模型方法

    元数据(Meta)选项 使用内部的class Meta 定义模型的元数据,例如: from django.db import models class Ox(models.Model): horn_l ...

  3. Django 1.10中文文档-第一个应用Part2-模型和管理站点

    本教程继续Part1.我们将设置数据库,创建您的第一个模型,并快速介绍Django的自动生成的管理网站. 数据库设置 现在,编辑mysite/settings.py.它是一个用模块级别变量表示Djan ...

  4. Django 1.10中文文档-执行查询

    Django 1.10中文文档: https://github.com/jhao104/django-chinese-doc 只要创建好 数据模型, Django 会自动为生成一套数据库抽象的API, ...

  5. Django 1.10中文文档-聚合

    Django 数据库抽象API 描述了使用Django 查询来增删查改单个对象的方法. 然而,有时候你要获取的值需要根据一组对象聚合后才能得到. 这个主题指南描述了如何使用Django的查询来生成和返 ...

  6. Django 1.10中文文档-第一个应用Part7-自定义管理站点

    开发第一个Django应用,Part7 本教程上接Part6.将继续完成这个投票应用,本节将着重讲解如果用Django自动生成后台管理网站. 自定义管理表单 通过admin.site.register ...

  7. Django 1.10中文文档-第一个应用Part5-测试

    本教程上接教程Part4. 前面已经建立一个网页投票应用,现在将为它创建一些自动化测试. 自动化测试简介 什么是自动化测试 测试是检查你的代码是否正常运行的行为.测试也分为不同的级别.有些测试可能是用 ...

  8. Django 1.10中文文档-第一个应用Part4-表单和通用视图

    本教程接Part3开始.继续网页投票应用程序,并将重点介绍简单的表单处理和精简代码. 一个简单表单 更新一下在上一个教程中编写的投票详细页面的模板polls/detail.html,让它包含一个HTM ...

  9. Django 1.10中文文档-第一个应用Part3-视图和模板

    本教程上接Django 1.10中文文档-第一个应用Part2-模型和管理站点.我们将继续开发网页投票这个应用,主要讲如何创建一个对用户开放的界面. 概览 视图是Django应用中的一“类”网页,它通 ...

随机推荐

  1. ElasticSearch 安装与运行

    公号:码农充电站pro 主页:https://codeshellme.github.io 本节来介绍 ES 的安装. 1,下载 ES ES 是基于 Java 语言开发的,因此,要安装 ES,首先需要有 ...

  2. Shiro权限项目

    目录 环境配置 spring容器 springmvc freemarker mybatis shiro 工具类 TokenManager.java Result.java 功能实现 登录 注册 个人中 ...

  3. Codeforces Round #666 (Div. 2)

    比赛链接:https://codeforces.com/contest/1397 A. Juggling Letters 题意 给出 $n$ 个字符串,可在字符串间任意移动字母,问最终能否使这 $n$ ...

  4. java swing JDialog 和 java.util.concurrent的使用

    参考链接: Java-Swing的JFrame的一些插件使用详解 java swing JDialog 使用 ScheduledExecutorService定时周期执行指定的任务 swing JDi ...

  5. uva10891 Game of Sum(博弈+区间dp+优化)

    题目:点击打开链接 题意:两个人做游戏,共有n个数,每个人可以任选一端取任意多连续的数,问两个人都想拿最多的情况下,先手最多比后手多拿多少分数. 思路:这题一开始想到的是用dp[i][j]表示区间[i ...

  6. hdu1394Minimum Inversion Number

    Problem Description The inversion number of a given number sequence a1, a2, ..., an is the number of ...

  7. fiddler抓包+安卓机 完成手机app抓包的配置 遇到的一些问题

    fiddler抓包+安卓模拟器完成手机app抓包的配置:fiddler抓包+雷电模拟器 完成手机app抓包的配置 其实在安卓真机上弄比在虚拟机上弄更麻烦一点,它们的步骤都差不多一样,就是在安卓真机上弄 ...

  8. Codeforces Round #531 (Div. 3) E. Monotonic Renumeration (构造)

    题意:给出一个长度为\(n\)的序列\(a\),根据\(a\)构造一个序列\(b\),要求: ​ 1.\(b_{1}=0\) ​ 2.对于\(i,j(i\le i,j \le n)\),若\(a_{i ...

  9. Educational Codeforces Round 56 (Rated for Div. 2) D. Beautiful Graph (二分图染色)

    题意:有\(n\)个点,\(m\)条边的无向图,可以给每个点赋点权\({1,2,3}\),使得每个点连的奇偶不同,问有多少种方案,答案对\(998244353\)取模. 题解:要使得每个点所连的奇偶不 ...

  10. Leetcode(38)-报数

    报数序列是指一个整数序列,按照其中的整数的顺序进行报数,得到下一个数.其前五项如下: 1. 1 2. 11 3. 21 4. 1211 5. 111221 1 被读作  "one 1&quo ...