-
Notifications
You must be signed in to change notification settings - Fork 771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alias names for filter and multiple comperators #774
Comments
Quick update: I've digged a little and I found that the function from django_filters import filterset
from django.db.models.constants import LOOKUP_SEP
def my_get_filter_name(field_name, lookup_expr):
"""
Combine a field name and lookup expression into a usable filter name.
Exact lookups are the implicit default, so "exact" is stripped from the
end of the filter name.
"""
if field_name == 'date_time_start':
field_name = 'start_date'
elif field_name == 'date_time_end':
field_name = 'end_date'
filter_name = LOOKUP_SEP.join([field_name, lookup_expr])
# This also works with transformed exact lookups, such as 'date__exact'
_exact = LOOKUP_SEP + 'exact'
if filter_name.endswith(_exact):
filter_name = filter_name[:-len(_exact)]
return filter_name
filterset.get_filter_name = my_get_filter_name The filter would look like this: class MeetingFilter(BaseFilter):
class Meta:
model = Meeting
fields = {
'date_time_end': BaseFilter.DATE_COMPERATORS,
'date_time_start': BaseFilter.DATE_COMPERATORS
} However, I hate having to monkey patch this... Therefore I would like to propose the following two options: Option 1 Option 2 fields = {
'date_time_end': BaseFilter.DATE_COMPERATORS,
'date_time_start': BaseFilter.DATE_COMPERATORS
}
field_mappings = {
'date_time_end': 'end_date',
'date_time_start': 'start_date'
} and update Option 1 would probably be super easy to do, and I would be happy to provide a PR for it. |
Hmmm. Interesting. Given that you definitely want That's not a problem. But the way you're doing it is right. Declaring the filters explicitly is the recommended way. I won't introduce more meta options. I can see the case for Of course, I guess you could just use a for loop to declare the filters in |
Thanks for your reply! I think the for loop can not live in |
You'd have to mungle More to your point I don't see a problem in moving |
But even that... I don't think you'd have an issue. |
Sorry for not trying this earlier. You are absolutely right, I can do this in the class MeetingFilter(BaseFilter):
def __init__(self, *args, **kwargs):
super(MeetingFilter, self).__init__(*args, **kwargs)
# add extra filters for date_time_end as end_date__{comperator}
for comperator in DATE_COMPERATORS:
filter_name = "end_date__{}".format(comperator)
filter = django_filters.DateTimeFilter(name='date_time_end', lookup_expr=comperator)
self.base_filters[filter_name] = filter
# add extra filters for date_time_start as start_date__{comperator}
for comperator in DATE_COMPERATORS:
filter_name = "start_date__{}".format(comperator)
filter = django_filters.DateTimeFilter(name='date_time_start', lookup_expr=comperator)
self.base_filters[filter_name] = filter The above code works just fine. However, the extra code required here isn't really pretty (and with pretty I mean understandable) compared to overriding the class MeetingFilter(BaseFilter):
class Meta:
model = Meeting
fields = {
'date_time_end': DATE_COMPERATORS,
'date_time_start': DATE_COMPERATORS
}
@classmethod
def get_filter_name(cls, field_name, lookup_expr):
"""
Rename the date_time_end and date_time_start fields of meeting to end_date and start_date
:param field_name:
:param lookup_expr:
:return:
"""
if field_name == 'date_time_start':
field_name = 'start_date'
elif field_name == 'date_time_end':
field_name = 'end_date'
return BaseFilter.get_filter_name(field_name, lookup_expr) Anyway, I hope my findings will help others! I will add a PR for this to work :) |
Making |
… each FilterClass, addressing issue carltongibson#774
First of all: Apologies if this topic is covered already in another issue, PR or SO post. I didn't manage to find anything that got remotely close.
Second: I am using Django Rest Framework and django-filter 1.0.4 with the Django ORM.
My Problem:
I have a
Meeting
model which (for legacy reasons) has the fieldsdate_time_start
anddate_time_end
. I want to create a Filter ClassMeetingFilter
with twoDateTimeFilter
s with the following comparators:DATE_COMPERATORS = ('lt', 'lte', 'gt', 'gte', 'exact', )
.This allows me to call the REST API endpoint with the following options:
/api/meetings/?date_time_start__gte=2017-01-01....&date_time_end__lte=2017-01-31...
For me, this is a very good and intuitive API.
However, I do not want to expose the names
date_time_start
anddate_time_end
via query parameters. Instead I would like them to be calledstart_date
andend_date
, so they match all my other APIs that are already built with those names. (I know I could just write a migration or "hack" myself into the request and change the paramters, etc..., but this is beyond the scope of what I am trying to achieve here.)So what I tried was this (note: using
name=
works as an alias for the ORM field):and everything is fine. Except for: it's ugly, and it's certainly not DRY.
Now I thought: it must be possible to do this DRY, mapping the alias and lookup expressions:
And indeed, this is possible. However, now the API has changed and I am no longer able to do
/api/meetings/?date_time_start__gte=2017-01-01....&date_time_end__lte=2017-01-31...
instead it is
/api/meetings/?start_date_0=2017-01-01....&start_date_1=gte&end_date_0=2017-01-31...&end_date_1=lte
I believe that while the code is now much nicer, the API is no longer intuitive. Now the big question is:
Can I define the filters DRY and have the intuitive API I want?
I've been looking through the code of django-filter already, and I found that the problem is here:
django_filters/filterset.py BaseFilterSet get_filters
Basically here it says "for every lookup expression: create a new filter for the field (unless the filter has been declared already)". However, if we use a declared filter and the lookup expressions list is provided in this declared filter, we get the
_0
and_1
appended to the URL parameters.I believe it should be possible to achieve the intuitive API with the following code:
end_date
andstart_date
in the meta fields, and declare the filters with an alias name.However, the django-filter code would have to apply
DATE_COMPERATORS
to the declared filters somehow. Am I out of luck, or is there a way to achieve this?The text was updated successfully, but these errors were encountered: