在Django中建模复杂关系
我正在用Django开发一个网络服务,需要处理一种非常复杂的关系,但我一直找不到合适的解决办法。
想象一下有三个基本模型,我们叫它们网站(Site)、分类(Category)和项目(Item)。每个网站可以包含一个或多个分类,但它们之间的关系有两种可能的方式:一种是“公共”分类,这种分类是多对多的关系,也就是说它们是预先定义好的,每个网站可以关联零个或多个分类,反之亦然。另一种分类是为每个网站单独定义的,这种分类只属于那个网站,不属于其他网站;也就是说,它们是多对一的关系,因为每个网站可以有多个这样的分类。
在内部,这两种分类完全相同,唯一的区别在于它们与网站的关系。不过,我可以把它们分成两个不同的模型(可能有一个共同的父模型),但这只能解决我问题的一半:项目模型与分类是多对一的关系,也就是说每个项目只属于一个分类,理想情况下,它不应该关心它与网站的关系。
另一种解决方案是让这两种不同类型的网站-分类关系共存(也就是说,在同一个分类模型上同时有外键和多对多字段),但这个方案感觉像是打开了一个新的麻烦盒子。
有没有人知道有没有第三种更好的解决办法来应对这个死胡同?
3 个回答
注意:我了解对象关系映射、Rails和Python,但对Django不太熟悉。
我看到还有两个额外的选项:
- 从数据库的角度来看,我可以让处理多对多关系的表多加一个字段,这个字段用来表示是“通用”关系还是“站点”关系,并且添加一些限制来限制“站点”关系的类型。我觉得在Django中可以做到这一点,具体可以参考“多对多关系中的额外字段”这部分。
如果你使用的是早期版本的Django,你仍然可以通过将多对多表做成一个明确的模型来实现这一点。
从对象的角度来看,我可以考虑把类别分成三个类:
BaseCategory(基础类别)
CommonCategory(通用类别,继承自BaseCategory)
SiteCategory(站点类别,继承自BaseCategory)
然后可以使用Django的继承模型之一。
作为一种替代方案,你可以使用Django的内容类型(通用关系)来连接这些项目。使用这种方法的一个好处是,它可以让你根据将来的数据需求,以不同的方式使用分类模型。
你可以通过编写模型方法来简化使用网站分类的过程,这样就能更方便地提取和排序分类。Django的管理后台也支持通用关系的嵌套使用。
你的模型可以这样设计:
Site(models.Model):
label = models.CharField(max_length=255)
Category(models.Model):
site = models.ManyToManyField(Site)
label = models.CharField(max_length=255)
SiteCategory(models.Model):
site = models.ForeignKey(Site)
label = models.CharField(max_length=255)
Item(models.Model):
label = models.CharField(max_length=255)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
如果想更深入了解内容类型以及如何查询通用关系,可以查看这里:http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
为什么不把两种类型的分类放在一个模型里,这样你只需要三个模型就行了呢?
Site
Category
Sites = models.ManyToManyField(Site)
IsCommon = models.BooleanField()
Item
Category = models.ForeignKey(Category)
你说“这两种分类在内部是完全相同的”。听起来这样是可行的。请注意,一个多对多字段(ManyToManyField)只包含一个值也是完全可以的,所以你不需要在同一个分类模型里同时使用“外键(ForeignKey)和多对多字段”,这听起来会很麻烦。只需在多对多字段里放一个值就可以了。