背景
我们在写前后端分离的项目的过程中一般使用的 DRF (Django Rest Framework) 在使用generics 进行接口的的开发的时候我们是不用进行sql的书写了,我们需要做的是对数据库表结构进行设计,合理的利用数据库表提高数据库的查询性能。
数据库表设计原则
数据库设计要同时满足以下三范式
第一范式
表的字段不可以在拆分
每行都要有主键
第二范式
每行数据必须完全依赖于主键,并不是部分依赖于组件
第三范式
每行的数据依赖是不传递的只依赖主键
合理使用关系
一对一(OneToOneField)两个模型有严格的一对一关系的时候使用
多对一 (ForeignKey) 常见的关联关系
多对多 (ManyToManyField) 需要中间表的情况
模型设计实践
1. 基础模型设计
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
2. 关系设计
多对一关系(ForeignKey)
class Category(models.Model):
name = models.CharField(max_length=50)
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
多对多关系(ManyToManyField)
class Tag(models.Model):
name = models.CharField(max_length=50)
class Article(models.Model):
tags = models.ManyToManyField(Tag, related_name='articles')
自定义中间表
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)
class Group(models.Model):
members = models.ManyToManyField(Person, through='Membership')
3. 继承策略
抽象基类
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Product(BaseModel):
name = models.CharField(max_length=100)
多表继承
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)
优化建议
-
索引优化
class Customer(models.Model): email = models.EmailField(db_index=True) name = models.CharField(max_length=100, index=True) -
使用 select_related 和 prefetch_related
select_related用于 ForeignKey 和 OneToOneFieldprefetch_related用于 ManyToManyField 和反向 ForeignKey
-
合理使用 choices
class Order(models.Model): STATUS_CHOICES = [ ('P', 'Pending'), ('C', 'Completed'), ('F', 'Failed'), ] status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='P') -
考虑使用 django-model-utils
- 提供 TimeStampedModel、StatusModel 等有用基类
常见问题解决方案
-
软删除模式
class SoftDeleteModel(models.Model): is_deleted = models.BooleanField(default=False) def delete(self, using=None, keep_parents=False): self.is_deleted = True self.save() class Meta: abstract = True -
多租户设计
class Tenant(models.Model): name = models.CharField(max_length=100) class TenantAwareModel(models.Model): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE) class Meta: abstract = True -
树形结构
from mptt.models import MPTTModel, TreeForeignKey class Category(MPTTModel): name = models.CharField(max_length=50) parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
通过遵循这些原则和实践,您可以设计出结构合理、易于维护且性能良好的 Django 模型。