How can I make a Generic Class Based Create View for a Model?

15,345

Solution 1

You could subclass the edit.CreateView generic view, set the class/course in the dispatch() method, and save this by overriding the form_valid() method:

from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.views.generic.edit import CreateView


class CourseEntryCreateView(CreateView):
    form_class = CourseEntryForm
    model = CourseEntry

    def dispatch(self, *args, **kwargs):
        self.course = get_object_or_404(Class, pk=kwargs['class_id'])
        return super(CourseEntryCreateView, self).dispatch(*args, **kwargs)

    def form_valid(self, form):
        self.object = form.save(commit=False)
        self.object.course = self.course
        self.object.save()
        return HttpResponseRedirect(self.get_success_url())

If you're not customising the CourseEntryForm ModelForm, then you can leave out the form_class property.

Unfortunately, it is not possible to call super() in the form_valid() method - due to the way it has been written would mean the object would be saved again.

If you need the Class (course?) instance in the template context, then you can add this in the get_context_data() method:

    def get_context_data(self, *args, **kwargs):
        context_data = super(CourseEntryCreateView, self).get_context_data(
            *args, **kwargs)
        context_data.update({'course': self.course})
        return context_data

Solution 2

An alternative to Matt Austin's answer might be to override the get_form method:

from django.shortcuts import get_object_or_404
from django.views.generic import CreateView

class CourseEntryCreateView(CreateView):
    form_class = CourseEntryForm
    model = CourseEntry

    def get_form(self, form_class):
        form = super(CustomCreateView, self).get_form(form_class)
        course = get_object_or_404(Class, pk=self.kwargs['class_id'])
        form.instance.course = course
        return form

This way, .course is on the CourseEntry instance in the context, and on the instance created when the form is saved upon POST.

Share:
15,345

Related videos on Youtube

Lockjaw
Author by

Lockjaw

Updated on June 04, 2022

Comments

  • Lockjaw
    Lockjaw almost 2 years

    What I'm trying to do is Django boilerplate for functional views. Any help here is very much appreciated, as the docs show examples for the template view and list view, but I've found very little for the model-based generic views. Am I missing an example in the docs?

    I have a model that represents an entry in a calendar. There's a foreign key to another object (not a user) that owns the entry. What I want to do is simply to create the entry, ensuring that the entry's foreign key is properly set and then return the user to the appropriate calendar page.

    I don't know, though, how class-based generic views receive their URL arguments and I'm not clear on how to set the success_url so that it reuses the id that was originally passed to the creation URL. Again, thank you in advance for your help.

    What I'm asking, essentially, is, what is the class-based generic view equivalent of the following:

    def create_course_entry(request, class_id):
    '''Creates a general calendar entry.'''
    if request.method == 'POST':
        form = CourseEntryForm(request.POST)
        if form.is_valid():
            new_entry = form.save(commit=False)
            new_entry.course = Class.objects.get(pk=class_id)
            new_entry.full_clean()
            new_entry.save()
            return HttpResponseRedirect('/class/%s/calendar/' % class_id)
    else:
        form = CourseEntryForm()
    
    return render_to_response('classes/course_entry_create.html',
            { 'class_id': class_id, 'form': form, },
            context_instance=RequestContext(request))
    
  • stschindler
    stschindler over 10 years
    Instead of saving the object in form_valid() manually, you can simply use form.instance to set course. That makes it also possible to call super() again.

Related