八Django之Models(译)

Posted skyflask

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了八Django之Models(译)相关的知识,希望对你有一定的参考价值。

本文为翻译的官方文档,原文地址为:https://docs.djangoproject.com/en/1.11/topics/db/models/。学习工具的最佳地方-官网。

model是唯一且明确的数据源。它包含了我们储存的数据的基本字段和方法。通常,每个模型映射到一张数据表。
基本概念:
  • 每个模型都是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之Models(译)的主要内容,如果未能解决你的问题,请参考以下文章

译ECMAScript 2016, 2017, 2018 新特性之必读篇

Django之Models

Django之admin中管理models中的表格

Django - Field types reference (译)

Django - Field types reference (译)

Django REST framework序列化