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.

Reply via email to