我正在尝试在Django中建模,但这似乎并不合适.书和电影可以包含一个或多个人,每个人在该书或电影上都有一个角色(如“作者”或“导演”).
我正在用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')
这似乎有用,但在获得一个人工作的所有书籍时似乎很奇怪:
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)
role.books.all()中的for book感觉不对 – 就像角色可以拥有多本书一样.我想我希望在一个特定的书/电影上工作的人之间有更直接的关系.
有没有更好的方法来模拟这个?
最佳答案 如果是我,我会通过角色模型尝试使用多对多关系的方法. (见
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()调用是必要的,因为一个人可以在一本书/电影上拥有多个角色(例如,他们可以是制片人和导演),所以没有它,过滤器会为每个人返回一本书的实例.该人在电影/书中的角色.
UPDATE
在回答您的评论时,或许更好的解决方案是使用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是没有意义的,所以我删除了这些字段.