Just wanted to put my +1 into this thread. I've been fighting for a clean way to implement permissions in class based views and the 'init' method would help my implementation be a lot more DRY.
On Wednesday, October 31, 2012 12:42:33 PM UTC-6, Jordan Hagan wrote: > > 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 <tin...@gmail.com> 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 django-d...@googlegroups.com. >>> To unsubscribe from this group, send email to >>> django-develop...@googlegroups.com. >>> 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/-/Um5fZBppDqMJ. 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.