模型
django 提供了一个强大的 orm(关系映射模型)系统.
模型包含了你要在数据库中创建的字段信息及对数据表的一些操作
使用模型
定义好模型后, 要告诉 django 使用这些模型, 你要做的就是在配置文件中的 INSTALLED_APPS 中添加模型所在的应用名称
字段类型
模型中的每个字段都是 Field 类相应的实例, django 根据 Field 类型来确定以下信息:
列类型, 告知数据库要存储那种数据
渲染表单时使用的默认 html widget
验证, 被用在 admin 和表单中
通用字段参数(常用)
null: 如果为 True,Django 将在数据库中将该字段存储为 NULL(如果该字段为空), 默认 False
blank: 如果为 True, 该字段允许为空值, 默认 False
注意, null 是数据库范畴, blank 是表单验证范畴
choices: 如果设置了该选项, 在渲染 HTML 时, 将会是一个下拉选择框. 该选项是一个二元组构成的可迭代对象, 选择框中的值就是二元组内的值
如:
- YEAR_IN_SCHOOL_CHOICES = (
- ('FR', 'Freshman'),
- ('SO', 'Sophomore'),
- ('JR', 'Junior'),
- ('SR', 'Senior'),
- ('GR', 'Graduate'),
- )
每个元组中的第一个元素是将被存储在数据库中值, 第二个元素由窗体小部件显示
给定一个模型, 可以使用 get_FOO_display()来访问字段在窗体中的显示值
如:
- 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: 字段的默认值, 可以是一个值也可以是一个对象, 如果该对象可调用, 那么每次创建一新模型对象时它都会被调用
注意, default 值是个可调用的对象时, 赋值一个对象的引用和调用该对象的区别
help_text: 表单部件额外的显示内容, 对生成文档也很有用
primary_key: 如果为 true, 该字段就是模型的主键, 如果没有指定该选项, 会默认生成一个 IntergerField 的自增 ID 字段作为主键字段
注意: 主键字段时只读的, 如果在一个已经存在的对象上面更改主键的值并保存, 一个新的对象将会被创建
unique: 如果为 true, 则该字段的值必须唯一
字段别名
除 ForeignKey ManyToManyField OneToOneField 之外, 每个字段都接受一个可选的位置参数(第一个参数), 若没有提供该参数, 将根据字段名称, 将字段名称下划线替换成空格作为别名
ForeignKey ManyToManyField OneToOneField 第一个参数是关联的模型类, 使用关键字参数 verbose_name 指定别名
关系
多对一: 如一个汽车厂生产多种汽车, 一辆汽车只有一个生产厂家
代码如下:
- from django.db import models
- class Manufacturer(models.Model):
- # ...
- pass
- class Car(models.Model):
- manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
多对多: 一个披萨上可以放多种配料, 一种配料可以放在多个披萨上
代码如下:
- from django.db import models
- class Topping(models.Model):
- # ...
- pass
- class Pizza(models.Model):
- # ...
- toppings = models.ManyToManyField(Topping)
一般来说 ManyToManyField 应该放在要在表单中被编辑的对象(如在 admin 中该字段会被渲染成多选框), 如本例: 一个披萨选择多种配料
多对多关系的额外字段: throuth
例如: 这样一个应用, 它记录音乐家所属的音乐小组. 我们可以用一个 https://yiyibooks.cn/__trs__/xx/Django_1.11.6/ref/models/fields.html#django.db.models.ManyToManyField 表示小组和成员之间的多对多关系. 但是, 有时你可能想知道更多成员关系的细节, 比如成员是何时加入小组的.
对于这些情况, Django 允许你指定一个中介模型来定义多对多关系. 你可以将其他字段放在中介模型里面. 源模型的 https://yiyibooks.cn/__trs__/xx/Django_1.11.6/ref/models/fields.html#django.db.models.ManyToManyField 字段将使用 https://yiyibooks.cn/__trs__/xx/Django_1.11.6/ref/models/fields.html#django.db.models.ManyToManyField.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)
应用实例如下:
- >>> 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()创建关系:
- >>> # 下列语句都是无法工作的
- >>> beatles.members.add(john)
- >>> beatles.members.create(name="George Harrison")
>>> beatles.members.set([john, paul, ringo, george])
为什么不能这样做? 这是因为你不能只创建 Person 和 Group 之间的关联关系, 你还要指定 Membership 模型中所需要的所有信息; 而简单的 add,create 和赋值语句是做不到这一点的. 所以它们不能在使用中介模型的多对多关系中使用. 此时, 唯一的办法就是创建中介模型的实例.
remove 方法被禁用也是出于同样的原因. 例如, 如果通过中介模型定义的表没有在
源模型 (Group) 和目标模型(perseon)
上强制执行唯一性, 则 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()
通过中介模型建立的 m2m 关系和普通 m2m 关系, 在查询方面是相似的:
>>> 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]>
如果你需要访问一个成员的信息, 你可以直接获取 Membership 模型:
- >>> 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 对象上反向查询:
- >>> 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.'
一对一: 和其他关系一样, 当某个对象扩展自另一个对象时, 最常用的方式就是在这个对象的主键上添加一对一关系
模型属性
objects: 模型最重要的属性是 Manager. 它是 Django 模型进行数据库查询操作的接口, 并用于从数据库提取实例. 默认的名称为 objects. Manager 只能通过模型类访问, 而不能通过模型实例访问.
模型方法
可以在模型上定义自定义方法来给对象添加自定义底层功能. Manager 方法用于 "表范围" 的事务, 模型的方法应该着眼于特定的模型实例. 这是一个非常有价值的技术, 让业务逻辑位于同一个地方 - 模型中.
例如, 下面的模型具有一些自定义的方法:
- from django.db import models
- class Person(models.Model):
- first_name = models.CharField(max_length=50)
- last_name = models.CharField(max_length=50)
- birth_date = models.DateField()
- def baby_boomer_status(self):
- "Returns the person's baby-boomer status."
- import datetime
- if self.birth_date <datetime.date(1945, 8, 1):
- return "Pre-boomer"
- elif self.birth_date < datetime.date(1965, 1, 1):
- return "Baby boomer"
- else:
- return "Post-boomer"
覆盖预定义的模型方法
models.Model 中封装了对数据库的各种操作, 特别是, 你将要经常改变 save()和 delete() 的工作方式
覆盖内建模型方法的一个典型的使用场景是, 你想在保存一个对象时做一些其它事情:
- 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(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
- do_something_else()
你还可以阻止保存:
- from django.db import models
- class Blog(models.Model):
- name = models.CharField(max_length=100)
- tagline = models.TextField()
- def save(self, *args, **kwargs):
- if self.name == "Yoko Ono's blog":
- return # Yoko shall never have her own blog!
- else:
- super(Blog, self).save(*args, **kwargs) # Call the "real" save() method
注意: 批量操作中被覆盖的模型方法不会被调用
当使用 QuerySet 批量删除对象或由于级联删除时, 对象的 delete()方法不一定被调用. 为确保自定义的删除逻辑得到执行, 你可以使用 pre_delete 和 / 或 post_delete 信号.
不幸的是, 当批量 creating 或 updating 对象时没有变通方法, 因为不会调用 save(),pre_save 和 post_save
模型继承
在 Django 中有 3 种风格的继承:
通常, 你只想使用父类来持有一些信息, 你不想在每个子模型中都敲一遍. 这个父类永远不会单独使用, 所以你要使用抽象的基类
如果你继承一个已经存在的模型且想让每个模型具有它自己的数据库表, 那么应该使用多表继承.
最后, 如果你只是想改变一个模块 Python 级别的行为, 而不用修改模型的字段, 你可以使用代理模型.
抽象基类
需要在父类中编写一个 Meta 类, 设置 abstract=True, 要注意, 如果父类和子类有相同的字段名, 会出现错误(ps: 难道不是重写吗? 为毛会报错)
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'
注意: abstract 属性不会被继承
多表继承
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 中也是有效的, 只不过没有保存在数据库中的 Restaurant 表中. 所以下面两个语句都是可以运行的:
- >>> Place.objects.filter(name="Bob's Cafe")
- >>> Restaurant.objects.filter(name="Bob's Cafe")
Meta 和多表继承
在多表继承中, 子类继承父类的 Meta 类是没什么意义的. 所有的 Meta 选项已经对父类起了作用, 再次使用只会起反作用(这与使用抽象基类的情况正好相反, 因为抽象基类并没有属于它自己的内容).
代理模型
有时你可能只想更改 model 在 Python 层的行为实现. 比如: 更改默认的 manager , 或是添加一个新方法, 而这, 正是代理继承要做的: 为原始模型创建一个代理 . 你可以创建, 删除, 更新代理 model 的实例, 而且所有的数据都可以像使用原始 model 一样被保存. 不同之处在于: 你可以在代理 model 中改变默认的排序设置和默认的 manager , 更不会对原始 model 产生影响. 声明代理 model 和声明普通 model 没有什么不同. 设置 Meta 类中 proxy 的值为 True, 就完成了对代理 model 的声明.
举个例子, 假设你想给 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")
代理模型管理器: 如果你没有在代理模型中定义管理器, 代理模型会继承基类管理器, 如果代理模型中定义了管理器, 它就会变成默认管理器, 不过在父类定义的管理器仍然有效
如果你想在代理模型中添加新的管理器, 并非替换基类管理器, 可以这样, 创建一个含有新管理器的基类, 作为代理模型的基类放在后面:
- # Create an abstract class for the new manager.
- class ExtraManagers(models.Model):
- secondary = NewManager()
- class Meta:
- abstract = True
- class MyPerson(Person, ExtraManagers):
- class Meta:
- proxy = True
模型继承中隐藏的规则
普通的 python 类允许子类覆盖父类的任何属性, 在 Django 中, 模型字段不允许这样做, 如果非抽象基类有一个 A 字段, 那么不能在任何继承自该基类的类中创建 A 字段. 对于抽象基类没有这个限制, 抽象基类的子类中会被覆盖, 也可以通过设置 field_name=None 来删除字段
在包中组织模型
如果有多个模型文件, 可以使用一个名为 models 的 python 包来替换原有的 models.py 文件, 但是必须将模型文件导入到所在包的__init__.py 文件中:
- #myapp/models/__init__.py
- from .organic import Person
- from .synthetic import Robot
更多细节可参照 django 内置模块, 如 django.db.models
来源: http://www.bubuko.com/infodetail-2566320.html