Django-filter: Filtrar en múltiples campos con condición OR

Creado en 13 oct. 2019  ·  8Comentarios  ·  Fuente: carltongibson/django-filter

Hola,
¿Cómo es posible filtrar en múltiples campos con la condición OR?
Por ejemplo:
first_name = foo OR last_name = bar
Equivalente a:
model.objects.filter(Q(first_name=foo) | Q(last_name=bar))

Comentario más útil

Hice algo como lo siguiente, no estoy seguro de que funcione para todos los casos, pero resolví mi problema:

class BaseFilter(django_filters.FilterSet):
    def OR(self, queryset, field_name, value):
        if not hasattr(self, "groups"):
            setattr(self, "groups", {})

        self.groups[field_name] = value
        return queryset

    <strong i="6">@property</strong>
    def qs(self):
        base_queryset = super().qs

        if not hasattr(self, "groups"):
            return base_queryset

        query = Q()
        for key, value in self.groups.items():
            query |= Q(**{key: value})

        return base_queryset.filter(query)
class PlanFilter(BaseFilter):
    double_visit__or = django_filters.UUIDFilter("double_visit", method="OR")
    visitor__or = django_filters.UUIDFilter("visitor", method="OR")
    class Meta:
        model = models.Plan
        fields = {
            "id": ["exact"],
            "date": ["exact", "gte", "lte",],
            "visitor": ["exact"],
            "doctor": ["exact"],
            "double_visit": ["exact"]
        }

Todos 8 comentarios

Gracias por abrir esto, esta es exactamente la misma pregunta que tengo.

Actualmente, recibo la siguiente advertencia:

/usr/local/lib/python3.7/site-packages/django_filters/rest_framework/backends.py:128: 
UserWarning: <class 'project.api.views.FooBarViewSet'> is not compatible with schema generation

Y tengo un FilterSet y ViewSet que se ve así:

# models
class FooBarUserAssignment(models.Model):
    user = models.ForeignKey("auth.User", on_delete=models.CASCADE)
    foobar = models.ForeignKey("FooBar", on_delete=models.CASCADE)

    class Meta:
        unique_together = (
            ("user", "foobar"),
        )


class FooBarGroupAssignment(models.Model):
    group = models.ForeignKey("auth.Group", on_delete=models.CASCADE)
    foobar = models.ForeignKey("FooBar", on_delete=models.CASCADE)

    class Meta:
        unique_together = (
            ("group", "foobar"),
        )

class FooBar(models.Model):
    title = models.CharField(max_length=160, unique=True)

    users = models.ManyToManyField(
        to="auth.User",
        through=FooBarUserAssignment,
    )
    groups = models.ManyToManyField(
        to="auth.Group",
        through=FooBarGroupAssignment,
    )

    def __str__(self):
        return self.title

# filters
from django_filters import rest_framework as rest_framework_filters

class FooBarFilter(rest_framework_filters.FilterSet):
    title = rest_framework_filters.CharFilter(field_name="title", lookup_expr="icontains")

    class Meta:
        model = FooBar
        fields = ("title", )

# viewsets
class FooBarViewSet(ModelViewSet):
    queryset = Foobar.objects.order_by("-title")
    serializer_class = FooBarSerializer
    filterset_class = FooBarFilter

    def get_queryset(self):
        queryset = self.queryset

        q_name = Q()
        rel_name = self.request.query_params.get("rel_name", None)
        if rel_name:
            q_name = Q(users__name=rel_name)

        q_groups = Q()
        rel_groups = self.request.query_params.get("rel_groups", "").split(",")
        if any(rel_groups):
            q_groups = Q(groups__name__in=rel_groups)

        qs = queryset.filter(q_name | q_groups).distinct()
        return qs

¿Cómo podría crear un FilterSet para ejecutar exactamente la misma consulta?
Pude implementar parte de FilterSet, pero aún no sé cuál es la mejor manera de mover la lógica OR de ViewSet.get_queryset() a FilterSet

import django_filters
from django_filters import rest_framework as rest_framework_filters

class CharInFilter(django_filters.BaseInFilter, rest_framework_filters.CharFilter):
    pass

class FooBarFilter(rest_framework_filters.FilterSet):
    title = rest_framework_filters.CharFilter(field_name="title", lookup_expr="icontains")
    rel_name = rest_framework_filters.CharFilter(field_name="users__name", lookup_expr="exact")
    rel_groups = CharInFilter(field_name="groups__name")

    class Meta:
        model = FooBar
        fields = ("title", )

    def filter_queryset(self, queryset):
        qs = super().filter_queryset(queryset)
        return qs.distinct()

Parece que esto es parte de esta extensión de django-filter que se puede instalar junto con django-filter. https://github.com/philipn/django-rest-framework-filters#complex -operaciones

Hola a todos. He creado #1167, que debería proporcionar validación y filtrado a nivel de grupo. Para el OP, el uso se vería así:

class UserFilter(FilterSet):
    class Meta:
        model = User
        field = ['username', 'first_name', 'last_name']
        groups = [
            CombinedGroup(filters=['first_name', 'last_name'], combine=operator.or_),
        ]

Cualquier comentario sobre las relaciones públicas sería muy apreciado.

Sigue siendo el mismo problema aquí, ¿hay alguna solución ya?

¡@JeromeK13 no parece nada! tengo la misma pregunta

Hice algo como lo siguiente, no estoy seguro de que funcione para todos los casos, pero resolví mi problema:

class BaseFilter(django_filters.FilterSet):
    def OR(self, queryset, field_name, value):
        if not hasattr(self, "groups"):
            setattr(self, "groups", {})

        self.groups[field_name] = value
        return queryset

    <strong i="6">@property</strong>
    def qs(self):
        base_queryset = super().qs

        if not hasattr(self, "groups"):
            return base_queryset

        query = Q()
        for key, value in self.groups.items():
            query |= Q(**{key: value})

        return base_queryset.filter(query)
class PlanFilter(BaseFilter):
    double_visit__or = django_filters.UUIDFilter("double_visit", method="OR")
    visitor__or = django_filters.UUIDFilter("visitor", method="OR")
    class Meta:
        model = models.Plan
        fields = {
            "id": ["exact"],
            "date": ["exact", "gte", "lte",],
            "visitor": ["exact"],
            "doctor": ["exact"],
            "double_visit": ["exact"]
        }

Creo que encontré una manera mucho más simple:

from django.db.models.query_utils import Q
user_contacts = Contact.objects.order_by('-contact_date').filter(Q(sender_id=request.user.user_id)|Q(receiver_id=request.user.user_id)) 

En mi caso, quería reunir todas las filas que tenían sender_id o receiver_id específicos. Y quería que estuvieran en un conjunto de consultas para ordenarlos por date

Hola,
¿Cómo es posible filtrar en múltiples campos con la condición OR?
Por ejemplo:
first_name = foo OR last_name = bar
Equivalente a:
model.objects.filter(Q(first_name=foo) | Q(last_name=bar))

Importar Q

django.db.models importar Q

Funcionó para mí.

¿Fue útil esta página
0 / 5 - 0 calificaciones