Django

Code

Ticket #2445 (new)

Opened 2 years ago

Last modified 1 week ago

[patch] allow callable values for limit_choices_to

Reported by: michael@actrix.gen.nz Assigned to: nobody
Component: Core framework Version:
Keywords: Cc: zeraien@gmail.com, gary.wilson@gmail.com, django@attardi.org, davidgrant@gmail.com
Triage Stage: Accepted Has patch: 1
Needs documentation: 0 Needs tests: 0
Patch needs improvement: 1

Description

Allow limit_choices_to to test for and invoke callable value arguments. This would allow filtering on dynamically determined values. It enhances the possibilities for customising the admin interface. This may be related to Ticket 2193 (sounds similar at any rate).

For example:

def assigned_tasks():
   return get_assigned_tasks_id_list(blah, blah)

class TimeRecord(models.Model):
   task = models.ForeignKey(Task, limit_choices_to = {'id__in': assigned_tasks})

Attachments

query_value.diff (0.6 kB) - added by michael@actrix.gen.nz on 07/28/06 22:02:16.
test callable(value) in db/models/query.py
evallimitchoices.diff (4.8 kB) - added by michael@actrix.gen.nz on 10/13/06 04:54:13.
Patch to allow evaluation of callables in limit_choices_to
choices_func.diff (2.1 kB) - added by Dmitri Fedortchenko <zeraien@gmail.com> on 11/23/07 16:06:21.
This patch is still a bit unpredictable with edit_inline objects, but works with normal relationships.
choices_func.2.diff (2.1 kB) - added by Dmitri Fedortchenko <zeraien@gmail.com> on 11/23/07 16:33:33.
Accidentally included some debug print statements in the previous patch...whoops
choices_func_newforms-admin.patch (2.5 kB) - added by David Grant <davidgrant@gmail.com> on 01/06/08 19:07:36.
patch for newforms-admin branch. Just one line is different. Still doesn't work with inlines

Change History

07/28/06 22:02:16 changed by michael@actrix.gen.nz

  • attachment query_value.diff added.

test callable(value) in db/models/query.py

08/08/06 23:53:01 changed by anonymous

  • cc set to gary.wilson@gmail.com.

08/12/06 00:46:17 changed by adrian

I agree with the concept, but the patch is incorrect -- the check for callable() should be made only on the limit_choices_to values, not in the code that parses *every* query. Could you have another look?

08/12/06 00:46:40 changed by adrian

  • summary changed from [patch] allow callable values for limit_choices_to to allow callable values for limit_choices_to.

Removing [patch] from the subject, because the patch is invalid.

08/12/06 21:22:03 changed by Stefano J. Attardi <django@attardi.org>

  • cc changed from gary.wilson@gmail.com to gary.wilson@gmail.com, django@attardi.org.

09/04/06 22:05:02 changed by michael@actrix.gen.nz

I took another look - I only have limited familiarity with the code.

The value of limit_choices_to is passed to complex_filter() in three places:

django/contrib/admin/views/main.py
django/db/models/manipulators.py
django/db/models/fields/__init__.py

I believe that calls to complex_filter() are the only places limit_choices_to is actually used.

The limit_choices_to is always obtained via referencing some fragment such as .rel.limit_choices_to The "rel" class needs a method (hopefully in some base class) that evaluates its' limit_choices_to and returns a new hashmap:

    def get_limit_choices_to(self):
        limiters = {}
        if self.limit_choices_to:
            for id in self.limit_choices_to:
                value = self.limit_choices_to[id]
                if callable(value):
                    value = value()
                limiters[id] = value
        return limiters

The existing direct references to ref.limit_choices_to need to change to ref.get_limit_choices_to()

Does this sound like I'm on the right track? I'm proceeding on these lines for now.

10/13/06 04:54:13 changed by michael@actrix.gen.nz

  • attachment evallimitchoices.diff added.

Patch to allow evaluation of callables in limit_choices_to

10/13/06 05:05:40 changed by michael@actrix.gen.nz

  • summary changed from allow callable values for limit_choices_to to [patch] allow callable values for limit_choices_to.

I attached a new patch that attempts to implement callables in limit_choices_to without involving query.py. I've put [patch] back in the header. The new patch is larger - I'm open to suggestions on how it might be more simply achieved.

01/24/07 21:25:49 changed by Gary Wilson <gary.wilson@gmail.com>

  • needs_docs set to 1.
  • needs_tests set to 1.
  • stage changed from Unreviewed to Design decision needed.

