过滤具有多对多关系的对象,检查它是不是包含列表中的至少一个元素

Posted

技术标签:

【中文标题】过滤具有多对多关系的对象,检查它是不是包含列表中的至少一个元素【英文标题】:Filter an object with a many to many relation, checking if it contains at least one of the elements in a list过滤具有多对多关系的对象,检查它是否包含列表中的至少一个元素 【发布时间】:2019-04-08 17:18:20 【问题描述】:

我们正在制作一款用于销售二手商品的应用。在这个应用程序内部,我们可以选择根据一系列标准过滤产品:用户的地理位置和最大距离、最低价格、最高价格、最低分数和标签列表。

我的主要问题是过滤产品与标签的多对多关系。我们要做的是获取所有包含至少一个用户选择的标签的产品。

这是过滤功能的代码:

def FilterProduct(request, format=None):
    if request.method != 'POST':
        return Response(status=status.HTTP_400_BAD_REQUEST)
    token = request.POST.get('token', 'nothing')
    if token == 'nothing':
        return Response(status=status.HTTP_400_BAD_REQUEST)
    else:
        try:
            tags = request.POST.get('tags', '')
            tags_pk = []
            for tag_search in tags:
                tag = Tag.objects.filter(nombre=tag_search)
                if tag != None:
                    tags_pk.append(tag.pk)

            user_latitude = request.POST.get('latitud', '')
            user_longitude = request.POST.get('longitud', '')
            max_distance = request.POST.get('distancia_maxima', '')
            min_price = request.POST.get('precio_minimo', '')
            max_price = request.POST.get('precio_maximo', '')
            min_score = request.POST.get('calificacion_minima', '')
            if tags == '' or user_latitude == '' or user_longitude == '' or max_distance == '' or min_price == '' or max_price == '' or min_score == '':
                return Response(status=status.HTTP_400_BAD_REQUEST)
            products = Productos.objects.filter(precio__lte=max_price, precio__gte=min_price, vendido_por__media_valoraciones__gte=min_score, tiene_tags__in=tags_pk)

由于某种原因,这不会返回至少包含主键列表中一项的所有产品。

这是models.py:

class Producto(models.Model):
    vendido_por = models.ForeignKey(
        to=Usuario,
        null=False,
        on_delete=models.CASCADE,
        verbose_name='Usuario que ha puesto a la venta el producto',
        related_name='producto_del_usuario')
    latitud = models.DecimalField(
        verbose_name='Latitud del producto',
        max_digits=9,
        decimal_places=6)
    longitud = models.DecimalField(
        verbose_name='Longitud del producto',
        max_digits=9,
        decimal_places=6)
    nombre = models.CharField(
        max_length=50,
        verbose_name='Nombre del producto')
    precio = models.CharField(
        max_length=10,
        verbose_name='Precio del producto')
    estado_producto = models.CharField(
        max_length=50,
        choices=[(tag.name, tag.value) for tag in EleccionEstadoProducto],
        verbose_name='Estado en el que se encuentra el producto: Nuevo, Semi-nuevo, etc')
    estado_venta = models.CharField(
        max_length=50,
        choices=[(tag.name, tag.value) for tag in EleccionEstadoVenta],
        verbose_name='Estado en el que se encuentra la venta')
    num_acciones = models.IntegerField(
        default=0,
        verbose_name='Marca si uno o los dos usuarios han confirmado el estado de venta')
    tipo_envio = models.CharField(
        max_length=50,
        verbose_name='Si el usuario que ha colgado el producto esta dispuestos a enviar a domicilio o no')
    descripcion = models.CharField(
        max_length=1000,
        verbose_name='Descripcion asociada al producto')
    tiene_tags = models.ManyToManyField(
        Tag,
        blank=True,
        editable=True,
        related_name='tiene_tags')
    le_gusta_a = models.ManyToManyField(
        Usuario,
        blank=True,
        editable=True,
        related_name='le_gusta_a')
    num_likes = models.IntegerField(
        default=0,
        verbose_name='Likes acumulados por el producto')

class Tag(models.Model):
    nombre = models.CharField(
        max_length=50,
        verbose_name='Nombre del tag')
    es_predeterminado = models.BooleanField(
        default=False,
        verbose_name='Marca si un tag ha sido creado por los administradores de la aplicacion')

是否有一种实际的方法来过滤在请求中发送的列表中至少包含一个标签的所有产品?

提前致谢!

【问题讨论】:

【参考方案1】:

我认为您可以简化标签过滤器的发生方式,例如

tags = request.POST.get('tags', '')
tag_queryset = Tag.objects.filter(nombre__in=tags)

然后您可以将查询集直接放入您的产品过滤器中,例如

products = Productos.objects.filter(precio__lte=max_price, precio__gte=min_price, vendido_por__media_valoraciones__gte=min_score, tiene_tags__in=tag_queryset)

希望有帮助

【讨论】:

以上是关于过滤具有多对多关系的对象,检查它是不是包含列表中的至少一个元素的主要内容,如果未能解决你的问题,请参考以下文章

在 Django Admin 中过滤多对多框

将对象添加到具有多对多关系的核心数据中的 NSSet

从具有多对多关系 django 的两个表中过滤数据

检查 ID 是不是包含在 JPA 的列表中

查询显示多对多关系中的关联列表

Django如何过滤多对多字段中的对象,而不是原始查询集