Django - 模型
Django - 模型
Section titled “Django - 模型”模型 (model) 在 Django 中是关于你数据的权威信息源。它是一个 Python 类,代表数据库中的一张表(在关系型数据库中)或一个集合(在 NoSQL 数据库如 MongoDB 中)。模型类的每个属性都映射到数据库字段(列)。
模型通常在应用的 models.py 文件中定义(例如,myapp/models.py)。Django 使用这些模型定义来:
- 通过其迁移系统 (migration system) 自动生成数据库模式(例如,
CREATE TABLE语句)。 - 提供一个高级 Python API(ORM - 对象关系映射器 (Object-Relational Mapper)),用于创建、检索、更新和删除 (CRUD) 数据库记录。
让我们定义一个简单的模型,代表虚构目录中的公司。
from django.db import models
class Company(models.Model): # 字段定义数据库表中的列 name = models.CharField(max_length=100, unique=True, help_text="Name of the company") website = models.URLField(max_length=200, blank=True, help_text="Company website URL (optional)") email = models.EmailField(max_length=100, help_text="Contact email address") phone_number = models.CharField(max_length=20, blank=True, help_text="Contact phone number (optional)") founded_date = models.DateField(null=True, blank=True, help_text="Date the company was founded") is_active = models.BooleanField(default=True, help_text="Is the company currently active?") # Django 自动添加的字段: # id = models.AutoField(primary_key=True) # 默认主键
class Meta: db_table = "companies" # 可选:明确指定数据库表名 verbose_name = "Company" # 可选:人类可读的单数名称 verbose_name_plural = "Companies" # 可选:人类可读的复数名称 ordering = ['name'] # 可选:查询时的默认排序
def __str__(self): # 用于 admin 和 shell 中的字符串表示 return self.name要点:
- 每个模型都是一个继承自
django.db.models.Model的 Python 类。 - 每个属性代表一个数据库字段,使用
models.Field类型定义(例如,CharField,URLField,IntegerField,DateField,BooleanField,ForeignKey等)。 - 常见的字段选项包括
max_length,unique,blank(与验证相关,允许表单中的空值),null(与数据库相关,允许数据库中的NULL),default,help_text。 - 除非你在另一个字段上指定
primary_key=True,否则 Django 会自动添加一个名为id的自增主键字段。 - 内部的
Meta类是可选的,允许你配置模型元数据,如数据库表名 (db_table)、默认排序 (ordering) 以及人类可读的名称 (verbose_name,verbose_name_plural)。 __str__()方法定义了对象的人类可读表示,这在 Django admin 和 shell 中非常有用。
你可以在 Django 文档中找到模型字段和选项的完整列表:https://docs.djangoproject.com/en/stable/ref/models/fields/
激活模型:迁移
Section titled “激活模型:迁移”定义模型类并不会自动创建相应的数据库表。你需要使用 Django 的迁移系统 (migration system):
-
确保你的应用在
INSTALLED_APPS中: 在项目settings.py的INSTALLED_APPS中添加'myapp'或'myapp.apps.MyappConfig'。 -
创建迁移: 这个命令会分析
models.py中的更改(与之前的迁移进行比较),并在myapp/migrations/目录中创建迁移文件。
python manage.py makemigrations myapp- 应用迁移: 这个命令会读取迁移文件,并将更改应用到数据库模式(例如,创建
companies表)。
python manage.py migrate每当你对模型进行更改时(添加/删除字段、更改字段类型、修改选项等),都需要运行 makemigrations 和 migrate。
操作数据 (CRUD 操作)
Section titled “操作数据 (CRUD 操作)”Django 提供了一个强大的数据库抽象 API (the ORM) 来与你的模型进行交互。你通过附加到模型类上的管理器 (Manager)(通常称为 objects)来访问这个 API。
让我们看看使用 Django shell (python manage.py shell) 或在视图 (view) 中进行 CRUD 操作的示例:
# 假设你运行了 'python manage.py shell'from myapp.models import Companyimport datetime
# === 创建 ===# 方法 1:分两步创建并保存c1 = Company(name="TechCorp", email="contact@techcorp.com", website="https://techcorp.com")c1.save() # 执行 INSERT SQL 查询
# 方法 2:使用管理器一步创建并保存c2 = Company.objects.create( name="Innovate Ltd", email="info@innovate.com", phone_number="123-456-7890", founded_date=datetime.date(2020, 1, 1))
print(f"Created company: {c1}")print(f"Created company: {c2}")
# === 检索 / 读取 ===# 获取所有公司all_companies = Company.objects.all() # 返回一个 QuerySet (惰性查询)print(f"\nAll Companies ({all_companies.count()}):")for company in all_companies: print(f"- {company.name} (ID: {company.id})")
# 通过主键 (id) 获取特定公司try: techcorp = Company.objects.get(pk=c1.id) # 或者 get(id=c1.id) print(f"\nFound by PK: {techcorp.name}")except Company.DoesNotExist: print(f"\nCompany with pk={c1.id} not found.")
# 使用其他字段查找(返回一个对象,或抛出 DoesNotExist / MultipleObjectsReturned 异常)try: innovate = Company.objects.get(name="Innovate Ltd") print(f"Found by name: {innovate.name}")except Company.DoesNotExist: print("\nInnovate Ltd not found.")
# 过滤以获取多个公司(返回一个 QuerySet)active_companies = Company.objects.filter(is_active=True)print(f"\nActive companies ({active_companies.count()}):")for company in active_companies: print(f"- {company.name}")
# 更复杂的查找 (field__lookuptype)recent_companies = Company.objects.filter(founded_date__year__gte=2020) # 成立于 2020 年或之后print(f"\nRecent companies ({recent_companies.count()}): {list(recent_companies)}")
# 排除公司non_tech_companies = Company.objects.exclude(name__icontains="tech") # 不区分大小写的包含print(f"\nNon-tech companies: {list(non_tech_companies)}")
# 排序结果companies_by_name_desc = Company.objects.order_by('-name') # 降序print(f"\nOrdered by name DESC: {list(companies_by_name_desc)}")
# === 更新 ===# 获取对象,修改属性,然后保存try: company_to_update = Company.objects.get(name="TechCorp") company_to_update.website = "https://new-techcorp.com" company_to_update.is_active = False company_to_update.save() # 执行 UPDATE SQL 查询 print(f"\nUpdated TechCorp: Website='{company_to_update.website}', Active={company_to_update.is_active}")except Company.DoesNotExist: print("\nTechCorp not found for update.")
# 一次更新多个对象(更高效,绕过 save() 方法)num_updated = Company.objects.filter(founded_date__isnull=True).update(is_active=False)print(f"\nSet {num_updated} companies with no founding date to inactive.")
# === 删除 ===# 获取对象并删除try: company_to_delete = Company.objects.get(name="Innovate Ltd") delete_result = company_to_delete.delete() # 执行 DELETE SQL 查询 print(f"\nDeleted Innovate Ltd. Result: {delete_result}")except Company.DoesNotExist: print("\nInnovate Ltd not found for deletion.")
# 一次删除多个对象(更高效)delete_result_multi = Company.objects.filter(is_active=False).delete()print(f"\nDeleted inactive companies. Result: {delete_result_multi}")模型可以相互关联,以表示数据之间的关系:
-
ForeignKey(多对一): 最常见的类型。将一个模型实例链接到另一个。示例:一个Employee(员工)模型可以有一个指向Company(公司)模型的ForeignKey(多个员工属于一个公司)。 -
OneToOneField(一对一): 类似于ForeignKey,但确保关系一方的每个实例最多链接到另一方的一个实例。示例:一个UserProfile(用户个人资料)模型可以有一个指向 Django 内置User(用户)模型的OneToOneField。 -
ManyToManyField(多对多): 将一个模型的实例链接到另一个模型的多个实例(反之亦然)。示例:一个Post(帖子)模型和一个Tag(标签)模型可以有一个ManyToManyField(一篇帖子可以有多个标签,一个标签可以应用于多篇帖子)。Django 会为此自动创建一个中间数据库表。
使用 ForeignKey 的示例:
from django.db import models
# 假设上面定义的 Company 模型已存在
class Employee(models.Model): name = models.CharField(max_length=100) email = models.EmailField(unique=True) # 链接到 Company (多个员工 -> 一个公司) company = models.ForeignKey( Company, on_delete=models.CASCADE, # 当引用的 Company 被删除时会发生什么? related_name='employees' # 如何从 Company 回溯引用到 Employee ) start_date = models.DateField()
class Meta: ordering = ['company', 'name']
def __str__(self): return f"{self.name} ({self.company.name})"
# --- 定义/修改模型后,运行:---# python manage.py makemigrations myapp# python manage.py migrate
# --- 使用示例(在 shell 或视图中)---# from myapp.models import Company, Employee
# # 假设公司 'c1' 已存在 (TechCorp)# c1 = Company.objects.get(name='TechCorp')
# # 为 TechCorp 创建员工# emp1 = Employee.objects.create(name='Alice', email='alice@techcorp.com', company=c1, start_date='2022-08-15')# emp2 = Employee.objects.create(name='Bob', email='bob@techcorp.com', company=c1, start_date='2023-01-10')
# # 从员工访问关联的公司# print(f"{emp1.name}'s company: {emp1.company.name}")
# # 访问公司关联的所有员工(使用 related_name)# print(f"Employees at {c1.name}:")# for emp in c1.employees.all(): # 使用 related_name 'employees'# print(f"- {emp.name}")
# # 根据公司属性过滤员工(双下划线查找)# techcorp_employees = Employee.objects.filter(company__name='TechCorp')# print(f"Query for TechCorp employees: {list(techcorp_employees)}")重要的 ForeignKey 参数:
on_delete: 指定当引用的对象(即Company)被删除时会发生什么。常见选择:models.CASCADE(也删除员工),models.PROTECT(如果存在员工则阻止删除),models.SET_NULL(将员工的公司字段设为NULL,需要字段设置null=True),models.SET_DEFAULT(设置为默认值,需要字段设置default)。related_name: 从目标模型访问关联对象集合时使用的名称(例如,company_instance.employees.all())。如果省略,Django 将使用modelname_set(例如,employee_set)。
模型是 Django 开发的基石,为你的数据提供了结构,并提供了强大的交互 API。
延伸阅读: