Class-based views

21 views
Skip to first unread message

Valentin Golev

unread,
Nov 5, 2010, 10:29:43 AM11/5/10
to Django developers
Hello,

I'm still playing with brand new class-based views. I think I'm not
wrong about writing my experiences, questions and ideas so you
developers could polish the API more (if I'm being useless here,
sorry).

My first question is, that is "the right way" to handle creation of
objects with parameters which can't be set in the creation form?

Let's say we have a model:

class Item(models.Model):
owner = models.ForeignKey(User)
title = models.CharField(max_length=120)

and we have a form

class ItemForm(forms.ModelForm):
class Meta:
exclude = ('owner',) # because one can only add items which
belongs to him

so if we just write

class ItemCreateView(CreateView):
model = Item

the form won't be saved! And I have to do something like this:

class ItemCreateView(CreateView):
model = Item
def form_valid(self, form):
obj = form.save(commit=False)
obj.owner = self.request.user
obj.save()
self.object = obj
return HttpResponseRedirect(self.get_success_url())

I used to create an unsaved "instance" for that with function-based
view. I guess there should be used get_object() in CreateView, like in
the other SingleObjectMixin descendants, instead of just writing
"self.object = None" (or add a get_instance() function). (If I'm
right, maybe I could write a patch? But how do you handle the bleeding-
edge fast-changing features development? My experience with ticket/
patch system on code.djangoproject.com is dissappointing).


Another question, or I'd rather say remark, is what Mixins are awesome
and seem better than decorators. My views.py file looks like this now:


class AuthenticatedOnlyMixin(object):
def dispatch(self, request, *args, **kwargs):
assert request.user.is_authenticated(), "You must be
authenticated!" # todo: a better handling, maybe redirect
return super(AuthenticatedOnlyMixin, self).dispatch(request,
*args, **kwargs)

class ItemMixin(object):
model = Item
form_class = ItemForm

class UserItemMixin(ItemMixin, AuthenticatedOnlyMixin):
def get_queryset(self):
return super(UserItemMixin,
self).get_queryset().filter(owner=self.request.user)


class ItemView(ItemMixin, DetailView):
pass

class ItemCreateView(ItemMixin, AuthenticatedOnlyMixin, CreateView):
def form_valid(self, form):
obj = form.save(commit=False)
obj.owner = self.request.user
obj.save()
self.object = obj
return HttpResponseRedirect(self.get_success_url())

class ItemListView(ItemMixin, ListView):
context_object_name = 'item_list'

IndexView = ItemListView


class ItemEditView(UserItemMixin, UpdateView):
pass

class ItemDeleteView(UserItemMixin, DeleteView):
success_url = '/'

