Models
模型就是关于数据的映射,通常一个模型对应一张数据库表。
Models
一个模型包含存储数据的字段和数据行为。
基本情况:
- 每个模型都是django.db.models.Model类的子类
- 每个模型的属性代表一个数据表的字段
- Django提供了获取数据API
快速案例
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
这个Person模型就类似代表在数据库中存在如下一张表
CREATE TABLE myapp_person(
"id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
)
相关说明:
- 表名myapp_person由Django根据应用名称和模型类名自动生成,可以通过修改元信息覆盖这种行为
- id是Django默认生成的主键,可以通过设置primary key覆盖这种行为
使用模型
当创建完模型后,需要进行如下设置,告诉Django需要使用这些模型。
- 编辑INSTALLED_APPS
在其中添加包含已经创建模型文件的应用名称。
# 例如
INSTALLED_APPS = [
"myapp",
]
- 执行数据库同步
python manage.py makemigrations
python manage.py migrate
字段
字段是模型中最重要的一个部分,代表数据库中每个数据表的列信息。在模型中字段用类的属性表示,需要注意的是字段名称不能和模型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类的实例对象。Django会根据字段类的类型做如下事情:
- 和数据库中字段的数据类型所对应
- 渲染成不同的网页表单字段
- 表单验证条件
Django提供了大量的内置字段种类,例如:
- 自增整型类:AutoField, BigAutoField, SmallAutoField
- 整型类:BigIntegerField, IntegerField, SmallIntegerField, PositiveBigIntegerField, PositiveIntegerField, PositiveSmallIntegerField
- 浮点型类:FloatField, DecimalField
- 字符类: CharField
- 文本类:TextField
- 日期时间相关类:TimeField, DateField, DateTimeField, DurationField
- 布尔类:BooleanField
- 特殊类:EmailField, ImageField, JSONField, FileField, FilePathField, GenericIPAddressField, SlugField, UUIDField, URLField
字段选项
每种字段类型都具有一些特定的选项参数,用来设定字段的限制。
通用选项
通用的字段选项如下:
- null
默认为False,如果设置为True,表示该字段允许NULL - blank
默认为False,如果设置为True,表示该字段允许空白。该字段设置为False时,表示字段必填 - choices
设定该字段的可选内容。表单渲染成选项。
通过模型的实例对象的get_**_display()方法获取相应的显示字段内容。
具有三种赋值方式:
- 元素为二元元组的列表形式
元组中第一个元素存储在数据库中,第二个元素是表单渲染时的显示形式
YEAR_IN_SCHOOL_CHOICES = [
('FR', 'Freshman'),
('SO', 'Sophomore')
]
- 字典形式
字典的键为存储在数据库中的值,字典的值为表单渲染时的显示内容
class Persion(models.Model):
SHIRT_SIZE = {
"S": "Small",
"M": "Medium",
"L": "Large"
}
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZE)
- 枚举类形式
利用枚举类型的形式作为choices选项的值。
from django.db import models
class Runner(models.Model):
MedalType = models.TextChoices("MedalType", "GOLD SILVER BRONZE")
# (GOLD,Gold),(SILVER, Silver), (BRONZE, Bronze)
name = models.CharField(max_length=60)
medal = models.CharField(blank=True, choices=MedalType, max_length=10)
-
default
设置字段的默认值 -
help_text
帮助文档,表单渲染时有用;对形成文档有帮助。 -
primary_key
为True时,设定该字段为主键。 同时会覆盖默认的id为主键的行为。
主键是只读的字段,更改其内容时会创建一个新记录 -
unique
为True时,表示该字段的内容不可重复 -
db_index
为True时,设定该字段为数据库索引 -
verbose_name
设定字段可读的名称。不提供时Django自动根据字段名提供,将下划线变空格
特殊选项
针对某些特殊的字段有一些特定的选项:
- 日期相关类
- auto_now
字段每次保存时,更新为最新的时间
- auto_now_add
记录创建时该字段的值为当前的时间
- DecimalField类
- max_digits
设定最大的数字位数 - decimal_places
设定小数点后的数字位数
关系
Django中存在三种表示表和表之间关系的字段,分别是ForeignKey, ManyToManyField和OneToOneField。
一对多关系
利用django.db.models.ForeignKey来定义一个一对多关系。
使用时需要指定一个相关的模型类。可以是类名,也可以是类名的字符串
from django.db import models
class Manufacturer(models.Model):
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete = models.CASCADE)
一个表可以关联自身。
推荐将外键的字段名设置为关联模型类的小写名字。
多对多关系
利用django.db.models.ManyToManyField来定义一个多对多关系。
使用时需要指定一个相关的模型类。可以是类名,也可以是类名的字符串。
from django.db import models
class Topping(models.Model):
pass
class Pizza(models.Model):
toppings = models.ManyToManyField(Topping,on_delete=models.CASCADE)
一个表可以和自身具有多对多的关系。
推荐将多对多关系关联模型类的小写名字的复数作为字段名。
两个模型存在多对多关系时,在哪个模型中设定多对多关系,不要紧。通常设定在被编辑的类中。
多对多关系的额外字段
当处理多对多关系的时候还存在其他的字段信息时,需要通过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)
一对一关系
Django利用OneToOneField来设定一对一关系。
使用方法和ForeignKey类似。
字段名限制
- 模型类的属性名不能是Python的关键字
pass - 模型类的属性名不能包含连续两个下划线
foo__bar - 模型类的属性名不能以下划线结尾
foo_
Meta选项
通过自定义模型类的内部类Meta可以设定非字段相关的设定,例如排序,表名等
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
模型属性
模型存在objects属性,代表一个Manager。它是Django模型和数据库操作的接口。如果没有自定义Manager,则默认为objects。
只能通过模型类名访问,不能通过模型类的实例对象访问。
自定义Manager
from django.db import models
class Person(models.Model):
people = models.Manager()
这种情况下Person的属性就由默认的Person.objects变成了Person.people
模型方法
在模型类中自定义方法,这些方法的作用范围为行级。而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):
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'
@property
def full_name(self):
return f"{self.first_name} {self.last_name}"
通常模型类需要重新定义一下两个方法:
- str(): 模型类实例的可读化表现
- get_absolute_url(): 计算模型类实例的URL。用在Django的admin应用中。
覆盖默认的数据库操作行为
可以通过覆盖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().save(*args, **kwargs) ## 必须执行此步,来保证基本的数据保存操作功能
do_something_else()
需要注意的是批量操作时,覆盖的方法不会启作用
模型继承
抽象模型类
通过抽象模型类来保存通用的模型字段。且不会在数据库中创建相应的数据表。
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)
- 子类中将继承抽象类中的字段
- 如果子类中不需要抽象类中的字段,则设置为None
Meta继承
当子类继承抽象类时,同样Meta内部类也会继承。但是子类的Meta不会设置abstract为True。如果需要在子类中扩展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"
Meta多继承
当子类存在多继承时,Meta默认只继承第一个父类的Meta,如果要继承多个父类的Meta,必须在子类的Meta中明确继承的父类Meta。
继承中处理related_name和related_query_name字段
当在ForeignKey或ManyToManyField中使用related_name和related_query_name时,Django要求其具有唯一的名称。
当抽象类中具有这两个字段参数时应特殊处理。
class Base(models.Model):
m2m = models.ManyToManyField(
OtherModel,
related_name = "%(app_label)s_%(class)s_related",
related_query_name = "%(app_label)s_%(class)ss"
)
class Meta:
abstract = True
当不指定related_name时,Django默认设置为类名后加'_set'表示。
多表继承
这个就是从一般的表中继承,这样会在表之间创建一个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)
这种继承情况下类似于Django做了如下的操作:
place_ptr = models.OneToOneField(
Place,
on_delete = models.CASCADE,
parent_link=True, # 覆盖默认的取回父类的名称
primary_key=True
)
- 在子类中可以重定义父类中的Meta属性,但是不能重定义父类中的字段属性。
- 当子类中存在与父类的关联关系时,需要明确related_name,否则会与OneToOneField冲突
代理模型
代理模型的作用就是当使用代理模型的增删改查操作时,数据同样会作用到源数据模型上。不同之处在于通过代理模型可以具有默认的排序,管理器,而不用修改源模型。
from django.db import models
class Person(models.Model):
frist_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
class OrderedPerson(Person):
class Meta:
proxy = True
ordering = ['first_name']
利用代理模型来呈现数据,而源数据没有改变。
模型集成
当一个应用中存在多个模型时,将其都写在一个models.py文件中不利于管理。此时就需要创建一个models包。
并在__init__.py中导入各个模型。

浙公网安备 33010602011771号