Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #1579 - added support for 'Q' objects in limit_choices_to.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@2850 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit f57e34e99038bf0834638e6690d04d3ca59cde81 1 parent 3f932e3
@spookylukey spookylukey authored
View
2  django/contrib/admin/views/main.py
@@ -714,7 +714,7 @@ def get_query_set(self):
qs = qs & other_qs
if self.opts.one_to_one_field:
- qs = qs.filter(**self.opts.one_to_one_field.rel.limit_choices_to)
+ qs = qs.complex_filter(self.opts.one_to_one_field.rel.limit_choices_to)
return qs
View
2  django/db/models/fields/__init__.py
@@ -295,7 +295,7 @@ def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
return first_choice + list(self.choices)
rel_model = self.rel.to
return first_choice + [(x._get_pk_val(), str(x))
- for x in rel_model._default_manager.filter(**self.rel.limit_choices_to)]
+ for x in rel_model._default_manager.complex_filter(self.rel.limit_choices_to)]
def get_choices_default(self):
if self.radio_admin:
View
12 django/db/models/fields/related.py
@@ -679,7 +679,9 @@ def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
- self.limit_choices_to = limit_choices_to or {}
+ if limit_choices_to is None:
+ limit_choices_to = {}
+ self.limit_choices_to = limit_choices_to
self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin
self.multiple = True
@@ -695,7 +697,9 @@ def __init__(self, to, field_name, num_in_admin=0, edit_inline=False,
self.to, self.field_name = to, field_name
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
self.related_name = related_name
- self.limit_choices_to = limit_choices_to or {}
+ if limit_choices_to is None:
+ limit_choices_to = {}
+ self.limit_choices_to = limit_choices_to
self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin
self.multiple = False
@@ -707,7 +711,9 @@ def __init__(self, to, num_in_admin=0, related_name=None,
self.num_in_admin = num_in_admin
self.related_name = related_name
self.filter_interface = filter_interface
- self.limit_choices_to = limit_choices_to or {}
+ if limit_choices_to is None:
+ limit_choices_to = {}
+ self.limit_choices_to = limit_choices_to
self.edit_inline = False
self.raw_id_admin = raw_id_admin
self.symmetrical = symmetrical
View
3  django/db/models/manager.py
@@ -68,6 +68,9 @@ def get(self, *args, **kwargs):
def filter(self, *args, **kwargs):
return self.get_query_set().filter(*args, **kwargs)
+ def complex_filter(self, *args, **kwargs):
+ return self.get_query_set().complex_filter(*args, **kwargs)
+
def exclude(self, *args, **kwargs):
return self.get_query_set().exclude(*args, **kwargs)
View
6 django/db/models/manipulators.py
@@ -261,9 +261,9 @@ def __init__(self, obj_key, follow=None):
# Sanity check -- Make sure the "parent" object exists.
# For example, make sure the Place exists for the Restaurant.
# Let the ObjectDoesNotExist exception propagate up.
- lookup_kwargs = self.opts.one_to_one_field.rel.limit_choices_to
- lookup_kwargs['%s__exact' % self.opts.one_to_one_field.rel.field_name] = obj_key
- self.opts.one_to_one_field.rel.to.get_model_module().get(**lookup_kwargs)
+ limit_choices_to = self.opts.one_to_one_field.rel.limit_choices_to
+ lookup_kwargs = {'%s__exact' % self.opts.one_to_one_field.rel.field_name: obj_key}
+ self.opts.one_to_one_field.rel.to.get_model_module().complex_filter(limit_choices_to).get(**lookup_kwargs)
params = dict([(f.attname, f.get_default()) for f in self.opts.fields])
params[self.opts.pk.attname] = obj_key
self.original_object = self.opts.get_model_module().Klass(**params)
View
11 django/db/models/query.py
@@ -297,6 +297,17 @@ def _filter_or_exclude(self, qtype, *args, **kwargs):
clone._filters = clone._filters & reduce(operator.and_, args)
return clone
+ def complex_filter(self, filter_obj):
+ """Returns a new QuerySet instance with filter_obj added to the filters.
+ filter_obj can be a Q object (has 'get_sql' method) or a dictionary of
+ keyword lookup arguments."""
+ # This exists to support framework features such as 'limit_choices_to',
+ # and usually it will be more natural to use other methods.
+ if hasattr(filter_obj, 'get_sql'):
+ return self._filter_or_exclude(None, filter_obj)
+ else:
+ return self._filter_or_exclude(Q, **filter_obj)
+
def select_related(self, true_or_false=True):
"Returns a new QuerySet instance with '_select_related' modified."
return self._clone(_select_related=true_or_false)
View
4 docs/model-api.txt
@@ -704,6 +704,10 @@ relationship should work. All are optional:
``pub_date`` before the current date/time to be
chosen.
+ Instead of a dictionary this can also be a ``Q`` object
+ (an object with a ``get_sql()`` method) for more complex
+ queries.
+
Not compatible with ``edit_inline``.
``max_num_in_admin`` For inline-edited objects, this is the maximum
View
7 tests/modeltests/or_lookups/models.py
@@ -86,4 +86,11 @@ def __repr__(self):
>>> Article.objects.filter(Q(headline__startswith='Hello')).in_bulk([1,2])
{1: Hello}
+# The 'complex_filter' method supports framework features such as
+# 'limit_choices_to' which normally take a single dictionary of lookup arguments
+# but need to support arbitrary queries via Q objects too.
+>>> Article.objects.complex_filter({'pk': 1})
+[Hello]
+>>> Article.objects.complex_filter(Q(pk=1) | Q(pk=2))
+[Hello, Goodbye]
"""
Please sign in to comment.
Something went wrong with that request. Please try again.