Diedreik, Thanks for your comments - your solution seems similar to the one proposed by Meshy unless I'm missing something (https://gist.github.com/1957251). It is good to see multiple people coming up with the same solution to the issue independently though as that to me is an indication that we're moving in the right direction.
Meshy, Thanks for the link - the django-braces project looks useful - I'll probably start using that. I would love to get some input from a core developer (or two) on this to see where they stand. From where I'm sitting there seems to be a number of people working around this problem in more or less the same manner, and most of the arguments against haven't taken into consideration compatibility with other mixins, already existing class methods, etc. I would be happy to put together a patch in a ticket on trac and do any other grunt work required to make this happen. Cheers, Jordan On Wednesday, 31 October 2012 22:57:28 UTC+13, Meshy wrote: > > Marc and I have been using a mixin to override `dispatch()` with this > functionality. He has an ongoing pull request on > django-braces<https://github.com/brack3t/django-braces/pull/8>with the code. > I hope this can be useful to some of you. > > Meshy. > > On Wednesday, October 31, 2012 9:49:26 AM UTC, Diederik van der Boor wrote: >> >> Hi, >> >> Please allow me to add my €0.02. >> In a large project I've experienced a similar issue; and we solved it in >> a slightly different way. >> What we also noticed was: >> - overriding dispatch(), get() or post() wasn't good enough anymore. >> - the views need some initialization moment before their workflow (in >> get/post of the base classes) start. >> >> What we ended up with is this base class (simplified a bit): >> https://gist.github.com/3985939 >> >> *I seriously propose having such init() function in the Django views.* >> Mind you, that took a heated debate in the organization I was contacted >> for, so please allow me to explain to context here. >> I think we've found a missing cornerstone in the way the class based >> views are structured, and there is an easy fix. >> >> *What is the problem with overriding dispatch()?* >> When overriding dispatch(), get() or post() the flow is always: >> >> def dispatch(self, request, *args, **kwargs): >> # my code here. >> return super(…).dispatch(request, *args, **kwargs) >> >> The same also applies to get() and post(). >> In other words, the last deriving class on top of the inheritance chain >> is always initializing first before it's base classes. >> It can't rely on a base class to do some initialization. >> >> With our permission check in the base class' dispatch() method, anything >> deriving from that effectively >> couldn't override dispatch() anymore because that would run before the >> permission check. >> >> *How does the init method fix this?* >> By doing a self.init() in the top-most dispatch() method, each class in >> the inheritance chain has a chance to fetch the objects it needs to have. >> >> That code can be written as: >> >> def init(self): >> super(..).init() >> # my code here. >> >> Now, the base class can initialize, then the deriving class. >> With a larger inheritance chain, this behavior becomes crucial. >> Each class can build upon what the other has prepared already. >> >> >> All of a sudden, we could do things like this: >> >> class PhotoListView(TabbedListView): >> """ >> Contents of an photo album; a list of photo's. >> """ >> model = Photo >> >> template_name = "photoalbum_album.html" >> permission_class = permissions.PhotoAlbumViewPermission >> >> def init(self): >> super(PhotoListView, self).init() >> self.photoalbum = get_object_or_404(PhotoAlbum, >> pk=self.kwargs['pk']) # parent object that filters the list >> >> def get_queryset(self): >> return super(PhotoListView, >> self).get_queryset().in_album(self.photoalbum) >> >> def get_context_data(self, **kwargs): >> context = super(PhotoListView, self).get_context_data(**kwargs) >> context['photoalbum'] = self.photoalbum >> context['can_delete'] = self.is_authorized_for(PhotoDeleteView) >> return context >> >> This is a list view for photo's, and it's limited to a current photo >> album. >> The alternative is making a DetailView, and putting the photo's >> somewhere in get_context_data() and thereby loose what the list view has >> to offer. >> Now we can just state it's a list view (which it is), and introduce the >> filter easily. >> >> Without the init() method, you're probably knotting that somewhere in the >> get_queryset() and get_context_data(), >> without having a clear path of that's happening. Thanks to the simple >> init() method is all remains clean. >> >> >> *Some background of our use-case* >> The project is made for health care and privacy must be fully guaranteed. >> Hence, all views have to implement a permission check, which we wanted to >> have in the base class. >> The only place to hook things up, was before the super call to >> dispatch(), otherwise the view already executed. >> >> At the same time, the permission check needs information from things like >> "self.object", and the URL kwargs. >> That's because the permission check is role based; clients only see their >> views, counselors may inspect their clients, etc.. >> Implementing the check half-way in the regular get() and post() workflow >> wasn't an option as it's easy to miss. >> >> With the init() method, we allow the view to initialize, and fetch all >> objects, so more sophisticated code can be performed afterwards. >> Currently the permission check still fetches the objects itself as well, >> which will likely change in the future when the checks have more role-based >> options. >> >> >> I hope this gives a clear explanation why such method would be beneficial >> to Django's class based views. >> >> Looking forward to your suggestions and response, >> >> Diederik >> >> >> -- >> linkedin: http://nl.linkedin.com/in/vdboor >> website: http://www.edoburu.nl/ >> >> >> Op 30 okt. 2012, om 22:44 heeft Jordan Hagan het volgende geschreven: >> >> I would really like to see something like Meshy's proposed solution >> implemented as this is an issue that I've run into a few times as well. >> >> Although I can appreciate TiNo's argument of: >> >> > self.request = request >> > ... >> >> This creates a problem for methods that are going to be used in the >> overridden dispatch method and the dispatched method that need access to >> these attributes as they will need to be passed in as a parameter: >> >> in dispatch: >> self.some_method(request, *args, **kwargs) >> >> in dispatched: >> self.some_method(self.request, *self.args, **self.kwargs) >> >> which is just really messy. >> >> In addition to this methods from other generic view mixins cannot be used >> in the overridden dispatch method as they expect these class attributes to >> be available - 'get_object' on SingleObjectMixin is a good example of this >> as it requires self.kwargs to function: >> >> >> https://github.com/django/django/blob/master/django/views/generic/detail.py#L34 >> >> The only options available to us are monkey patching or code duplication, >> neither of which offer a good solution to this problem. Generic views are >> great for reducing boilerplate in code, and adding a hook in this case >> would do just that. Without this hook I'm forced to add code like the >> following to each of my projects as a workaround >> https://gist.github.com/3983252 >> >> Cheers, >> Jordan >> >> On Saturday, 17 March 2012 09:52:43 UTC+13, Mike Fogel wrote: >> >>> > I don't really see what difference another function makes. Sayhttps:// >>> gist.github.com/1957251is implemented, what makes: >>> > >>> > def prepare_view(self, request, *args, **kwargs): >>> > # the thing I want to do >>> > super(ClassName, self).prepare_view(request, *args, **kwargs) >>> > >>> > preferable over: >>> > >>> > def dispatch(self, request, *args, **kwargs): >>> > # the thing I want to do >>> > super(ClassName, self).dispatch(request, *args, **kwargs) >>> > >>> > ? >>> >>> https://gist.github.com/1957251 would allow: >>> >>> def prepare_view(self, request, *args, **kwargs): >>> super(ClassName, self).prepare_view(request, *args, **kwargs) >>> # the thing I want to do - can use self.request, self.args, >>> self.kwargs >>> >>> As things stand now, I don't know of a graceful manner to use >>> self.request in a http-method independent way. >>> >>> FWIW, I've ran into this restriction a number of times, including >>> today. If one of the core devs will nod approval on this, I'll open a >>> ticket and attach this gist to it. >>> >>> Cheers, >>> >>> Mike >>> >>> On Mar 4, 9:45 am, Tino de Bruijn <[email protected]> wrote: >>> > I don't really see what difference another function makes. Sayhttps:// >>> gist.github.com/1957251is implemented, what makes: >>> > >>> > def prepare_view(self, request, *args, **kwargs): >>> > # the thing I want to do >>> > super(ClassName, self).prepare_view(request, *args, **kwargs) >>> > >>> > preferable over: >>> > >>> > def dispatch(self, request, *args, **kwargs): >>> > # the thing I want to do >>> > super(ClassName, self).dispatch(request, *args, **kwargs) >>> > >>> > ? >>> > >>> > You'll still have a super call because otherwise you have to repeat >>> the >>> > >>> > self.request = request >>> > self.args = args >>> > self.kwargs = kwargs >>> > >>> > part of prepare_view. >>> > >>> > What is wrong with overriding dispatch and calling super? (Or not, if >>> you >>> > don't want to progress in the view) >>> > >>> > Tino >>> >>> -- >> You received this message because you are subscribed to the Google Groups >> "Django developers" group. >> To view this discussion on the web visit >> https://groups.google.com/d/msg/django-developers/-/uYmm9IR6P7QJ. >> To post to this group, send email to [email protected]. >> To unsubscribe from this group, send email to >> [email protected]. >> For more options, visit this group at >> http://groups.google.com/group/django-developers?hl=en. >> >> >> -- You received this message because you are subscribed to the Google Groups "Django developers" group. To view this discussion on the web visit https://groups.google.com/d/msg/django-developers/-/JEr87QVHOC4J. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
