1
votes

Comment filtrer dynamiquement avec le nom de champ, la condition et les valeurs dans Django

J'ai une exigence, c'est-à-dire que du côté client, j'obtiendrai un tableau d'objets qui auront des noms de champ, des conditions de filtrage et des valeurs de filtrage comme celle-ci.

Exemple de tableau d'objets:

def assign_filter_condition(self, request, filter_condition, field_name, filter_value):

   if filter_condition == "contains":
        kwargs = {
                '{0}__icontains'.format(field_name): filter_value
            }
        return kwargs
       ...

et je dois filtrer en fonction de toutes les conditions ci-dessus. Considérez que j'ai un modèle avec le nom Book et les champs qui sont là dans le tableau des objets (ie book_name, book_category, book_author, book_price). Pour filter_condition, j'ai écrit une fonction qui compare la filter_condition et assigne l'API Django queryset. Par exemple.

[
 {field_name: "book_name", filter_condition: "contains", filter_value: "some book name"},
 {field_name: "book_category", filter_condition: "not_equal", filter_value: "category value"},
 {field_name: "book_author", filter_condition: "starts_with", filter_value: "authoer name"},
 {field_name: "book_price", filter_condition: "equal", filter_value: 100}
]

Ici, je ne sais pas comment appliquer des conditions d'exclusion et NON de requête.

Je ne comprends vraiment pas comment écrire la logique de cela. Quelqu'un peut-il s'il vous plaît aider avec la logique.

Merci d'avance.


3 commentaires

Je ne connais pas votre cas d'utilisation ou les conditions dans lesquelles vous travaillez, mais vous devriez au moins avoir une raison de refuser d'utiliser Django-filter django-filter.readthedocs.io/en/master/index.html


@ nigel222 L'exemple de tableau d'objets ci-dessus que j'ai mentionné dans la question, tout comme je vais envoyer une requête à DRF. puisque vous pouvez voir la condition du filtre et le nom du champ changera. Donc, je ne sais pas comment m'y attaquer ou écrire la logique dans le backend.


OK, Django-filter n'est probablement pas le bon outil pour votre utilisation (DRF). Espérons que le commentaire informera les personnes qui arrivent ici à la recherche d'un "filtre dynamique" ou autre.


3 Réponses :


0
votes

Vous pouvez le faire en utilisant le déballage de dict. Veuillez noter que vous n'avez pas besoin de conditions donc soyez un dict, le tableau est suffisant. Le code suivant vous mènera là où vous voulez.

conditions = [
 ("book_name", "contains",  "some book name"),
 ("book_category", "not_equal",  "category value"),
 ("book_author", "starts_with",  "authoer name"),
 ("book_price", "equal",  100)
]
def get_filter(values):
    name,condition,value = values
    key = f"{name}__{condition}"
    return key, value

filters = dict(map(get_filter,conditions))
qs = qs.filter(**filters)


0 commentaires

1
votes

Après avoir parcouru quelques publications StackOverflow, j'ai compris la logique. Voici donc la logique.

def get_filter(self, field_name, filter_condition, filter_value):
        # thanks to the below post
        # https://stackoverflow.com/questions/310732/in-django-how-does-one-filter-a-queryset-with-dynamic-field-lookups
        # the idea to this below logic is very similar to that in the above mentioned post
        if filter_condition.strip() == "contains":
            kwargs = {
                '{0}__icontains'.format(field_name): filter_value
            }
            return Q(**kwargs)

        if filter_condition.strip() == "not_equal":
            kwargs = {
                '{0}__iexact'.format(field_name): filter_value
            }
            return ~Q(**kwargs)

        if filter_condition.strip() == "starts_with":
            kwargs = {
                '{0}__istartswith'.format(field_name): filter_value
            }
            return Q(**kwargs)
        if filter_condition.strip() == "equal":
            kwargs = {
                '{0}__iexact'.format(field_name): filter_value
            }
            return Q(**kwargs)

        if filter_condition.strip() == "not_equal":
            kwargs = {
                '{0}__iexact'.format(field_name): filter_value
            }
            return ~Q(**kwargs)

def get(self, request):
    # getting the array of objects data to filter. The array of objects data 
    # example is in the question
    filter_data = request.query_params.getlist('filterData[]')

    all_books = Books.objects.all()
    # Creating initial Q object
    filter_objects = Q()

    # Looping through the array of objects
    for data in filter_data:
        # The main part. Calling get_filter and passing the filter object data.
        filter_objects &= self.get_filter(
                data["fieldName"], data["filterCondition"],
                data["filterValue"])
    filtered_data = all_books.filter(filter_objects)

J'espère que ça aide quelqu'un. :)


0 commentaires

0
votes

créer une classe qui hérite de DjangoFilterBackends et utiliser à la place de ceci

class MyFilterBackend(DjangoFilterBackend):
    def get_filterset_kwargs(self, request, queryset, view):
        data = request.query_params
        data._mutable = True
        for item in data:
            try:
                print(item)
                field = getattr(queryset.model, item)
                field: DeferredAttribute
                data[item] = [x for x in list(field.field.choices) if x[1] == data[item]][0][0]
            except:
                pass
        return {
            'data': request.query_params,
            'queryset': queryset,
            'request': request,
        }


0 commentaires