Class-Based Generic Views (CreateView) - field exclusions and defaults

5,453 views
Skip to first unread message

Paul Walsh

unread,
Jul 15, 2011, 5:18:17 AM7/15/11
to django...@googlegroups.com
I am pretty new to Django - new enough to be developing my first Django app on 1.3. So, I am basing all my work on class-based generic views, and have never used the older generic view functions.

A problem I have is the lack of examples and documentation on the new class-based generic views.

My current problem is that I am trying to build some forms based on models. I am not using Django Forms (ModelForm), but rather CreateView. CreateView gets me almost there, but I can't find any documentation on (1) how to exclude fields, and (2) how to fix a value for a field.

My class is like so:

class CreateCampaignView(CreateView):
model = Campaign
template_name = "forms/create.html"

I tried exclude from ModelForms, like this:

class CreateCampaignView(UserAccountCreateView):
model = Campaign
template_name = "forms/create.html"
        exclude = ('user', 'name', 'content_inlined')

and that doesn't work. Is there another way to exclude fields using CreateView?

and, one of my excluded fields, "user", also needs to get the value of self.request.user

I tried again here to apply the docs from ModelForms here for CreateView, but it doesn't work.


Some might suggest I use Django Forms - it is a reasonable suggestion of course. But, being new, I don't have any "view legacy" and I just like the idea of working with class-based generic views where possible.

thanks.


Kenneth Gonsalves

unread,
Jul 15, 2011, 6:37:34 AM7/15/11
to django...@googlegroups.com
On Fri, 2011-07-15 at 02:18 -0700, Paul Walsh wrote:
> I am pretty new to Django - new enough to be developing my first
> Django app
> on 1.3. So, I am basing all my work on class-based generic views, and
> have
> never used the older generic view functions.

caveat - I have never used generic views, class based or otherwise, but
I was under the impression that if one wants customised forms or views,
generic is not the way to go.
--
regards
KG
http://lawgon.livejournal.com
Coimbatore LUG rox
http://ilugcbe.techstud.org/

Venkatraman S

unread,
Jul 15, 2011, 6:58:23 AM7/15/11
to django...@googlegroups.com
Class based views reduce the amount of code written, but i am yet to get a grasp of it(need more experimentation).
Somehow, i prefer plain 'old' way of methods in views.py which respond to different urls - it gives me better control and is easily to maintain.

-V

Michał Sawicz

unread,
Jul 15, 2011, 6:59:48 AM7/15/11
to django...@googlegroups.com
Dnia 2011-07-15, pią o godzinie 16:07 +0530, Kenneth Gonsalves pisze:

> caveat - I have never used generic views, class based or otherwise,
> but
> I was under the impression that if one wants customised forms or
> views,
> generic is not the way to go.

Why not? The model edit views use the ModelFormMixin, which has a
get_form_class that can be overriden [1] to taste. You can simply get
the form from super().get_form_class and put it through
modelform_factory [2] again to only get the form for the fields you
want.

The classy generic views are really flexible like that. Sadly docs
aren't that great, so reading the code is probably best to get the hang
of it.

[1]
https://code.djangoproject.com/browser/django/trunk/django/views/generic/edit.py#L66
[2]
https://code.djangoproject.com/browser/django/trunk/django/forms/models.py#L370

Cheers
--
Michał (Saviq) Sawicz <mic...@sawicz.net>

signature.asc

Andre Terra

unread,
Jul 15, 2011, 9:09:10 AM7/15/11
to django...@googlegroups.com
Paul,

Your form logic will go on your Form (or ModelForm) definition, not on the view. As the docs [1] say, CreateView is a combination of ModelFormMixin and ProcessFormView. Please read the docs on those two mixins to understand what methods your CampaignCreate class can override, but do move the form logic (i.e. field excluding, validation, etc) to a custom form.

