You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

API配套数据库设计合理性咨询:附需求与Django实现代码

数据库模型设计检查:API开发需求对应实现是否遗漏?

需求回顾

  • 城市区域(City district):包含name、id字段
  • 分类(Category):包含name、id字段
  • 组织网络(Organization Network):包含name、id字段
  • 组织(Organization):
    • 隶属于单个组织网络
    • 包含id、name、description字段
    • 可同时关联多个区域
    • 拥有带价格的产品列表
  • 产品(Product):
    • 包含id、name、category字段
    • 可在组织网络内的多个组织售卖
    • 不同组织对应售价可能不同

你提供的Django模型代码

from django.db import models


class District(models.Model):
    name = models.CharField(max_length=100, unique=True)


class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)


class OrganizationNetwork(models.Model):
    name = models.CharField(max_length=100, unique=True)


class Organization(models.Model):
    name = models.CharField(max_length=100, unique=True)
    description = models.TextField()
    organization_network = models.ForeignKey(
        OrganizationNetwork,
        on_delete=models.CASCADE,
        related_name="organizations",
    )
    district = models.ManyToManyField(
        "District",
        through="DistrictOrganization",
        through_fields=("organization", "district"),
        related_name="organizations",
    )
    products = models.ManyToManyField(
        "Product",
        through="OrganizationProduct",
        through_fields=("organization", "product"),
        related_name="organizations",
    )


class Product(models.Model):
    name = models.CharField(max_length=100, unique=True)
    category = models.ForeignKey(
        Category, on_delete=models.CASCADE, related_name="products"
    )


class DistrictOrganization(models.Model):
    district = models.ForeignKey(District, on_delete=models.CASCADE)
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE)


class OrganizationProduct(models.Model):
    price = models.DecimalField(max_digits=10, decimal_places=2)
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)

设计遗漏与优化建议

1. 关联表的联合唯一约束缺失

目前DistrictOrganization和OrganizationProduct没有设置联合唯一约束,会出现以下问题:

  • 同一个组织可能重复关联同一个区域
  • 同一个产品在同一个组织下被添加多次(导致同一产品同一组织出现多份价格记录,不符合“不同组织对应售价可能不同”的需求逻辑,同一组织同一产品应该只有一个售价)

修复方案:给这两个模型添加联合唯一约束(Django 2.2+推荐使用UniqueConstraint):

# 修改DistrictOrganization
class DistrictOrganization(models.Model):
    district = models.ForeignKey(District, on_delete=models.CASCADE)
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['district', 'organization'], name='unique_district_org')
        ]

# 修改OrganizationProduct
class OrganizationProduct(models.Model):
    price = models.DecimalField(max_digits=10, decimal_places=2)
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['product', 'organization'], name='unique_product_org')
        ]

2. 产品与组织网络的关联限制缺失

需求明确产品“可在组织网络内的多个组织售卖”,但当前模型没有限制产品只能被同一组织网络内的组织售卖——也就是说现在的设计允许一个产品被不同组织网络的组织上架,这可能违背业务规则。

可选修复方案:

  • 方案一:在OrganizationProduct中添加organization_network字段,通过约束确保该字段与关联组织的organization_network一致:
    class OrganizationProduct(models.Model):
        price = models.DecimalField(max_digits=10, decimal_places=2)
        organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
        product = models.ForeignKey(Product, on_delete=models.CASCADE)
        organization_network = models.ForeignKey(OrganizationNetwork, on_delete=models.CASCADE)
    
        class Meta:
            constraints = [
                models.UniqueConstraint(fields=['product', 'organization'], name='unique_product_org'),
                models.CheckConstraint(check=models.Q(organization__organization_network=F('organization_network')), name='org_network_match')
            ]
    
  • 方案二:通过Django信号(如pre_save)或业务逻辑层在保存OrganizationProduct时,验证组织的网络归属,不符合则抛出异常。

3. 字段可选性优化

Organization的description字段默认blank=False,如果业务允许组织没有描述信息,建议设置blank=True:

description = models.TextField(blank=True)

4. 命名规范优化(非功能遗漏,仅可读性提升)

Organization中的district字段名建议改为districts(因为是多对多关联,复数更直观),同时保持related_name="organizations"不变,这样反向查询district.organizations和正向查询organization.districts的语义更清晰。


内容的提问来源于stack exchange,提问作者karambaq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 19:20:59