最好的方法来指定现有的ManyToMany"通过表"关系?

时间:2017-11-18 13:32:28

标签: django django-models

我有这些模型用于库的数据库:

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模型中添加一个布尔字段,以指定哪一个是主要模型,并通过表单验证来控制选择一本书中的一个而且只选择其中一个。有效,但也许有更优雅的解决方案。)

2 个答案:

答案 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