Try something like (the following code hasn't been tested):

# views.py
class CampaignCreate(CreateView):
    template_name = "forms/create.html"
    form_class = CampaignForm
   
   
# forms.py
class CampaingForm(forms.ModelForm)
    class Meta:
        model = Campaign

        exclude = ('user', 'name', 'content_inlined')


Hope that helps!


Cheers,
André


[1] https://docs.djangoproject.com/en/dev/ref/class-based-views/#django.views.generic.edit.CreateView




--
You received this message because you are subscribed to the Google Groups "Django users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-users/-/g4ADYoJAbE0J.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

Andre Terra

unread,
Jul 15, 2011, 9:12:26 AM7/15/11
to django...@googlegroups.com
On Fri, Jul 15, 2011 at 7:37 AM, Kenneth Gonsalves <law...@thenilgiris.com> wrote:

caveat - I have never used generic views, class based or otherwise, but
I was under the impression that if one wants customised forms or views,
generic is not the way to go.


These views are classes and they exist precisely so that the code can stay DRY through specific classes (e.g. MyModelCreateView) that subclass more generic classes (e.g. MyBaseCreateView) all the way up to the tools django provides (e.g. CreateView).


Cheers,
André

Russell Keith-Magee

unread,
Jul 15, 2011, 10:28:34 PM7/15/11
to django...@googlegroups.com
On Fri, Jul 15, 2011 at 5:18 PM, Paul Walsh <pauly...@gmail.com> wrote:
> I am pretty new to Django - new enough to be developing my first Django app
> on 1.3. So, I am basing all my work on class-based generic views, and have
> never used the older generic view functions.
> A problem I have is the lack of examples and documentation on the new
> class-based generic views.

This is admittedly a big problem with the class-based generic views.
As the person responsible for committing them, I can only offer my
humble apologies for their current state.

> Some might suggest I use Django Forms - it is a reasonable suggestion of
> course. But, being new, I don't have any "view legacy" and I just like the
> idea of working with class-based generic views where possible.

Some might, and they would be correct :-)

This isn't a legacy issue at all -- Forms and Views solve different problems.

The View is solving the problem of "how do I handle this request and
convert it into a response?". The Form is solving the problem of "How
do I convert the POST data in this request into a model object (or a
change to a model object)?".

Very roughly, a view is doing the following:

1. View gets a request
2. View works out whether this is a GET or a POST
3. If its a POST, View asks the Form to turn the Post into a model change
4. Form returns success or failure
5. View responds to the success or failure of the Form.
6. View returns a response.

The functionality of the Form is a complete subset of the
functionality of the View -- and for this reason, it's a completely
interchangable internal component.

Now, in simple situations, it's possible for a View to guess all the
defaults for the form -- all it needs to know is that you're dealing
with a Foo model, and it can construct a default Foo ModelForm.
However, if you have more sophisticated form requirements, you're
going to need a customized Form.

We *could* have implemented this by exposing all the options of
ModelForm on the View class; but in order to keep everything clean, we
kept the ModelForm isolated, and provided the View with a way to
specify which Form class it's going to use.

So - to cover your use case of excluding fields, you define a
ModelForm that excludes the fields, then let the CreateView know the
form you want to use:

class ModelForm(forms.ModelForm):


class Meta:
model = Campaign

exclude = ('user', 'name', 'content_inlined')

class CreateCampaignView(CreateView):
form_class = CampaignForm
template_name = "forms/create.html"

I'm guessing when you say "fix a values for a field", you mean setting
the values of user, name and content_inlined before you save the new
Campaign instance; to do this, you need to inject some extra code into
the form processing logic of the form:

class CreateCampaignView(CreateView):
form_class = CampaignForm
template_name = "forms/create.html

def form_valid(self, form):
self.object.user = ... (something meaningful.. e.g., self.request.user)
return super(CreateCampaignView, self).form_valid(form)

This overrides the default behavior when the form is valid, and sets
the extra values. The super() implementation of form_valid() will then
save the instance.

For the record, this could also be done by overriding the save()
method on the ModelForm -- however, if you do that, you lose the
request object, which you will need if you're trying to set the
instance values to something that is request-sensitive.

I hope I've provided some helpful clarification here.

Yours,
Russ Magee %-)

Venkatraman S

unread,
Jul 15, 2011, 11:33:56 PM7/15/11
to django...@googlegroups.com
The below email contents can go into the django wiki? Explained in a lucid manner.
Actually, it would be awesome, if someone from this group can volunteer to extract useful
text explanations from this ML and put in the wiki.

I would have done it, but i lost my password, and i dont see a 'forgot password' link in the wiki ;)

--
You received this message because you are subscribed to the Google Groups "Django users" group.

Paul Walsh

unread,
Jul 16, 2011, 2:26:27 PM7/16/11
to django...@googlegroups.com
Hi Russ,

Wow - your response is *exactly* what I needed - thanks a great deal :)

As you mention in there, *because* CreateView gets me so far without the Form class (it builds me a form based on the model I declare), I was exactly looking to do other "ModelForm" stuff at the CreateView level.

Now it is clear to me what I need to do. Thanks for your class-based generic views contribution, and I really hope someone will have the chance to do more docs and examples for class-based views. 

