Как использовать 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')
        ...
Другие вопросы по тегам