and I like this "style" more than dancing with old-school decorators
in one way or another. We've already had this conversation, but I
can't still can't see neither why this style is bad nor how should I
write it instead? (By "bad" and "should write" I mean "not supported
and not encouraged by Django" and "supported and encouraged by
Django").

I hope my thoughts can help you (and every django-lover) a little bit.

- Valya

Russell Keith-Magee

unread,
Nov 5, 2010, 11:35:09 AM11/5/10
to django-d...@googlegroups.com

There's a much easier way to do this, and no extra features are required:

class ItemCreateView(CreateView):
form_class = ItemForm
def form_valid(self, form):
form.instance.owner = self.request.user
return super(MyView, self).form_valid(form)

> Another question, or I'd rather say remark, is what Mixins are awesome
> and seem better than decorators. My views.py file looks like this now:

...


> and I like this "style" more than dancing with old-school decorators
> in one way or another. We've already had this conversation, but I
> can't still can't see neither why this style is bad nor how should I
> write it instead? (By "bad" and "should write" I mean "not supported
> and not encouraged by Django" and "supported and encouraged by
> Django").

Well, Mixins predate decorators by some time; Mixins are really just a
pattern of multiple inheritance.

It's not a matter of one being better than the other, either. They are
for solving different problems. Decorators allow you to wrap an
existing block of logic with entry and exit conditions. Mixins allow
you to compose complex behaviors out of smaller primitives, and
enhance internal behaviors by replacing key components.

In some cases, you will be able to get the same outcome using either
approach; it's really up to you as to which one you want to use.

Yours
Russ Magee %-)

Valentin Golev

unread,
Nov 5, 2010, 11:40:44 AM11/5/10
to django-d...@googlegroups.com
Are you certain about self.instance?

Well, Django can provide tools for extending views with either decorators or mixins (or both - won't it cause uncertainity? maybe with some recommendation). Like the one from my example, django.contrib.auth.decorators.login_required can be transformed into Mixin. Will it be?

--
Best Regards,
Valentin Golev
Lead Developer
r00, http://r00.ru

http://valyagolev.net
+7 921 789 0895, avaiable 12:00-18:00 MSK



--
You received this message because you are subscribed to the Google Groups "Django developers" group.
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.


Russell Keith-Magee

unread,
Nov 5, 2010, 11:47:01 AM11/5/10
to django-d...@googlegroups.com
On Fri, Nov 5, 2010 at 11:40 PM, Valentin Golev <m...@valyagolev.net> wrote:
> Are you certain about self.instance?
> http://code.djangoproject.com/browser/django/trunk/django/views/generic/edit.py Lines
> 173 and 90 tell me another thing...

I didn't say self.instance. I said *form*.instance.

> Well, Django can provide tools for extending views with either decorators or
> mixins (or both - won't it cause uncertainity? maybe with some
> recommendation). Like the one from my example,
> django.contrib.auth.decorators.login_required can be transformed into Mixin.
> Will it be?

Unlikely, because login is a natural fit for a decorator.

You have a view. The operation of that view can be a black box with
regards to login. The decorator can check login status, and redirect
if you're not logged in; or call the black box if you are.

Sure, you *could* implement this as a mixin, but I'm not sure I see
the gain in offering two ways to do the same thing.

Yours,
Russ Magee %-)

Valentin Golev

unread,
Nov 5, 2010, 11:52:39 AM11/5/10
to django-d...@googlegroups.com
I'm sorry, I misread it.
Please mention form.instance field in "forms for models" docs somewhere.

The gain is inheritance. If one of your view classes is intented to be subclassed and requires logging in, Mixins are natural solution, methinks


--
Best Regards,
Valentin Golev
Lead Developer
r00, http://r00.ru

http://valyagolev.net
+7 921 789 0895, avaiable 12:00-18:00 MSK



Yours,
Russ Magee %-)

Łukasz Rekucki

unread,
Nov 5, 2010, 12:12:28 PM11/5/10
to django-d...@googlegroups.com
On 5 November 2010 16:52, Valentin Golev <m...@valyagolev.net> wrote:
> I'm sorry, I misread it.
> Please mention form.instance field in "forms for models" docs somewhere.
>
> The gain is inheritance. If one of your view classes is intented to be
> subclassed and requires logging in, Mixins are natural solution, methinks

You can see my clumsy implementation of a CBV decorator[1]. This lets you write:

@view_decorator(login_required)
class ProtectedView(View):
pass

class MyView(ProtectedView):
pass

and the view function produced by MyView.as_view() will also have
login_required applied to it. You can use a simillar technique to
transform a function decorator to a Mixin, but class decorators are
more readable IMHO.

[1]: https://github.com/lqc/django/blob/cbvdecoration_ticket14512/django/utils/decorators.py#L42

PS. I guess I need to cleanup that patch so It could make it to 1.3 ;)

--
Łukasz Rekucki

Russell Keith-Magee

unread,
Nov 5, 2010, 12:16:52 PM11/5/10
to django-d...@googlegroups.com
2010/11/6 Łukasz Rekucki <lrek...@gmail.com>:

For the record - I'm still interested in getting CBV decorators into 1.3.

Yours,
Russ Magee %-)

Valentin Golev

unread,
Nov 5, 2010, 12:19:00 PM11/5/10
to django-d...@googlegroups.com
Actually, I've already added your function into my django_future.py ;-) Thank you!
I wasn't sure it's good with inheritance - good to hear that!
But I wanted to know "the right way" in Django, and since your patch isn't in trunk yet, I didn't want to rely on it.
If using both mixins and decorators simultaneously is how Django going to work  - I'll put up with it.

--
Best Regards,
Valentin Golev
Lead Developer
r00, http://r00.ru

http://valyagolev.net
+7 921 789 0895, avaiable 12:00-18:00 MSK


2010/11/5 Łukasz Rekucki <lrek...@gmail.com>

--
Reply all
Reply to author
Forward
0 new messages