[Answered ]-Excluding fields in generic CRUD views


Have a look at Russel’s answer to a similar question on the django-users group earlier this week.

Quoting the answer*:

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 CampaignForm(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):
        form.instance.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.

*the original answer set self.object.user instead of form.instance.user. This gives an AttributeError so I have changed it above.

Leave a comment