【问题标题】:DRF - queryset filter using contains field lookup on SlugRelatedFieldDRF - 在 SlugRelatedField 上使用包含字段查找的查询集过滤器
【发布时间】:2018-11-19 11:44:43
【问题描述】:

我正在努力弄清楚如何在 SlugRelatedField 上使用“field__contains”运行查询集过滤器。 我有一个简单的 Book 模型和一个 Tag 模型,如下所示:

class Book(models.Model):
  title = models.CharField(max_length=100)
  authors = models.ManyToManyField(Author)
  publisher = models.ForeignKey(Publisher)
  publication_date = models.DateField()

class MetaTag(models.Model):
  book = models.ManyToManyField('Book', related_name='meta_tags',
  help_text='The book this meta tag belongs to')
  value = models.CharField(max_length=400, unique=True, help_text='Meta tag value')

class BookSerializer(serializers.HyperlinkedModelSerializer):
  class BookHyperlink(serializers.HyperlinkedIdentityField):
    """A Hyperlink field for book details"""

      def get_url(self, obj, view_name, request, format):
        url_kwargs = {
            'pk': obj.id,
        }
        return reverse(view_name, kwargs=url_kwargs, request=request, format=format)
  url = BookHyperlink(view_name='book-detail')
  meta_tags = CreatableSlugRelatedField(many=True, slug_field='value', queryset=MetaTag.objects.all())

  class Meta:
    model = Book
    fields = (
        'id',
        'title',
        'publisher',
        'publication_date',
        'meta_tags',
        'url'
    )

class MetaTagSerializer(serializers.ModelSerializer):

  class Meta:
    model = MetaTag
    fields = ('id', 'book', 'value',)

class CreatableSlugRelatedField(serializers.SlugRelatedField):

  def to_internal_value(self, data):
    try:
        return self.get_queryset().get_or_create(**{self.slug_field: data})[0]
    except ObjectDoesNotExist:
        self.fail('does_not_exist', slug_name=self.slug_field, value=smart_text(data))
    except (TypeError, ValueError):
        self.fail('invalid')

  class Meta:
    model = MetaTag
    fields = ('id', 'book', 'value', )

现在在我的 BooksView 中,我希望能够按 meta_tags 值过滤查询集。我已尝试使用“__contains”字段查找进行以下操作:

class Books(viewsets.ModelViewSet):
  """Default view for Book."""

  queryset = Book.objects.all()
  serializer_class = BookSerializer
  permission_classes = (IsAuthenticated, )

  filter_backends = (DjangoFilterBackend,)
  filter_fields = tuple(f.name for f in Book._meta.get_fields())

  def get_queryset(self):

    search_pattern = self.request.query_params.get('search', None)
    if search_pattern is not None and search_pattern is not '':
        self.queryset = self.queryset.filter(meta_tags__contains = search_pattern)
    return self.queryset

def get_object(self):
    if self.kwargs.get('pk'):
        return Book.objects.get(pk=self.kwargs.get('pk'))

但我从 django 收到以下错误:

文件“~MyProject/venv/lib/python3.6/site-packages/django/db/models/sql/query.py”,第 1076 行,在 build_lookup raise FieldError('相关字段查找无效:​​{}'.format(lookup_name)) django.core.exceptions.FieldError:相关字段查找无效:​​包含

据我了解,这意味着由于“meta_tags”不是常规数组或文本字段,因此无法在该字段上应用包含字段查找。

如果是这样,在这种情况下过滤查询集以获取 meta_tags 值的最佳方法是什么?

【问题讨论】:

    标签: django-models django-rest-framework django-serializer drf-queryset


    【解决方案1】:

    我就这个问题咨询过一位 django 专家,建议在与外部模型一起使用时尝试将“slug_field”(在本例中为“__value”)附加到“__contains”字段查找。

    它没有在任何地方记录,甚至没有在 https://docs.djangoproject.com/en/2.0/ref/contrib/postgres/fields/#contains 的 django 官方文档中记录,所以我无法知道它是这样工作的,但这个解决方案确实有效:

    queryset = queryset.filter(meta_tags__value__contains=search_pattern)
    

    当您深入研究 MetaTag 模型时,这实际上是有道理的,因为“值”是 meta_tags 模型的内部字段:

    class MetaTag(models.Model):
    
        book = models.ManyToManyField('Book', related_name='meta_tags',
                                     help_text='The book this meta tag belongs to')
        value = models.CharField(max_length=400, unique=True, help_text='Meta tag value')
    
        def __str__(self):
            return '%s > %s' % (self.channel, self.value)
    

    首先附加 __value 不是那么明显的原因是因为 meta_tags 数组(对象数组)是使用 SlugRelatedField 序列化程序展平的,其中只有 slug_field 被投影,其余字段被省略。 所以 meta_tags 数组的最终输出是平的:

    meta_tags: ['tag1','tag2']
    

    代替:

    meta_tags: [{book: 'a', value: 'tag1'},{book: 'a', value: 'tag2'}]
    

    但由于 django DRF 上的序列化是在后期(在查询集完成后)进行的,因此应考虑原始字段架构。

    希望有一天这会减轻某人的头痛。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-19
      • 1970-01-01
      • 1970-01-01
      • 2021-09-05
      • 2022-09-27
      • 2013-03-22
      • 1970-01-01
      • 2018-12-23
      相关资源
      最近更新 更多