I like the idea (touched on in #1891) of getting rid of limit_choices_to in favor of a choices that accepts QuerySets? or callables.

03/11/07 12:58:29 changed by webograph <webograph@eml.cc>

i think that callables should be passed a reference to self, at least if they accept it -- think of the following model:

class Mother(models.Model):
    firstborn = models.ForeignKey('Child', limit_choices_to={'mother':lambda me: me})
    
class Child(models.Model):
    mother = models.ForeignKey('Mother', related_name='children')

09/14/07 14:36:49 changed by jkocherhans

  • needs_docs deleted.
  • has_patch deleted.
  • needs_tests deleted.
  • stage changed from Design decision needed to Accepted.

We discussed this in Chicago and I think the plan is to allow some special methods to generate the choices.

class SomeOtherModel(models.Model):
    name = models.CharField(max_length=255)

class MyModel(models.Model):
    test = models.ForeignKey(SomeOtherModel):

    def choices_for_test(self):
        """Hook for providing custom choices. Returns a queryset or an iterable of choices tuples."""
        return SomeOtherModel.objects.all()

11/23/07 15:52:27 changed by Dmitri Fedortchenko <zeraien@gmail.com>

  • cc changed from gary.wilson@gmail.com, django@attardi.org to zeraien@gmail.com, gary.wilson@gmail.com, django@attardi.org.

Is anyone still working on this?

I've been seeking this functionality so I wrote a patch that actually implements jkocherhanss way of dealing with this.

By defining a function in your model called choices_forFIELDNAME you can limit the choices using data from the current row...

The patch also allows for using QuerySets? for the choices attribute for any field...

I am still working on getting it to work with edit_inline, but it works perfectly with normal models.

11/23/07 16:06:21 changed by Dmitri Fedortchenko <zeraien@gmail.com>

  • attachment choices_func.diff added.

This patch is still a bit unpredictable with edit_inline objects, but works with normal relationships.

11/23/07 16:33:33 changed by Dmitri Fedortchenko <zeraien@gmail.com>

  • attachment choices_func.2.diff added.

Accidentally included some debug print statements in the previous patch...whoops

12/01/07 05:36:51 changed by jezdez

  • has_patch set to 1.

12/01/07 17:22:00 changed by Dmitri Fedortchenko <zeraien@gmail.com>

  • needs_better_patch set to 1.

Patch needs work since it does not work with edit inlines.

01/06/08 19:07:36 changed by David Grant <davidgrant@gmail.com>

  • attachment choices_func_newforms-admin.patch added.

patch for newforms-admin branch. Just one line is different. Still doesn't work with inlines

01/06/08 19:58:06 changed by David Grant <davidgrant@gmail.com>

  • cc changed from zeraien@gmail.com, gary.wilson@gmail.com, django@attardi.org to zeraien@gmail.com, gary.wilson@gmail.com, django@attardi.org, davidgrant@gmail.com.

What's the problem with inlines? I've been trying to figure this out all day and I can't seem to figure out what's going on. It seem to be calling the choices_for* on out-of-date instances. The only thing I can think of is that the code in models/base.py isn't being called again, so there is an old choices_for* method attached to the field.

01/08/08 06:11:03 changed by David Grant <davidgrant@gmail.com>

I think the problem that appears with inlines is in attaching this method to the model's field (see content of patch). The choices_for__somefield method should not be a Field method it should be a model method (which is what it looks like anyways). This business of attaching it to the field needs to go away and the choices_for__somefield method needs to be called to filter out the choices for somefield within the context of some instance of the model (the self that is passed in).

This problem goes away if you always create an instance of the class that has the limited-choice field in it before calling the choices method. Then the choices choices_for__somefield gets reattached to the field and when you ask the field what it's choices are (via get_choices method) you get what you expect...but this is not how inlines work right now, where an instance of the inlined object is not always instantiated before looking at the field's choices, hence you will get the choices of the last-instantiated child object! But re-laod the page again and it works.

Only an instance of the model you are editing is instantiated (the non-inlined model) right away, which is why this hack works when editing the inlined model in non-in-lined mode (editing the child directly rather than through the parent).

03/10/08 17:44:56 changed by Alex

David's approach looks to be correct, however related fields don't actually have a limit_choices_to parameter, so I think we should combine this and #1891 and remove limit_choices_to entirely, and allow callables and querysets for choices, as this does.

05/09/08 03:58:38 changed by Matias Surdi <matiassurdi@gmail.com>

Do yo know if this patch is going to be applied to trunk in any momment?


Add/Change #2445 ([patch] allow callable values for limit_choices_to)




Change Properties
Action