模型数据库
模型是您的数据惟一并且准确的信息来源。它包含您正在储存的数据的重要字段和行为。通常来讲,每个模型都映射一个数据库表。django
基础:app
- 每一个模型都是一个 Python 的类,这些类继承
django.db.models.Model
- 模型类的每一个属性都至关于一个数据库的字段。每一个属性映射为一个数据库列。
- 综上诉说,Django 给你一个自动生成访问数据库的 API
默认状况下表名是appname_classname,由app名和你建立的模型名组成,能够自定义。ide
默认状况下会在表中自动建立一个'id'字段,它是默认主键字段,能够本身手动设置url
一旦你定义了你的模型,你须要告诉Django你准备*使用*这些模型。你须要修改设置文件中的INSTALLED_APPS
,在这个设置中添加包含你 models.py
文件的模块的名字。spa
字段代理
模型中的每一个字段都应该是相应Field
类的实例 。Django使用字段类类型来肯定一些事情:code
- 字段类型用以指定数据库数据类型(如:
INTEGER
,VARCHAR
,TEXT
) - 默认的HTML表单输入框</ ref / forms / widgets>(如:<input type =“text”> <select>)
- 用于Django admin和自动生成表单的基本验证。
每一个字段都采用一组特定于字段的参数。例如, CharField
(及其子类)须要一个max_length
参数,该 参数指定VARCHAR
用于存储数据的数据库字段的大小。还有一些通用参数:orm
null
若是True
,Django将NULL
在数据库中存储空值。默认是False
。对象
blank若是True
,该字段容许为空。默认是False
。
请注意,这不一样于null
。 null
纯粹与数据库相关,而 blank
与验证相关。若是字段有blank=True
,则表单验证将容许输入空值。若是字段有blank=False
,则须要该字段。
choices
2元组的可迭代(例如,列表或元组),用做此字段的选项。若是给出了这个,则默认表单小部件将是一个选择框而不是标准文本字段,并将限制对给定选项的选择。
选项列表以下所示:
YEAR_IN_SCHOOL_CHOICES = ( ('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior'), ('GR', 'Graduate'), )
每一个元组中的第一个元素是将存储在数据库中的值。第二个元素由字段的窗体小部件显示。
给定模型实例,choices
可使用该get_FOO_display()
方法访问字段的显示值。
default
字段的默认值。这能够是值或可调用对象。若是可调用,则每次建立新对象时都会调用它。help_text
使用表单小部件显示额外的“帮助”文本。即便您的字段未在表单上使用,它也对文档颇有用。
primary_key若是True
,此字段是模型的主键。
若是没有primary_key=True
为模型中的任何字段指定,Django将自动添加一个 IntegerField
来保存主键,所以primary_key=True
除非要覆盖默认的主键行为,不然不须要设置 任何字段。主键字段是只读的。若是更改现有对象上主键的值而后保存它,则将建立一个与旧对象并排的新对象。
unique
若是True
,该字段在整个表格中必须是惟一的。
自动主键字段
默认状况下,Django为每一个模型提供如下字段:
id = models.AutoField(primary_key=True)
这是一个自动递增的主键。
若是您要指定自定义主键,只需primary_key=True
在其中一个字段中指定便可 。若是Django看到你明确设置Field.primary_key
,它将不会添加自动 id
列。
每一个模型只须要一个字段primary_key=True
(显式声明或自动添加)。
详细字段名
除了和 以外ForeignKey
, 每一个字段类型都采用可选的第一个位置参数 - 一个详细的名称。若是没有给出详细名称,Django将使用字段的属性名称自动建立它,将下划线转换为空格。ManyToManyField
OneToOneField
在此示例中,详细名称为:"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", )
关系
显然,关系数据库的力量在于将表相互关联。Django提供了定义三种最多见数据库关系类型的方法:多对一,多对多和一对一。
要定义多对一关系,请使用django.db.models.ForeignKey
。您能够像使用任何其余Field
类型同样使用它:将其包含为模型的类属性。
ForeignKey
须要一个位置参数:与模型相关的类。
from django.db import models class Manufacturer(models.Model): # ... pass class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE) # ...
多对多关系
要定义多对多关系,请使用 ManyToManyField
。您能够像使用任何其余Field
类型同样使用它 :将其包含为模型的类属性。
ManyToManyField
须要一个位置参数:与模型相关的类。
例如,若是a Pizza
有多个Topping
对象 - 也就是说,a Topping
能够在多个比萨饼上,每一个Pizza
都有多个浇头 - 这就是你如何表示:
from django.db import models class Topping(models.Model): # ... pass class Pizza(models.Model): # ... toppings = models.ManyToManyField(Topping)
建议(但不要求)a的名称 ManyToManyField
(toppings
在上面的示例中)是描述相关模型对象集的复数。
哪一种型号有什么关系并不重要 ManyToManyField
,但您应该只将其放在其中一种型号中 - 而不是两种型号。
一般,ManyToManyField
实例应该放在要在表单上编辑的对象中。在上面的例子中, toppings
是Pizza
(而不是Topping
拥有pizzas
ManyToManyField
),由于考虑披萨的配料比在多个披萨上打顶更天然。在上面设置的方式,Pizza
表单将容许用户选择浇头。
多对多关系中的额外字段
当您只处理简单的多对多关系时,例如混合和匹配比萨饼和浇头,ManyToManyField
您只须要一个标准 。可是,有时您可能须要将数据与两个模型之间的关系相关联。
例如,考虑应用程序跟踪音乐家所属的音乐组的状况。一我的与他们所属的团体之间存在多对多的关系,所以您可使用a ManyToManyField
来表示这种关系。可是,您可能但愿收集的成员资格有不少详细信息,例如此人加入该组的日期。
对于这些状况,Django容许您指定将用于管理多对多关系的模型。而后,您能够在中间模型上添加额外的字段。中间模型与ManyToManyField
使用 through
参数指向将充当中介的模型相关联 。对于咱们的音乐家示例,代码看起来像这样:
from django.db import models class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through='Membership') def __str__(self): 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)
设置中间模型时,您明确指定多对多关系中涉及的模型的外键。此显式声明定义了两个模型的关联方式。
一对一的关系
要定义一对一的关系,请使用 OneToOneField
。您能够像使用任何其余Field
类型同样使用它 :将其包含为模型的类属性。
当对象以某种方式“扩展”另外一个对象时,这对于对象的主键最有用。
OneToOneField
须要一个位置参数:与模型相关的类。
例如,若是您正在构建“地点”数据库,您将在数据库中构建很是标准的内容,例如地址,电话号码等。而后,若是你想在这些地方创建一个餐馆数据库,而不是重复本身并在Restaurant
模型中复制这些字段,你能够作Restaurant
一个OneToOneField
to Place
(由于一个餐馆“是一个”地方;事实上,处理这一般使用 继承,它涉及隐式的一对一关系)。
OneToOneField
字段也接受可选 parent_link
参数。
OneToOneField
用于自动成为模型主键的类。这再也不是真的(尽管你能够手动传入primary_key
参数)。所以,如今能够OneToOneField
在单个模型上具备多个类型的字段 。
注意:
因为Django的查询查找语法的工做方式,字段名称不能在一行中包含多个下划线。例如:
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
Meta
选项
使用内部提供模型元数据,以下所示:class Meta
from django.db import models class Ox(models.Model): horn_length = models.IntegerField() class Meta: ordering = ["horn_length"] verbose_name_plural = "oxen"
模型元数据是“任何不是字段的东西”,例如排序选项(ordering
),数据库表名(db_table
)或人类可读的单数和复数名称(verbose_name
和 verbose_name_plural
)。不是必要,添加到模型是彻底可选的。
模型属性
-
objects
模型最重要的属性是Manager
。它是为Django模型提供数据库查询操做的接口,用于 从数据库中 检索实例。若是Manager
未定义自定义,则默认名称为objects
。
模型方法
在模型上定义自定义方法,以向对象添加自定义“行级”功能。虽然Manager
方法旨在执行“表格范围”的事情,但模型方法应该做用于特定的模型实例。
__str__()
Python“魔术方法”,返回任何对象的字符串表示形式。这是Python和Django在模型实例须要被强制并显示为纯字符串时将使用的内容。最值得注意的是,当您在交互式控制台或管理员中显示对象时会发生这种状况。
你老是想要定义这个方法; 默认状况下根本没有用。
get_absolute_url()
这告诉Django如何计算对象的URL。Django在其管理界面中使用它,而且只要它须要找出对象的URL。
具备惟一标识它的URL的任何对象都应定义此方法。
覆盖预约义的模型方法
还有另外一组模型方法,它们封装了一些您想要自定义的数据库行为。特别是你常常想改变方式save()
和 delete()
工做方式。
您能够自由地覆盖这些方法(以及任何其余模型方法)来改变行为。
用于覆盖内置方法的经典用例是,若是您但愿在保存对象时发生某些事情。例如(参见 save()
它接受的参数的文档):
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def save(self, *args, **kwargs): do_something() super().save(*args, **kwargs) # Call the "real" save() method. do_something_else()
重要的是要记住调用超类方法 - 那就是业务 - 以确保对象仍然保存到数据库中。若是您忘记调用超类方法,则不会发生默认行为,也不会触及数据库。super().save(*args, **kwargs)
传递能够传递给模型方法的参数也很重要 - 这就是位的做用。Django将不时扩展内置模型方法的功能,增长新的参数。若是在方法定义中使用,则能够保证代码在添加时自动支持这些参数。*args,**kwargs
*args, **kwargs
继承模型
模型继承在Django中与普通类继承在Python中的工做方式几乎彻底相同,但也仍有遵循本页开头的内容。这意味着其基类应该继承自django.db.models.Model
。
您必须作出的惟一决定是,您是但愿父模型自己是模型(使用本身的数据库表),仍是父母只是经过子模型可见的公共信息的持有者。
Django中有三种可能的继承方式。
- 一般,您只想使用父类来保存您不但愿为每一个子模型键入的信息。这个类不会被孤立使用,因此抽象基类就是你所追求的。
- 若是你是现有模型的子类(多是彻底来自另外一个应用程序的东西),并但愿每一个模型都有本身的数据库表,那么 多表继承是最佳选择。
- 最后,若是您只想修改模型的Python级行为,而不以任何方式更改模型字段,则可使用 代理模型。
抽象基类
当您想要将一些公共信息放入许多其余模型时,抽象基类很是有用。你写你的基类,并把abstract=True
在元 类。而后,此模型将不用于建立任何数据库表。相反,当它用做其余模型的基类时,其字段将添加到子类的字段中。
一个例子:
from django.db import models class CommonInfo(models.Model): name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = True class Student(CommonInfo): home_group = models.CharField(max_length=5)
该Student
模型将有三个领域:name
,age
和 home_group
。该CommonInfo
模型不能用做普通的Django模型,由于它是一个抽象基类。它不生成数据库表或具备管理器,而且没法直接实例化或保存。
从抽象基类继承的字段可使用其余字段或值覆盖,也可使用删除None
。
对于许多用途,这种类型的模型继承将彻底符合您的要求。它提供了一种在Python级别分解公共信息的方法,同时仍然只在数据库级别为每一个子模型建立一个数据库表。
Meta
继承
当建立抽象基类时,Django使 您在基类中声明的任何Meta内部类可用做属性。若是子类没有声明本身的Meta 类,它将继承父类的Meta。若是孩子想要扩展父类的Meta类,它能够将其子类化。例如:
from django.db import models class CommonInfo(models.Model): # ... class Meta: abstract = True ordering = ['name'] class Student(CommonInfo): # ... class Meta(CommonInfo.Meta): db_table = 'student_info'
Django确实对抽象基类的Meta类进行了一次调整:在安装Meta属性以前,它设置了abstract=False
。这意味着抽象基类的子节点自己不会自动成为抽象类。固然,您能够建立一个继承自另外一个抽象基类的抽象基类。您只须要记住abstract=True
每次都明确设置。
多表继承
Django支持的第二种模型继承是当层次结构中的每一个模型都是模型自己时。每一个模型对应于本身的数据库表,能够单独查询和建立。继承关系引入子模型与其每一个父模型之间的连接(经过自动建立OneToOneField
)。例如:
from django.db import models class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) class Restaurant(Place): serves_hot_dogs = models.BooleanField(default=False) serves_pizza = models.BooleanField(default=False)
尽管数据将驻留在不一样的数据库表Place
中Restaurant
,但全部字段都将可用。因此这些都是可能的:
>>> Place.objects.filter(name="Bob's Cafe") >>> Restaurant.objects.filter(name="Bob's Cafe")
Meta
和多表继承
在多表继承状况下,子类从其父类的Meta类继承是没有意义的。全部的Meta选项都已经应用于父类,而且再次应用它们一般只会致使矛盾的行为(这与基类自己不存在的抽象基类状况造成对比)。
所以,子模型没法访问其父级的Meta类。可是,有一些有限的状况,子进程从父进程继承行为:若是子进程没有指定 ordering
属性或 get_latest_by
属性,它将从其父进程继承它们。
若是父级有一个排序而你不但愿孩子有任何天然顺序,你能够明确地禁用它:
class ChildModel(ParentModel): # ... class Meta: # Remove parent's ordering effect ordering = []
继承和反向关系
由于多表继承使用隐式 OneToOneField
连接子项和父项,因此能够从父项向下移动到子项,如上例所示。可是,这会占用名称和 关系的默认related_name
值 。若是要将这些类型的关系放在父模型的子类上,则 必须 在每一个此类字段上指定该属性。
class Supplier(Place): customers = models.ManyToManyField(Place)
这会致使错误,添加related_name
到该customers
字段将解决错误:models.ManyToManyField(Place,related_name='provider')
代理模型
使用多表继承时,会为模型的每一个子类建立一个新的数据库表。这一般是所需的行为,由于子类须要一个位置来存储基类上不存在的任何其余数据字段。可是,有时您只想更改模型的Python行为 - 可能更改默认管理器或添加新方法。
这就是代理模型继承的用途:为原始模型建立代理。您能够建立,删除和更新代理模型的实例,而且将保存全部数据,就像使用原始(非代理)模型同样。不一样之处在于您能够更改代理中的默认模型排序或默认管理器等内容,而无需更改原始内容。
代理模型声明为普通模型。你经过设置类的proxy
属性告诉Django它是一个代理模型。Meta
True
例如,假设您要向Person
模型添加方法。你能够这样作:
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) class MyPerson(Person): class Meta: proxy = True def do_something(self): # ... pass
该MyPerson
班在同一个数据库表做为它的父工做 Person
类。特别是,任何新的实例Person
也能够经过MyPerson
,反之亦然:
>>> p = Person.objects.create(first_name="foobar") >>> MyPerson.objects.get(first_name="foobar") <MyPerson: foobar>
你仍然可使用一个代理模型来定义模型的默认排序方法你也许不会想一直对“Persion”进行排序,可是一般状况下用代理模型根据“姓氏”属性进行排序这很简单。:
class OrderedPerson(Person): class Meta: ordering = ["last_name"] proxy = True
如今,正常Person
查询将是无序的,OrderedPerson
查询将按顺序排序last_name
。
代理继承和非托管模型之间的差别¶
代理模型继承可能看起来与使用managed
模型Meta
类的属性建立非托管模型很是类似。
经过仔细设置,Meta.db_table
您能够建立一个非托管模型,该模型能够隐藏现有模型并为其添加Python方法。可是,若是您进行任何更改,则须要保持两个副本同步,这将是很是重复和脆弱的。
另外一方面,代理模型的行为与它们所表明的模型彻底相同。它们始终与父模型同步,由于它们直接继承其字段和管理器。
通常规则是:
- 若是要镜像现有模型或数据库表,而且不想要全部原始数据库表列,请使用
Meta.managed=False
。该选项一般用于建模不受Django控制的数据库视图和表。 - 若是您想要更改模型的仅Python行为,但保留与原始字段相同的字段,请使用
Meta.proxy=True
。这进行了设置,以便在保存数据时代理模型是原始模型的存储结构的精确副本。