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>