br

unread,
Jul 18, 2011, 9:21:52 AM7/18/11
to Django users
While this doesn't contribute an answer to your question, I must say
that name "class-based generic views" is a bit misleading . . .
implying that these class-based views are for doing things you want to
do that follow a strictly set pattern out of the box (i.e, listviews,
detailviews, create views, etc.), but that you'll have to use
something else (i.e., function based views) if you are defining your
own views. However, this is not the case at all. We wanted to switch
all our views to class-based, and have had great success with writing
our own class-based views by extending built-in view and mixin
classes. Basically, most of our own views extend the generic
TemplateView and we add in our own Mixins or add functionality to the
overridden class methods. Some of our views extend other generic view
classes. As the documentation gets updated and edited, this paradigm
probably ought to be explained, because for a while I just figured
that i still had to still use function views if I wanted to do
anything custom, beyond what the generic views provide in and of
themselves. They're a different beast but can be much more DRY and
add some OO awesomeness to your code once you get your head around
them. For now, I'd recommend looking at the source code to see which
functions you can override, its pretty straightforward.

Ben

Juergen Schackmann

unread,
Jan 11, 2012, 3:54:05 PM1/11/12
to django...@googlegroups.com
if is use this code, as proposed by russ:
    def form_valid(self, form):
        self.object.user = ... (something meaningful.. e.g., self.request.user)
        return super(CreateCampaignView, self).form_valid(form)

i get the error 'NoneType' object has no attribute 'user'. and actually, by looking at the source code, that is exactly what is supposed to happen in a create view: self.object is set to None, as you can see in BaseCreateView

    def post(self, request, *args, **kwargs):
        self.object = None
        return super(BaseCreateView, self).post(request, *args, **kwargs)

am i the only one having this problem? any help is highly appreciated. 
thanks
juergen

Juergen Schackmann

unread,
Jan 12, 2012, 3:45:40 PM1/12/12
to django...@googlegroups.com
can really no one help? i am really stuck here at the moment

Andres Reyes

unread,
Jan 12, 2012, 4:44:12 PM1/12/12
to django...@googlegroups.com
What are you triying to achieve?

2012/1/12 Juergen Schackmann <juergen.s...@googlemail.com>:


> can really no one help? i am really stuck here at the moment
>

> --
> You received this message because you are subscribed to the Google Groups
> "Django users" group.

> To view this discussion on the web visit

> https://groups.google.com/d/msg/django-users/-/RYLQqxJE7HYJ.


>
> To post to this group, send email to django...@googlegroups.com.
> To unsubscribe from this group, send email to
> django-users...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/django-users?hl=en.

--
Andrés Reyes Monge
arm...@gmail.com
+(505)-8873-7217

Juergen Schackmann

unread,
Jan 12, 2012, 5:22:36 PM1/12/12
to django...@googlegroups.com
I am trying to do the same as described in previous example:
I have a model with a foreign key field to User and in the view I want to populate the the field from request.user. 

And when doing it exactly the way as Russ proposed, I get the described error.

Alasdair Nicol

unread,
Jan 12, 2012, 5:48:58 PM1/12/12
to django...@googlegroups.com
--
I agree that it looks like self.object is None, so you can't set self.object.user as Russ wrote.

How about the following:

    def form_valid(self, form):
        form.instance.user = request.user
        return super(CreateCampaignView, self).form_valid(form)

I haven't had a chance to test the code. I hope that it works, or at least leads you in the right direction!

Regards,
Alasdair
-- 
Alasdair Nicol
Developer, MEMSET

mail: alas...@memset.com
 web: http://www.memset.com/

Memset Ltd., registration number 4504980. 25 Frederick Sanger Road, Guildford, Surrey, GU2 7YD, UK.

alej0

unread,
Jan 12, 2012, 6:57:50 PM1/12/12
to django...@googlegroups.com
self.object is None because you the workflow is

def form_valid(self, form):
    self.object = form.save(commit=False)
    self.object.user = self.request.user
    self.object.save()
    super(CreateCampaignView, self).form_valid(form) # that does the redirect

actually it's not necesary to neme it self.object but is a good practice, is how other class-based generic views work

self.object is None because you are usin a CreateView, so there is no object to work with until you save the form and get the corresponding instance.

Juergen Schackmann

unread,
Jan 13, 2012, 5:21:49 PM1/13/12
to django...@googlegroups.com
you made my day, working exactly like expected.  :-)
thanks a lot
Reply all
Reply to author
Forward
0 new messages