On Jun 1, 12:26 pm, Russell Keith-Magee <russ...@keith-magee.com>
wrote:
> Is this really something that's going to be able to be managed at the
> routing level? It seems to me that the decision about exactly which
> set of objects you want returned needs much finer control that a
> router method will allow.
>
> It might not happen in your case specifically, but I wouldn't think it
> would be unusual to have a single view in which both "full" list and
> the "filtered" list are retrieved. If you're working with the managers
> directly, that's easy:
>
> selected_canddiates = Candidate.objects.all()
> all_candidates = Candidate.objects_all.all()
>
> but if you want to go through the relation for objects related to a
> specific race, it's not:
>
> selected_canddiates = race.candidates.all()
> all_candidates = race.candidates.all()  # this won't give the right results...
>
> If I'm understanding your proposal correctly, I'd need to either tweak
> the _state of race between the two calls to switch the context, or
> write a router that was able to differentiate between the two uses
> based on some other contextual cue (I'm not sure what could be
> provided to give that differentiation, though).
>
> The context switching might be possible with some utility methods or a
> Python context managers, but it still seems like the wrong way to
> affect control -- modifying an object's state so that it's default
> behaviour changes, rather than explicitly requesting the behaviour
> your want.
>
> Here's a counter-proposal:
>
> mycandidate.races is a descriptor. That descriptor implements
> __get__() to return a RelatedObjectsManager that does the appropriate
> filtering. The related objects manager is a standard manager that
> allows calls to all(), filter() etc. Why not also implement __call__,
> and have __call__() return a new RelatedObjectsManager, bound to a
> different manager (as named in the arguments to call)?
>
> So, using your example:
>
>  * race.candidates.all() would use _default_manager
>
>  * race.candidates('objects_all').all() would use the objects_all manager
>
>  * race.candidates('objects').all() would also use _default_manager
> (but explicitly)
>
> With some slightly neater manager naming, it becomes even easier to
> read. Consider the following example:
>
> class Person(…):
>     people = Manager()
>     men = MaleManager()
>     women = FemaleManager()
>
> class Event(…):
>     attendees = ManyToMany(Person, related_name='events')
>     objects = Manager()
>     confirmed = ConfirmedManager()
>
> then:
>
> all people: Person.people.all()
> all women: Person.women.all()
>
> all events: Event.objects.all()
> all confirmed events: Event.confirmed.all()
>
> all events being attended by the Person frank: frank.events.all()
> all confirmed events being attended by the Person frank:
> frank.events('confirmed').all()
>
> all people attending the brogrammer event: brogrammer.attendees.all()
> all men attending the brogrammer event: brogrammer.attendees('men').all()
>
> For my money, that's an explicit API that makes sense when reading the
> single query line, and doesn't require the interpolation of extra
> state/context from a router or some other context manager. It's also
> backwards compatible, because there won't be any existing code
> invoking __call__() on a manager.
>
> Russ %-)

+1.

Just as a bike-shedding thought: Would it be possible to have
frank.events.confirmed.all() as the syntax? I see this a tiny bit
cleaner. On the other hand it isn't explicit you can only use
the .confirmed only directly after the events, and there is the
possibility of clashing with queryset methods. The clash issue can be
solved by just using frank.events('clashing_name').all() instead of
frank.events.clashing_name.all() in the rare cases this is needed.

How would this work with prefetch_related? Maybe
prefetch_related('events__confirmed')? IMHO this is an important use
case, as there isn't currently any possibility to use different
filtering, ordering etc than the one given by the default related
manager.

 - Anssi

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to