每个模型都是一个 Python 的类,继承 django.db.models.Model
模型类的每个属性都相当于一个数据库的字段
Django 提供了一个自动生成访问数据库的 API
1.使用模型
(1)setting.py的INSTALLED_APPS配置models所在的应用
(2)运行命令
manage.py makemigrations
manage.py migrate
2.字段
模型中每一个字段都应该是某个 Field 类的实例, Django 利用这些字段类来实现以下功能:
- 字段类型用以指定数据库数据类型(如:INTEGER, VARCHAR, TEXT)
- 在渲染表单字段时默认使用的 HTML 视图
- 基本的有效性验证功能,用于 Django 后台和自动生成的表单。
2.1.字段类型
(1)AutoField 自动递增
(2)BooleanField true/false
(3)CharField 字符串
字段选项:max_length
(4)DateField 日期
auto_now=True 每次保存对象时,自动将该字段设置为现在。对于“最后修改”的时间戳很有用。
auto_now_add=True 当第一次创建对象时,自动将该字段设置为现在。对创建时间戳很有用。
(5)DateTimeField 日期时间
(6)EmailField
(7)ImageField
(8)IntegerField
2.2.通用字段选项
(1)null
如果设置为 True,当该字段为空时,Django 会将数据库中该字段设置为 NULL。默认为 False 。
(2)blank
如果设置为 True,该字段允许为空。默认为 False。
该选项与 null 不同, null 选项仅仅是数据库层面的设置,而 blank 是涉及表单验证方面。如果一个字段设置为 blank=True ,在进行表单验证时,接收的数据该字段值允许为空,而设置为 blank=False 时,不允许为空。
(3)choices
一系列二元组,用作此字段的选项。如果提供了二元组,默认表单小部件是一个选择框,而不是标准文本字段,并将限制给出的选项。
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()
print(p.shirt_size) # "L"
print(p.get_shirt_size_display()) # "Large"
(4)default
默认值
(5)primary_key
如果设置为 True ,将该字段设置为该模型的主键。
如果没有对任何一个字段设置 primary_key=True 选项。 Django 会自动添加一个 IntegerField 字段,并设置为主键。
(6)unique
如果设置为 True,这个字段的值必须在整个表中保持唯一。
3.关联关系
3.1.多对一
ForeignKey
3.2.多对多
ManyToManyField
3.3.一对一
OneToOneField
4.Meta类
使用内部 Meta类 来给模型赋予元数据,模型的元数据即“所有不是字段的东西”,
比如排序选项( ordering ),数据库表名( db_table ),权限(permissions)
5.模型方法
模型方法应该在某个对象实例上生效。在模型中添加自定义方法会给对象提供自定义的“行级”操作能力,与之对应的是类 Manager 的方法意在提供“表级”的操作。
5.1.重写已定义的模型方法
5.2.执行自定义SQL
6.模型继承
6.1.抽象基类
(1)概念
抽象基类在要将公共信息放入很多模型时会很有用。编写基类,并在 Meta 类中填入abstract=True,该模型将不会创建任何数据表。当其用作其它模型类的基类时,它的字段会自动添加至子类。
从抽象基类继承来的字段可被其它字段或值重写,或用 None 删除。
(2)Meta 继承
抽象基类的子类不会自动地变成抽象类。为了继承一个抽象基类创建另一个抽象基类,需要在子类上显式地设置 abstract=True。
子类的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"
抽象基类的某些 Meta 属性对子类是没用的。比如, db_table
如果子类从多个抽象基类继承,则默认情况下仅继承第一个列出的类的 Meta 选项。为了从多个抽象类中继承 Meta 选项,必须显式地声明 Meta 继承。
(3)related_name 和 related_query_name
若在 外键 或 多对多字段 使用了 related_name 或 related_query_name,必须为该字段提供一个 独一无二 的反向名字和查询名字。这在抽象基类中一般会引发问题,因为基类中的字段都被子类继承,且保持了同样的值(包括 related_name 和 related_query_name)。
为了解决此问题,当在抽象基类中(也只能是在抽象基类中)使用 related_name 和 related_query_name,部分值需要包含 '%(app_label)s' 和 '%(class)s'。
- '%(class)s' 用使用了该字段的子类的小写类名替换。
- '%(app_label)s' 用小写的包含子类的应用名替换。每个安装的应用名必须是唯一的,应用内的每个模型类名也必须是唯一的。因此,替换后的名字也是唯一的。
6.2.多表继承
(1)概念
每个模型都是一个单独的模型,都指向分离的数据表,且可被独立查询和创建。
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)
Restaurant 中自动创建的连接至 Place 的 OneToOneField 看起来像这样:
place_ptr = models.OneToOneField( Place, on_delete=models.CASCADE, parent_link=True, primary_key=True, )
(2)Meta
多表继承情况下,子类不会继承父类的 Meta,但有几个属性除外:若子类未指定 ordering 属性或 get_latest_by 属性,子类会从父类继承这些。
(3)继承与反向关系
如果使用的名字是 ForeignKey 和 ManyToManyField 关系的默认值,在继承父类模型的子类中添加了这些关联,必须 指定 related_name 属性。假如忘了,Django 会抛出一个合法性错误。
class Supplier(Place):
customers = models.ManyToManyField(Place, related_name='provider')
(4)指定父类连接字段
Django 会自动创建一个 OneToOneField ,将子类连接回非抽象的父类。如果你想修改连接回父类的属性名,你可以自己创建 OneToOneField,并设置 parent_link=True,表明该属性用于连接回父类。
6.3.代理模型
(1)概念
为原模型创建一个 代理。可以创建,删除和更新代理模型的实例,所以的数据都会存储的像使用原模型(未代理的)一样。不同点是可以修改代理默认的模型排序和默认管理器,而不需要修改原模型。
代理模型就像普通模型一样申明。需要告诉 Django 这是一个代理模型,通过将 Meta 类的 proxy 属性设置为 True。
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
(2)基类约束
一个代理模型只能继承自一个非抽象模型类,不能继承多个非抽象模型类,因为代理模型无法在不同数据表之间提供任何行间连接
一个代理模型可以继承任意数量的抽象模型类
一个代理模型也可以继承任意数量的代理模型,只需他们共享同一个非抽象父类。
7.操作数据库
7.1.创建/保存对象
(1)创建对象
b = Blog(name="Beatles Blog", tagline="All the latest Beatles news.")
(2)保存对象
Django 在显式调用 save() 才操作数据库。
(3)创建并保存对象
b3 = Blog.objects.create(name="Blog_3", tagline="All the latest Beatles news.")
(4)保存ForeignKey字段
(5)保存 ManyToManyField字段
7.2.QuerySet
要从数据库查询对象,要通过模型类的 Manager 构建一个 QuerySet。要从数据库查询对象,要通过模型类的 Manager 构建一个 QuerySet在 SQL 的层面上, QuerySet 对应 SELECT 语句,而*filters*对应类似 WHERE 或 LIMIT 的限制子句。
(1)查询全部
all_entries = Entry.objects.all()
(2)过滤器
filter(**kwargs)返回一个新的 QuerySet,包含的对象满足给定查询参数。
exclude(**kwargs)返回一个新的 QuerySet,包含的对象 不 满足给定查询参数。
链式过滤:
Entry.objects.filter(headline__startswith="What").exclude(pub_date__gte=datetime.date.today()).filter(pub_date__gte=datetime.date(2005, 1, 30))
(3)get()查询单个对象
one_entry = Entry.objects.get(pk=1)
如果没有满足查询条件的结果, get() 会抛出一个 DoesNotExist 异常;有不止一个记录满足 get() 查询条件时发出警告。会抛出 MultipleObjectsReturned
(4)跨模型
跨模型使用关联字段名,字段名由双下划线分割,直到拿到想要的字段。
(5)跨多值关联
每个记录需同时满足
blog_1 = Blog.objects.filter(entry__headline__contains="Lennon", entry__pub_date__year=2023)
相同的记录,满足一个即可
blog_2 = Blog.objects.filter(entry__headline__contains="Lennon").filter(entry__pub_date__year=2023)
例如:
blog_1返回空,blog_2返回一条记录。
7.3.执行SQL
(1)原生查询
raw()
(2)直接执行自定义 SQL
对象 django.db.connection 代表默认数据库连接。要使用这个数据库连接,调用 connection.cursor() 来获取一个指针对象。然后,调用 cursor.execute(sql, [params]) 来执行该 SQL 和 cursor.fetchone(),或 cursor.fetchall() 获取结果数据。