Commit eefc88fe authored by Christopher Adams's avatar Christopher Adams Committed by Tim Graham
Browse files

Fixed #2445 -- Allowed limit_choices_to attribute to be a callable.

ForeignKey or ManyToManyField attribute ``limit_choices_to`` can now
be a callable that returns either a ``Q`` object or a dict.

Thanks michael at actrix.gen.nz for the original suggestion.
parent a718fcf2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ answer newbie questions, and generally made Django that much better:

    Gisle Aas <gisle@aas.no>
    Chris Adams
    Christopher Adams <christopher.r.adams@gmail.com>
    Christopher Adams <http://christopheradams.info>
    Mathieu Agopian <mathieu.agopian@gmail.com>
    Roberto Aguilar <roberto@baremetal.io>
    ajs <adi@sieker.info>
+4 −1
Original line number Diff line number Diff line
@@ -240,7 +240,7 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
        if related_admin is not None:
            ordering = related_admin.get_ordering(request)
            if ordering is not None and ordering != ():
                return db_field.rel.to._default_manager.using(db).order_by(*ordering).complex_filter(db_field.rel.limit_choices_to)
                return db_field.rel.to._default_manager.using(db).order_by(*ordering)
        return None

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
@@ -383,6 +383,9 @@ class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
        # ForeignKeyRawIdWidget, on the basis of ForeignKey.limit_choices_to,
        # are allowed to work.
        for l in model._meta.related_fkey_lookups:
            # As ``limit_choices_to`` can be a callable, invoke it here.
            if callable(l):
                l = l()
            for k, v in widgets.url_params_from_lookup_dict(l).items():
                if k == lookup and v == value:
                    return True
+6 −6
Original line number Diff line number Diff line
@@ -459,17 +459,17 @@ def get_limit_choices_to_from_path(model, path):
    """ Return Q object for limiting choices if applicable.

    If final model in path is linked via a ForeignKey or ManyToManyField which
    has a `limit_choices_to` attribute, return it as a Q object.
    has a ``limit_choices_to`` attribute, return it as a Q object.
    """

    fields = get_fields_from_path(model, path)
    fields = remove_trailing_data_field(fields)
    limit_choices_to = (
    get_limit_choices_to = (
        fields and hasattr(fields[-1], 'rel') and
        getattr(fields[-1].rel, 'limit_choices_to', None))
    if not limit_choices_to:
        getattr(fields[-1].rel, 'get_limit_choices_to', None))
    if not get_limit_choices_to:
        return models.Q()  # empty Q
    elif isinstance(limit_choices_to, models.Q):
    limit_choices_to = get_limit_choices_to()
    if isinstance(limit_choices_to, models.Q):
        return limit_choices_to  # already a Q
    else:
        return models.Q(**limit_choices_to)  # convert dict to Q
+4 −1
Original line number Diff line number Diff line
@@ -180,7 +180,10 @@ class ForeignKeyRawIdWidget(forms.TextInput):
        return mark_safe(''.join(output))

    def base_url_parameters(self):
        return url_params_from_lookup_dict(self.rel.limit_choices_to)
        limit_choices_to = self.rel.limit_choices_to
        if callable(limit_choices_to):
            limit_choices_to = limit_choices_to()
        return url_params_from_lookup_dict(limit_choices_to)

    def url_parameters(self):
        from django.contrib.admin.views.main import TO_FIELD_VAR
+2 −2
Original line number Diff line number Diff line
@@ -742,11 +742,11 @@ class Field(RegisterLookupMixin):
            lst = [(getattr(x, self.rel.get_related_field().attname),
                   smart_text(x))
                   for x in rel_model._default_manager.complex_filter(
                       self.rel.limit_choices_to)]
                       self.get_limit_choices_to())]
        else:
            lst = [(x._get_pk_val(), smart_text(x))
                   for x in rel_model._default_manager.complex_filter(
                       self.rel.limit_choices_to)]
                       self.get_limit_choices_to())]
        return first_choice + lst

    def get_choices_default(self):
Loading