Django-filter: Filtern Sie nach mehreren Feldern mit ODER-Bedingung

Erstellt am 13. Okt. 2019  ·  8Kommentare  ·  Quelle: carltongibson/django-filter

Hallo,
Wie ist es möglich, nach mehreren Feldern mit ODER-Bedingung zu filtern!?
Beispielsweise:
first_name = foo OR last_name = bar
Gleichwertig:
model.objects.filter(Q(first_name=foo) | Q(last_name=bar))

Hilfreichster Kommentar

Ich habe so etwas wie das Folgende getan, ich bin mir nicht sicher, ob es für alle Fälle funktioniert, aber mein Problem gelöst:

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"]
        }

Alle 8 Kommentare

Danke für die Eröffnung, das ist genau die gleiche Frage, die ich habe.

Aktuell bekomme ich folgende Warnung:

/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

Und ich habe ein FilterSet und ein ViewSet, das so aussieht:

# 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

Wie könnte ich ein FilterSet erstellen, um genau dieselbe Abfrage auszuführen?
Ich konnte einen Teil von FilterSet implementieren, weiß aber immer noch nicht, wie ich die OR -Logik am besten von ViewSet.get_queryset() nach FilterSet verschieben kann

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()

Es sieht so aus, als ob dies Teil dieser Erweiterung von django-filter ist, die zusammen mit django-filter installiert werden kann. https://github.com/philipn/django-rest-framework-filters#complex -operations

Hallo zusammen. Ich habe #1167 erstellt, das eine Validierung und Filterung auf Gruppenebene ermöglichen sollte. Für das OP würde die Verwendung in etwa so aussehen:

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

Jedes Feedback zur PR wäre sehr willkommen.

Immer noch das gleiche Problem hier, gibt es schon Lösungen?

@JeromeK13 sieht aus wie nichts! Ich habe dieselbe Frage

Ich habe so etwas wie das Folgende getan, ich bin mir nicht sicher, ob es für alle Fälle funktioniert, aber mein Problem gelöst:

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"]
        }

Ich glaube, ich habe einen viel einfacheren Weg gefunden:

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)) 

In meinem Fall wollte ich alle Zeilen sammeln, die bestimmte sender_id oder receiver_id enthalten. Und ich wollte, dass sie in einem Abfragesatz enthalten sind, um sie nach date zu sortieren

Hallo,
Wie ist es möglich, nach mehreren Feldern mit ODER-Bedingung zu filtern!?
Beispielsweise:
first_name = foo OR last_name = bar
Gleichwertig:
model.objects.filter(Q(first_name=foo) | Q(last_name=bar))

Q importieren

django.db.models importiert Q

Es hat für mich funktioniert.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen