Как использовать GenericRelation с Django Rest Framework?
Я хочу включить модель с GenericRelation
обратная ссылка в DRF
Документы указывают, что это должно быть легко (чуть выше: http://www.django-rest-framework.org/api-guide/relations/) - но я что-то упустил!
Обратите внимание, что обратные универсальные ключи, выраженные с использованием поля GenericRelation, могут быть сериализованы с использованием обычных типов полей реляционных данных, поскольку тип цели в отношении всегда известен.
Для получения дополнительной информации см. Документацию Django об общих отношениях.
мои модели:
class Voteable(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
direct_vote_count = models.IntegerField(default=0)
class Question(models.Model):
user = models.ForeignKey(UserExtra, related_name='questions_asked')
voteable = GenericRelation(Voteable)
question = models.CharField(max_length=200)
и мои сериализаторы:
class VoteableSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Voteable
fields = ('pk', 'id', 'url', 'direct_vote_count')
class QuestionSerializer(serializers.HyperlinkedModelSerializer):
#voteable = VoteableSerializer(read_only=True, many=False)
#voteable = serializers.PrimaryKeyRelatedField(many=False, read_only=True)
class Meta:
depth = 1
model = Question
fields = ('url', 'question', 'user', 'voteable')
Две закомментированные строки - мои попытки рассказать DRF, как сериализовать voteable
внутри Question
Первый дает мне
'GenericRelatedObjectManager' object has no attribute 'pk'
а второй
<django.contrib.contenttypes.fields.create_generic_related_manager.<locals>.GenericRelatedObjectManager object at 0x7f7f3756cf60> is not JSON serializable
Итак, ясно, что я что-то недопонимаю, любая идея, что?
3 ответа
Ну, у меня есть рабочее решение, хотя оно не похоже на правильное решение....
class VoteableSerializer(serializers.ModelSerializer):
class Meta:
model = Voteable
fields = ('pk', 'direct_vote_count')
class VoteableRelatedField(serializers.RelatedField):
def to_representation(self, value):
serializer = VoteableSerializer(value.get_queryset()[0])
return serializer.data
class QuestionSerializer(serializers.HyperlinkedModelSerializer):
#voteable = VoteableSerializer(read_only=True, many=False)
#voteable = serializers.PrimaryKeyRelatedField(many=False, read_only=True)
voteable = VoteableRelatedField(read_only=True)
class Meta:
depth = 1
model = Question
fields = ('url', 'question', 'user', 'voteable')
read_only_fields = ('voteable',)
- Удалить
url
отVoteableSerializer
- менять
VoteableSerializer
вModelSerializer
отHyperlinkedModelSerializer
- добавлять
VoteableRelatedField
и получить первый элемент из набора запросов (это, в частности, кажется неправильным)
Я не буду отмечать это как принятое еще, в надежде, что кто-то может просветить меня о том, как это должно быть сделано!
Альтернативная идея для решения, которое лучше подходит GenericRelation...
- Сделать голосуемую абстрактной моделью
- Измените класс голосования (не показан в этом вопросе), чтобы указать на что-либо с помощью GenericForeignKey.
Плюсы:
Это будет означать, что информация о голосовании всегда находится на соответствующем объекте, упрощая сортировку и запросы и избегая объединений.
Минусы:
Голоса заняли бы немного больше места
class Voteable(models.Model):
votes = GenericRelation(Vote)
direct_vote_count = models.IntegerField(default=0)
class Meta:
abstract = True
class Question(Voteable):
user = models.ForeignKey(UserExtra, related_name='questions_asked')
question = models.CharField(max_length=200)
class Vote(models.Model):
user = models.ForeignKey(UserExtra, related_name='questions_asked')
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
Возможно, более оптимальным, но менее СУХИМым было бы иметь отдельный класс "Голосование" для каждого типа объектов, который наследуется Voteable
class Voteable(models.Model):
direct_vote_count = models.IntegerField(default=0)
class Meta:
abstract = True
class Question(Voteable):
user = models.ForeignKey(UserExtra, related_name='questions_asked')
question = models.CharField(max_length=200)
class QuestionVote(models.Model):#This class also repeated for each item that can be voted on
user = models.ForeignKey(UserExtra, related_name='questions_asked')
parent = models.ForeignKey(Question, related_name='votes')
Пользовательское связанное поле сериализатора кажется ненужным для документа, имеющего эту сноску для GenericRelation:
Обратите внимание, что обратные общие ключи, выраженные с использованием поля GenericRelation, могут быть сериализованы с использованием обычных типов полей реляционных данных, поскольку тип цели в отношении всегда известен.
Приложение ниже. Также проверьте это расширение DRF3.
class QuestionSerializer(serializers.HyperlinkedModelSerializer):
voteable = VoteableSerializer(read_only=True)
class Meta:
model = Question
fields = ('url', 'question', 'user', 'voteable')
...