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
)

相关说明:

  1. 表名myapp_person由Django根据应用名称和模型类名自动生成,可以通过修改元信息覆盖这种行为
  2. id是Django默认生成的主键,可以通过设置primary key覆盖这种行为

使用模型

当创建完模型后,需要进行如下设置,告诉Django需要使用这些模型。

  1. 编辑INSTALLED_APPS

在其中添加包含已经创建模型文件的应用名称。

# 例如
INSTALLED_APPS = [
"myapp",
]
  1. 执行数据库同步
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会根据字段类的类型做如下事情:

  1. 和数据库中字段的数据类型所对应
  2. 渲染成不同的网页表单字段
  3. 表单验证条件

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()方法获取相应的显示字段内容。
具有三种赋值方式:

  1. 元素为二元元组的列表形式
    元组中第一个元素存储在数据库中,第二个元素是表单渲染时的显示形式
YEAR_IN_SCHOOL_CHOICES = [
('FR', 'Freshman'),
('SO', 'Sophomore')
]
  1. 字典形式
    字典的键为存储在数据库中的值,字典的值为表单渲染时的显示内容
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)
  1. 枚举类形式
    利用枚举类型的形式作为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自动根据字段名提供,将下划线变空格

特殊选项

针对某些特殊的字段有一些特定的选项:

  1. 日期相关类
  • auto_now

字段每次保存时,更新为最新的时间

  • auto_now_add

记录创建时该字段的值为当前的时间

  1. DecimalField类
  • max_digits
    设定最大的数字位数
  • decimal_places
    设定小数点后的数字位数

关系

Django中存在三种表示表和表之间关系的字段,分别是ForeignKey, ManyToManyFieldOneToOneField

一对多关系

利用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类似。

字段名限制

  1. 模型类的属性名不能是Python的关键字 pass
  2. 模型类的属性名不能包含连续两个下划线 foo__bar
  3. 模型类的属性名不能以下划线结尾 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}"

通常模型类需要重新定义一下两个方法:

  1. str(): 模型类实例的可读化表现
  2. 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)
  1. 子类中将继承抽象类中的字段
  2. 如果子类中不需要抽象类中的字段,则设置为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
)
  1. 在子类中可以重定义父类中的Meta属性,但是不能重定义父类中的字段属性。
  2. 当子类中存在与父类的关联关系时,需要明确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中导入各个模型。

posted @ 2024-06-14 15:04  Python习者  阅读(61)  评论(0)    收藏  举报