Discussion:
Class-based views
Valentin Golev
2010-11-05 14:29:43 UTC
Permalink
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
--
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-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
To unsubscribe from this group, send email to django-developers+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Russell Keith-Magee
2010-11-05 15:35:09 UTC
Permalink
Post by Valentin Golev
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?
   owner = models.ForeignKey(User)
   title = models.CharField(max_length=120)
and we have a form
       exclude = ('owner',) # because one can only add items which
belongs to him
so if we just write
   model = Item
   model = Item
       obj = form.save(commit=False)
       obj.owner = self.request.user
       obj.save()
       self.object = obj
       return HttpResponseRedirect(self.get_success_url())
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)
Post by Valentin Golev
Another question, or I'd rather say remark, is what Mixins are awesome
...
Post by Valentin Golev
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 %-)
--
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-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
To unsubscribe from this group, send email to django-developers+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Valentin Golev
2010-11-05 15:40:44 UTC
Permalink
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...

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
Post by Valentin Golev
Post by Valentin Golev
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?
owner = models.ForeignKey(User)
title = models.CharField(max_length=120)
and we have a form
exclude = ('owner',) # because one can only add items which
belongs to him
so if we just write
model = Item
model = Item
obj = form.save(commit=False)
obj.owner = self.request.user
obj.save()
self.object = obj
return HttpResponseRedirect(self.get_success_url())
form_class = ItemForm
form.instance.owner = self.request.user
return super(MyView, self).form_valid(form)
Post by Valentin Golev
Another question, or I'd rather say remark, is what Mixins are awesome
...
Post by Valentin Golev
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 %-)
--
You received this message because you are subscribed to the Google Groups
"Django developers" group.
To unsubscribe from this group, send email to
.
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 post to this group, send email to django-developers-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
To unsubscribe from this group, send email to django-developers+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Russell Keith-Magee
2010-11-05 15:47:01 UTC
Permalink
Post by Valentin Golev
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.
Post by Valentin Golev
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 %-)
--
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-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
To unsubscribe from this group, send email to django-developers+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Valentin Golev
2010-11-05 15:52:39 UTC
Permalink
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
Post by Valentin Golev
Post by Valentin Golev
Are you certain about self.instance?
http://code.djangoproject.com/browser/django/trunk/django/views/generic/edit.py
Lines
Post by Valentin Golev
173 and 90 tell me another thing...
I didn't say self.instance. I said *form*.instance.
Post by Valentin Golev
Well, Django can provide tools for extending views with either decorators
or
Post by Valentin Golev
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.
Post by Valentin Golev
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 %-)
--
You received this message because you are subscribed to the Google Groups
"Django developers" group.
To unsubscribe from this group, send email to
.
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 post to this group, send email to django-developers-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
To unsubscribe from this group, send email to django-developers+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Łukasz Rekucki
2010-11-05 16:12:28 UTC
Permalink
Post by Valentin Golev
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
--
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-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
To unsubscribe from this group, send email to django-developers+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Russell Keith-Magee
2010-11-05 16:16:52 UTC
Permalink
Post by Łukasz Rekucki
Post by Valentin Golev
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
@view_decorator(login_required)
   pass
   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 ;)
For the record - I'm still interested in getting CBV decorators into 1.3.

Yours,
Russ Magee %-)
--
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-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
To unsubscribe from this group, send email to django-developers+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Valentin Golev
2010-11-05 16:19:00 UTC
Permalink
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
Post by Łukasz Rekucki
Post by Valentin Golev
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
@view_decorator(login_required)
pass
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.
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
--
You received this message because you are subscribed to the Google Groups
"Django developers" group.
To unsubscribe from this group, send email to
.
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 post to this group, send email to django-developers-/JYPxA39Uh5TLH3MbocFF+G/***@public.gmane.org
To unsubscribe from this group, send email to django-developers+***@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Loading...