How to edit a model with an inline formset

542 views
Skip to first unread message

Cody Scott

unread,
Jan 9, 2014, 11:57:37 AM1/9/14
to django...@googlegroups.com
I am trying to create a model with an inline formset. I followed the guide here http://kevindias.com/writing/django-class-based-views-multiple-inline-formsets/

Creating my model as in the guide works. But I am trying to edit my model. The EditPage renders fine with the form but when I submit the form I get an error on the {{ skill_form.management_form }} line in my template. list index out of range.

#models
class Job(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='jobs')
    title = models.CharField('job title', max_length=255)
    slug = models.SlugField(max_length=255, blank=True, default='')
    city = models.CharField(max_length=255)
    company = models.CharField(max_length=255)
    start_date = models.DateField()
    type = models.CharField("Work Shedule", max_length=255)
    remote = models.BooleanField("Remote Work")
    contact = models.TextField()

    description = models.TextField()
    requirements = models.TextField(null=True, blank=True)
    responsibilities = models.TextField(null=True, blank=True)
    education = models.TextField(null=True, blank=True)
    experience = models.TextField(null=True, blank=True)
    perks = models.TextField(null=True, blank=True)
    about = models.TextField("About the Company", null=True, blank=True)

    post_date = models.DateField(auto_now_add=True, editable=False)
    updated_date = models.DateTimeField(auto_now=True, editable=False)

    class Meta:
        ordering = ["-updated_date", "title"]

    def __unicode__(self):
        return self.title

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
        super(Job, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('job:view', args=(), kwargs={'id': self.id, 'slug': self.slug})


class Skill(models.Model):
    job = models.ForeignKey(Job, related_name='skills')
    skill = models.CharField(max_length=255)


    def __unicode__(self):
        return self.skill

#forms
class JobForm(forms.ModelForm):
    type = forms.ChoiceField(choices=[('Full-Time', 'Full-Time'), ('Part-Time', 'Part-Time')])

    class Meta:
        model = Job
        fields = ['title', 'city', 'company', 'start_date', 'description',
              'requirements', 'responsibilities', 'education', 'experience',
              'perks', 'contact', 'remote', 'about']


SkillFormSet = inlineformset_factory(Job, Skill)


#views
class CreateJob(generic.CreateView):
    model = Job
    template_name = 'job/add.html'
    form_class = JobForm

    def get(self, request, *args, **kwargs):
        """
        instantiates blank versions of the form
        and its inline formsets
        """
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        skill_form = SkillFormSet()
        return self.render_to_response(self.get_context_data(
            form=form,
            skill_form=skill_form,
        ))

    def post(self, request, *args, **kwargs):
        """
        instantiates the form with its inline formsets
        with the passed POST data and validates
        """
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        skill_form = SkillFormSet(self.request.POST)
        if form.is_valid() and skill_form.is_valid():
            return self.form_valid(form, skill_form)
        else:
            return self.form_invalid(form, skill_form)

    def form_valid(self, form, skill_form):
        """
        If both forms are valid then create Job with skills and redirect
        """
        self.object = form.save(commit=False)
        self.object.user = self.request.user
        self.object.save()
        skill_form.instance = self.object
        skill_form.save()
        return HttpResponseRedirect(self.object.get_absolute_url())

    def form_invalid(self, form, skill_form):
        """
        If either of the forms are invalid
        Re-render the page with data-filled forms and errors
        """
        return self.render_to_response(self.get_context_data(form=form, skill_form=skill_form))

    @method_decorator(login_required(login_url=reverse_lazy('login')))
    def dispatch(self, *args, **kwargs):
        return super(CreateJob, self).dispatch(*args, **kwargs)


class EditJob(generic.UpdateView):
    model = Job
    template_name = 'job/edit.html'
    context_object_name = 'job'

    def get(self, request, *args, **kwargs):
        """
        instantiates the form and inline formset
        """
        self.object = self.get_object()
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        skill_form = SkillFormSet(instance=self.object)
        return self.render_to_response(self.get_context_data(
            form=form,
            skill_form=skill_form,
        ))

    def post(self, request, *args, **kwargs):
        """
        instantiates the form with its inline formsets
        with the passed POST data and validates
        """
        self.object = self.get_object()
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        skill_form = SkillFormSet(self.request.POST)
        if form.is_valid() and skill_form.is_valid():
            return self.form_valid(form, skill_form)
        else:
            return self.form_invalid(form, skill_form)

    def form_valid(self, form, skill_form):
        """
        If both forms are valid then create Job with skills and redirect
        """
        self.object = form.save(commit=False)
        self.object.user = self.request.user
        self.object.save()
        skill_form.instance = self.object
        skill_form.save()
        return HttpResponseRedirect(self.object.get_absolute_url())

    def form_invalid(self, form, skill_form):
        """
        If either of the forms are invalid
        Re-render the page with data-filled forms and errors
        """
        return self.render_to_response(self.get_context_data(form=form, skill_form=skill_form))


#edit template form
<form method="POST">{% csrf_token %}
    <table class="table table-hover">

      {% include "_layouts/custom_field.html" with field=form.title %}
      {% include "_layouts/custom_field.html" with field=form.city %}
      {% include "_layouts/custom_field.html" with field=form.company %}
      {% include "_layouts/custom_field.html" with field=form.start_date %}
      {% include "_layouts/custom_field.html" with field=form.type %}
      {% include "_layouts/custom_field.html" with field=form.remote %}
      {% include "_layouts/custom_field.html" with field=form.contact %}
      {% include "_layouts/custom_field.html" with field=form.description %}

    </table>
    <fieldset>
        <legend>Skills</legend>
        {{ skill_form.management_form }}
        {{ skill_form.non_form_errors }}
        {% for form in skill_form %}
            {{ form.id }}
            <div class="inline {{ skill_form.prefix }}">
                {{ form.skill.errors }}
                {{ form.skill.label_tag }}
                {{ form.skill }}
            </div>
        {% endfor %}
    </fieldset>
    <table class="table table-hover">

      {% include "_layouts/custom_field.html" with field=form.requirements %}
      {% include "_layouts/custom_field.html" with field=form.responsibilities %}
      {% include "_layouts/custom_field.html" with field=form.education %}
      {% include "_layouts/custom_field.html" with field=form.experience %}
      {% include "_layouts/custom_field.html" with field=form.perks %}
      {% include "_layouts/custom_field.html" with field=form.about %}

    </table>
    <input type="submit" value="Edit Job" class="btn btn-primary">
</form>

Brad Rice

unread,
Jan 10, 2014, 9:15:48 AM1/10/14
to django...@googlegroups.com
I'm following that blog entry, too, and getting an error when I try to save: [u'ManagementForm data is missing or has been tampered with']
Reply all
Reply to author
Forward
0 new messages