我试图在Django中建模,但它看起来并不合适。 Book
和Movie
可以包含一个或多个Person
个,其中每个人都有Role
(例如"作者"或& #34;导演")关于Book
或Movie
。
我用这样的Generic relations做这件事:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=255)
class Role(models.Model):
person = models.ForeignKey('Person')
role_name = models.CharField(max_length=255)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Book(models.Model):
title = models.CharField(max_length=255)
roles = GenericRelation('Role', related_query_name='books')
class Movie(models.Model):
title = models.CharField(max_length=255)
roles = GenericRelation('Role', related_query_name='movies')
这似乎有效,但是当获得Book
Person
所有person = Person.objects.get(pk=1)
for role in person.role_set.all():
print(role.role_name)
for book in role.books.all():
print(book.title)
时,它似乎很奇怪:
for book in role.books.all()
vagrant up
感觉不对 - 就像角色可以有多本书一样。我想我希望在一个特定书籍/电影上工作的人之间有更直接的关系。
有更好的方法对此进行建模吗?
答案 0 :(得分:1)
如果是我,我会通过角色模型使用多对多的关系来尝试如下方法。 (见https://docs.djangoproject.com/en/1.10/topics/db/models/#s-extra-fields-on-many-to-many-relationships)
正如你在评论中提到的那样,麻烦来了,因为你有几个需要这种关系的模型(书和电影)。解决方案是使用BaseRole模型并为电影角色和书籍角色创建单独的模型。像这样:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=255)
class BaseRole(models.Model):
role_name = models.CharField(max_length=255)
person = models.ForeignKey('Person', on_delete=models.CASCADE)
class Meta:
abstract = True
class MovieRole(BaseRole):
movie = models.ForeignKey('Movie', on_delete=models.CASCADE)
class BookRole(BaseRole):
book = models.ForeignKey('Book', on_delete=models.CASCADE)
class Book(models.Model):
title = models.CharField(max_length=255)
roles = models.ManyToManyField(Person, through='BookRole')
class Movie(models.Model):
title = models.CharField(max_length=255)
roles = models.ManyToManyField(Person, through='MovieRole')
这样,当您想要过滤所有人工作的书籍时,您可以按照以下方式进行:
person = Person.objects.get(pk=1)
books = Book.objects.filter(roles=person).distinct()
distinct()
调用是必要的,因为一个人可以在书籍/电影上有多个角色(例如,他们可能是制片人和导演),所以如果没有它,过滤器将返回一个实例该书中每个人在电影/书中扮演的角色。
在回复您的评论时,或许更好的解决方案是使用Django Polymorphic(https://django-polymorphic.readthedocs.io/en/stable/quickstart.html)。
所以你要设置你的模型:
from django.db import models
from polymorphic.models import PolymorphicModel
class Person(models.Model):
name = models.CharField(max_length=255)
class Role(models.Model):
role_name = models.CharField(max_length=255)
person = models.ForeignKey('Person', on_delete=models.CASCADE)
media = models.ForeignKey('Media', on_delete=models.CASCADE)
class Media(PolymorphicModel):
title = models.CharField(max_length=255)
roles = models.ManyToManyField(Person, through='Role')
class Book(Media):
pass
class Movie(Media):
pass
通过这种方式,你仍然可以获得一个人所有的书籍:
person = Person.objects.get(pk=1)
books = Book.objects.filter(roles=person).distinct()
但你也可以通过以下方式获得所有媒体:
media = person.media_set.all()
是的,你是对的,拥有content_type和object_id毫无意义,所以我删除了这些字段。
答案 1 :(得分:1)
在这种情况下,Django的通用关系可能不是最佳选择。 相反,看起来某种形式的继承更合适。
Django使用关系数据库。关系数据库的功能,效率和可靠性在很大程度上取决于以下事实:表之间的关系由 designer (或开发人员,或其他人员)固定:只需查看模型定义,您知道表格之间的关系。 用户不能影响这一点。
另一方面,GenericForeignKey
通过允许用户与任何建立新的关系,从而打破了这个基本概念。 >数据库中的其他表。这些关系仅存在于数据中,而不存在于 schema 中。 设计器无法知道在任何时间点将存在哪些关系,模型定义也无法告诉我们表之间的关系。为此,我们需要查看数据库中的实际数据。不用说,这个clever trick使事情变得非常复杂,并且使关系完整性很难维护,即使不是不可能的。
仍然,在某些用例中,GenericForeignKey
的灵活性正是所需要的。 Django自己的TaggedItem示例很好地说明了这一点。
但是,据我所知,OP的情况不是 其中之一。
answer by @tdsymonds指向正确的方向,即使用某种来自Media
类的继承形式。但是,我相信使用django-polymorphic
会使事情复杂化,因为有several ways使用纯Django实现此目的。
尽管可能不是最有效的查询方法,但最简单的方法可能是使用Django的multi-table inheritance(又称class-table inheritance)。
在OP的示例中,它变为:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=255)
roles = models.ManyToManyField(to='Media', through='Role')
class Role(models.Model):
person = models.ForeignKey('Person', on_delete=models.CASCADE)
media = models.ForeignKey('Media', on_delete=models.CASCADE)
role_name = models.CharField(max_length=255)
class Media(models.Model):
title = models.CharField(max_length=255)
class Book(Media):
pass
class Movie(Media):
pass
请注意,Role
直接与ForeignKey
有Media
,可以使用Media
对象,Book
对象,{{ 1}}对象,或您决定添加的任何其他子类。子类可以具有其他属性。我们可以通过Movie
(基本的many-to-many relation)轻松获得Person
所扮演的所有角色的列表,而不管Media
的类型如何。
要查看管理员中的角色,请添加内联,如docs中所述:
person.roles.all()