Фильтрация результатов запроса модели на основе идентификаторов связанных идентификаторов ManyToMany другой модели - PullRequest
0 голосов
/ 28 января 2020

У меня есть 2 модели

class Tag(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name


class Question(models.Model):
    ques_id = models.IntegerField(default=0)
    name = models.CharField(max_length=255)
    Tag_name = models.ManyToManyField(Tag)

    class Meta:
        ordering = ['ques_id']

    def __str__(self):
        return self.name

searlizers.py

class TagSerializers(serializers.ModelSerializer):

    class Meta:
        model = Tag
        fields = '__all__'

class QuestionSerializers(serializers.ModelSerializer):
    class Meta:
        model = Question
        fields = '__all__'

Это мой класс поисковиков. Я хочу получить ответ, подобный

{
  "id": 1,
  "name": "QUES 1",
  "tags": [{
     "id": 1,
     "name": "Abcd" 
  }]
} 

, каким будет запрос, чтобы получить вопросы Fetch 10, учитывая некоторые идентификаторы входных тегов, например, Tag_id = 1 или 2 или 3.

Ответы [ 3 ]

0 голосов
/ 28 января 2020

Первый : я бы посоветовал вам провести рефакторинг вашей Question модели, поскольку она имеет ques_id, и я думаю, что она считается дубликатом (поскольку Django уже создает поле id по умолчанию)

Затем Вам нужно изменить имя ManyToManyField на tags и makemigrations, затем migrate

class Question(models.Model):
    name = models.CharField(max_length=255)
    tags = models.ManyToManyField(Tag)

    class Meta:
        ordering = ['id']

    def __str__(self):
        return self.name
# run make migrations
python manage.py makemigrations <<<YOUR QUESTIONS APP NAME>>>
# It will prompt you to check if you change the many to many relationship say yes
Did you rename question.Tag_name to question.tags (a ManyToManyField)? [y/N] y
# Then Run migrate
python manage.py migrate

Второй : обновите QuestionSerializers, чтобы поле тегов сериализовало отношение

class QuestionSerializers(serializers.ModelSerializer):
    tags = TagSerializers(many=True)

    class Meta:
        model = Question
        fields = '__all__'

Таким образом, вы сделаете свой код чище. И вы хороши для go.


Ответ обновлен (фильтрация и разбиение на страницы)

Теперь, если вы хотите отфильтровать вопросы на основе предоставленных идентификаторов тегов.

Вам нужно использовать PageNumberPagination для просмотра и фильтрации DjangoFilterBackend.

Я рекомендую вам сделать их настройками по умолчанию для DRF.

Убедитесь, что вы установите django-filter.

# In your settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    ),
    'PAGE_SIZE': 10
}

Теперь создайте свой собственный фильтр

# Inside filters.py
import re

import django_filters

from questions.models import Question


class QuestionsFilterSet(django_filters.FilterSet):
    tags = django_filters.CharFilter(field_name='tags__id', method='tags_ids_in')

    def tags_ids_in(self, qs, name, value):
        ids_list = list(map(lambda id: int(id), filter(lambda x: x.isdigit(), re.split(r'\s*,\s*', value))))
        return qs.filter(**{f"{name}__in": ids_list}).distinct()

    class Meta:
        model = Question
        fields = ('tags',)

Теперь используйте ListAPIView, чтобы составить список своих вопросов

class QuestionsListAPIView(ListAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializers
    filter_class = QuestionsFilterSet

Теперь, если вы нажмете

http://<PATH_TO_VIEW>?tags=1,2,3

, вы получите все Вопросы с тегом id 1, 2 или 3. Все они будут разбиты на 10 страниц за раз.

0 голосов
/ 29 января 2020

Вам просто нужно написать поле tags, подобное этому, в вашем QuestionSerializers

class QuestionSerializers(serializers.ModelSerializer):
    tags = TagSerializers(source='Tag_name', many=True)

    class Meta:
        model = Question
        fields = ('id', 'name', 'tags')

Это даст вам такой ответ

{
  "id": 1,
  "name": "QUES 1",
  "tags": [{
     "id": 1,
     "name": "Abcd" 
  }]
} 

Ваш запрос на выборку база тегов будет выглядеть так:

Question.objects.filter(Tag_name__in=[1, 2, 4])
0 голосов
/ 28 января 2020

Вам необходимо добавить поле tags в качестве еще одного сериализатора данных к вашему QuestionSerializer.

Ваш код QuestionSerializer должен выглядеть следующим образом:

class QuestionSerializers(serializers.ModelSerializer):
    Tag_name = TagSerializer(many=True)

    class Meta:
        model = Question
        fields = '__all__'

Если вы хотите точно tags имя в ответе, вы можете указать SerializerMethodField вот так:

class QuestionSerializer(serializers.ModelSerializer):
    tags = serializers.SerializerMethodField()

    get_tags(self, instance):
        return TagSerializer(instance.Tag_name, many=True).data

    class Meta:
        model = Question
        fields = ('ques_id', 'name', 'tags')
...