-
Notifications
You must be signed in to change notification settings - Fork 767
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
ChoiceFilter: don't call callable choices immediately #744
Comments
My first inclination it to think you're imports are badly structured. You should be able to run your migrations without importing your filtersets. (Happy to see why that inclination is wrong...) |
Django loads urls config when running manage.py -> urls.py: |
No. That seems fine. But I've not seen this particular problem, so I'm wondering why you are. (You're evaluating a fixed set of choices at import time... — that can't be right — I add a new item, my choices change — they MUST be evaluated per instantiation... — your filter set definition can't be right.) |
I do not intend to evaluate the choices at import time, this is exactly why I made a separate function that generates a list of choices:
I'd expect django-filter/django_filters/filters.py Line 199 in 7e94907
I haven't noticed this issue myself, until I moved a project to production server with an empty DB. |
Right, OK, I see. Yep, happy to consider solutions. 👍 — I guess the initial idea here wasn't considering you'd evaluate a QuerySet in your callable. (This being introduced as an alternative to a QuerySet.) But yes, evaluation should probably be deferred. |
This worked for me: import django_filters
from django.forms.fields import CallableChoiceIterator
from django_filters.conf import settings as django_filters_settingss
class DeferredChoiceFilter(django_filters.ChoiceFilter):
def __init__(self, *args, **kwargs):
empty_label = kwargs.pop(
'empty_label', django_filters_settingss.EMPTY_CHOICE_LABEL)
null_label = kwargs.pop(
'null_label', django_filters_settingss.NULL_CHOICE_LABEL)
null_value = kwargs.pop(
'null_value', django_filters_settingss.NULL_CHOICE_VALUE)
self.null_value = null_value
if 'choices' in kwargs:
choices = kwargs.get('choices')
# coerce choices to list
if callable(choices):
choices = CallableChoiceIterator(choices)
else:
choices = list(choices)
# create the empty/null choices that prepend the original choices
prepend = []
if empty_label is not None:
prepend.append(('', empty_label))
if null_label is not None:
prepend.append((null_value, null_label))
kwargs['choices'] = chain(prepend, choices)
# Skipping ChoiceFilter init
django_filters.Filter.__init__(self, *args, **kwargs)
Any thoughts? |
Well the obvious one is just, why not return an generator or such from Other than that, "Not sure, right now". I'd need to have a look into |
Because of django-filter/django_filters/filters.py Line 200 in 7e94907
It gets evaluated there too. |
Fine. Yes. Of course it does. OK. Your use-case seems legitimate. I'd take a PR on this. The starting point for that it a failing test case. I'll take a look at |
OK, I'll start working on a PR when I have more time, probably on the next weekend.
Since it's not a part of Django's public API, it might be reasonable to include a version of |
By the way, I believe this is a duplicate of #710. @orgkhnargh - could you install the branch from PR 680 and see if that fixes the issue? |
Yep. #680 looks good for this. @orgkhnargh care to give it a run? 👍 |
#680 does make |
When
choices
is a callable and it fetches data from DB, an error will be raised during first migration (relation does not exist
for postgres) if filterset is imported when running manage.py, because ChoiceFilter will runchoices
at the time of importing the file in which the filterset is defined, which may happen before any tables are created. This may also be the case whenchoices
callable references fields that will be created when later migrations are applied, but are not available at the moment, but I haven't tested this, so I can't be sure.Django itself wraps ChoiceField choices in an iterator, presumably to avoid evaluating choices at the moment the field is instantiated, which would help to prevent the error I am describing. See https://github.com/django/django/blob/8714403614c4dfa37e806db4a6708b8a91a827a4/django/forms/fields.py#L838
Would anyone be interested in a PR that employs a similar approach to ChoiceFilter?
The text was updated successfully, but these errors were encountered: