我有这些模型用于库的数据库:
class Author(models.Model):
...
class Task(models.Model):
... # detailed explanation of how the author collaborated in the book
class BookTask(models.Model):
book = models.ForeignKey(Book)
author = models.ForeignKey(Author)
task = models.ForeignKey(Task)
class Book(models.Model):
authors = models.ManyToManyField(Author, through='BookTask'...)
那里的一切都很好,但我想指定一个现有的BookTask关系作为主要关系。想想一本书中有3位作者参与过的书。我想将所有3本书分配给书籍,然后将其中1本作为主要书籍。
我试过这个:
class Book(models.Model):
authors = models.ManyToManyField(Author, through='BookTask'...)
author_main = models.ForeignKey(BookTask...)
但是生成的管理网页没有显示author_main
字段的预期选择小部件。有什么想法吗?
(注意:我目前的解决方案是在BookTask
模型中添加一个布尔字段,以指定哪一个是主要模型,并通过表单验证来控制选择一本书中的一个而且只选择其中一个。有效,但也许有更优雅的解决方案。)
答案 0 :(得分:1)
一方面是作者/书籍与另一方面是任务之间的关系?如果是这样,从数据库管理的角度来看,添加BooleanField的解决方案是最佳的。您在那儿拥有的其他任何外键(例如author_main)都会弄乱数据库模式。
如果这种关系是一对多的任务(一位作者可以在同一本书上完成许多任务),我会这样做:
class AuthorBook(models.Model):
book = models.ForeignKey(Book)
author = models.ForeignKey(Author)
primary_author = models.BooleanField()
class AuthorBookTask(models.Model):
author_book = models.ForeignKey(AuthorBook)
task = models.ForeignKey(Task)
这样,您可以先指定作者身份,然后将任务映射到该作者身份。
无论上述内容如何,您还需要一些规则来指定每本书只能有一位主要作者。以下SQL查询应返回小于或等于1的结果:
select sum(primary_author)
from AuthorBook
group by book
希望这对您有帮助...
答案 1 :(得分:1)
您的解决方案在大多数方面都足够“优雅”。可能让您质疑解决方案的部分是必须在管理员级别验证关系数据,不是吗?
在这种情况下,您是正确的。有一种简单的方法可以将其卸载到数据层并在其中执行一致性,而不是在管理员验证级别。
使用您的示例,您可以像这样强制实施数据约束:
class BookTask(models.Model):
book = models.ForeignKey(Book)
author = models.ForeignKey(Author)
task = models.ForeignKey(Task)
main = models.BooleanField(default=False)
def save(self, *args, **kwargs):
if self.main:
self.__class__._default_manager.filter(book=self.book, main=True).update(main=False)
super(BookTask, self).save(*args, **kwargs)
这基本上将为该BookTask
的所有其他Book
上的“ main”切换。这将有效地在数据库中为每个BookTask
保留一个“主” Book
。