2015-06-04 2 views
2

У меня есть две таблицы под названием Post и Reply. Пользователи могут создать только один Response для каждого Post. Модели выглядят следующим образом:Добавить булевское поле, если строка существует в другой таблице?

class Post(models.Model): 
    name = models.CharField(max_length = 100) 

class Reply(models.Model): 
    post = models.ForeignKey(Post, related_name = 'replies') 

Теперь у меня есть мнение, возвращающий сообщения, как это:

class PostList(generics.ListAPIView): 
    permission_classes = (permissions.IsAuthenticated,) 
    queryset = Post.objects.all() 
    serializer_class = PostSerializer 

И сериализатор для сообщений:

class PostSerializer(serializers.ModelSerializer): 
    class Meta: 
     fields = ('id', 'name') 

Результаты из этой точки зрения выглядят следующим образом:

[ 
    { 
     "id": "1", 
     "name": "The first post" 
    }, 
    { 
     "id": "2", 
     "name": "The second post" 
    } 
] 

Теперь, к настоящей проблеме: я хотел бы иметь логическое поле в результатах, которое было бы true, если пользователь ответил на сообщение, и false, если они этого не сделали. В принципе, результат для ситуации, когда текущий пользователь ответил на первый пост, но не второй пост будет выглядеть следующим образом:

[ 
    { 
     "id": "1", 
     "name": "The first post", 
     "replied": "true" 
    }, 
    { 
     "id": "2", 
     "name": "The second post", 
     "replied": "false" 
    } 
] 

Как этого добиться? У меня есть подозрение, что это как-то должно быть реализовано в сериализаторе, но я не знаю, как это сделать.

Заранее благодарим за вашу помощь!

+2

Первой идее заключается в добавлении пользовательского сериализатора поля метод в вашем сериализатороме (http://www.django-rest-framework.org/api-guide/ fields/# serializermethodfield), но имейте в виду, что у вас должен быть текущий пользовательский объект в вашем классе serializer (например, вы можете переопределить конструктор сериализатора для его передачи). – dydek

ответ

1

Отказ от ответа дайдека здесь.

Вы перезаписать get_queryset в Апи View

class PostList(generics.ListAPIView): 
    ... 
    def get_queryset(self): 
     Post.objects.all().extra(select={ 
     'current_user_replies_count': 'SELECT COUNT(*) FROM <reply table> WHERE' + 
     'post_id=posts_post.id AND owner_id = %s' 
            },select_params=(request.user.id,)) 

Это добавит «» current_user_replies_count как свойство объектов Post в вашем QuerySet.

+0

Работает как очарование, спасибо! – manabreak

+0

Вы можете объяснить 'post_id = posts_post.id' отношение?Thanx –

1

Следующая проблема здесь для многих запросов sql, когда вы будете получать, например. 100 объектов (у вас будет BASE_QUERIES_COUNT + len (объекты)). Из корней мы не хотим, чтобы число запросов на линейные sql составляло, и этой ситуации никогда не должно происходить, особенно в производственной версии. Идеальное решение здесь было бы, если бы мы смогли получить все данные в один запрос sql. Это возможно с переопределением метода get_queryset (здесь https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/generics.py#L47). И тогда запрос может выглядеть так, как показано ниже, параметр current_user_replies_count доступен как обычная переменная экземпляра модели.

# all only for example 
Post.objects.all().extra(select={ 
     'current_user_replies_count': 'SELECT COUNT(*) FROM <reply table> WHERE '+ 
             'post_id=posts_post.id AND owner_id = %s' 
    }, 
    select_params=(request.user.id,) 
) 

Я не проверял, так что используйте его больше, как, например, вместо готового раствора, и вы должны бросить current_user_replies_count к BOOL.

+0

У меня возникли проблемы с пониманием того, как именно я должен реализовать это в своем коде. – manabreak

1

Вы можете добавить пользовательские поля к PostSerializer

class PostSerializer(serializers.ModelSerializer): 
    replied = serializers.SerializerMethodField('has_replies') 

    def has_replies(post): 
     return post.replies.filter(owner=self.context["request"].user).exists() 

    class Meta: 
     fields = ('id', 'name', 'replied') 
+0

Проблема заключается в том, ответил ли 'request.user' на сообщение, а не на пользователя – crhodes

+0

. Я вижу, вы можете получить доступ к' запросу' внутри сериализатора через 'self.context [" request "]'. Контекст задается методом ListAPIView.get_serializer_context'. Предполагая, что объект 'Reply' имеет поле под названием' owner', вы можете использовать ответ выше. –

+0

Не вызывает ли запрос дополнительного запроса в 'has_replies()' один раз в строке в исходном запросе? – manabreak

Смежные вопросы