I have a Formset, where one of the fields is a multi-select checkbox, with the options available being determined by a foreign key relationship (to the business model; the form takes business as an arguement). This works, however upon submission of the Formset, nothing is saved to the database despite a POST request and redirect taking place (I would expect it to create an instance or instances of the Instructors model).
My models.py (including only relevant models):
class Instructors(models.Model):
uid = models.CharField(verbose_name="Unique ID", max_length=255, primary_key=True)
business = models.ForeignKey(Business, blank=False, on_delete=models.CASCADE)
first_name = models.CharField(verbose_name="First Name", max_length=255, blank=False)
surname = models.CharField(verbose_name="Surname", max_length=255, blank=False)
activities = models.ManyToManyField(Activity, blank=False)
def __str__(self):
return str(self.uid)
class Business(models.Model): # if is_business on profile model = true, then get access to create Business Profile
business_name = models.CharField(verbose_name='Business Name', max_length=255)
business_description = models.TextField(verbose_name='Business Description', max_length=500)
slug = models.SlugField(verbose_name='Slug', max_length=250, null=True, blank=True, unique=True)
business_contact_number = models.CharField(verbose_name='Business Contact Number', max_length=32)
business_email = models.EmailField(verbose_name='Business Email')
business_profile_image = models.ImageField(verbose_name="Profile Picture", null=True, blank=True, upload_to='business_images/')
creation_date = models.DateTimeField(auto_now_add=True)
address = models.CharField(verbose_name="Street Address", max_length=100, null=True, blank=True)
town = models.CharField(verbose_name="Town/City", max_length=100, null=True, blank=True)
county = models.CharField(verbose_name="County", max_length=100, null=True, blank=True)
post_code = models.CharField(verbose_name="Post Code", max_length=8, null=True, blank=True)
country = models.CharField(verbose_name="Country", max_length=100, null=True, blank=True)
longitude = models.CharField(verbose_name="Longitude", max_length=50, null=True, blank=True)
latitude = models.CharField(verbose_name="Latitude", max_length=50, null=True, blank=True)
activities = models.ManyToManyField(Activity, blank=True)
def __str__(self):
return str(self.business_name)
class Activity(models.Model):
""" List of all types of activity which are on the platform """
title = models.CharField(max_length=255, primary_key=True, unique=True)
tags = models.ManyToManyField(Tag, null=True, blank=True) # there is a problem with this
slug = models.SlugField(verbose_name='Slug', max_length=250, null=True, blank=True, unique=True)
activity_image = models.ImageField(verbose_name="Activity Image", blank=True, null=True, upload_to="activity_images/")
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
My forms.py:
class AddInstructorForm(forms.ModelForm):
""" A form for adding instructors. """
activities = forms.ModelMultipleChoiceField(queryset=None, widget=forms.CheckboxSelectMultiple(attrs={'name': 'activities'}))
def __init__(self, business, *args, **kwargs):
super(AddInstructorForm, self).__init__(*args, **kwargs)
business_activities = Business.activities.through.objects.filter(business_id=business).values('activity')
self.fields['activities'].queryset = business_activities.values_list('activity', flat=True)
class Meta:
model = Instructors
fields = ['first_name', 'surname', 'activities']
widgets = {
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
'surname': forms.TextInput(attrs={'class': 'form-control'})
}
AddInstructorFormSet = formset_factory(AddInstructorForm, extra=0)
My views.py:
class AddInstructorView(View, RandomStringMixin):
template_name = 'businesshub/add_instructor.html'
def get(self, request, *args, **kwargs):
business = self.request.user.profile.business.id
formset = AddInstructorFormSet(form_kwargs={'business': business})
context = {
'formset': formset,
'slug': self.kwargs['slug'],
}
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
business = self.request.user.profile.business
formset = AddInstructorFormSet(request.POST, form_kwargs={'business': business})
if formset.is_valid():
instances = []
for form in formset:
form.uid = str(business.id) + '_' + self.get_string(6, 6)
instructor = form.save(commit=False)
instructor.business = business
instances.append(instructor)
Instructors.objects.bulk_create(instances)
user = self.request.user
slug = user.profile.business.slug
return redirect(reverse('businesshub:business_hub', kwargs={'slug': slug}))
else:
print("FORM IS NOT VALID")
context = {
'formset': formset,
'slug': self.kwargs['slug'],
}
return render(request, self.template_name, context)
My html template:
{% extends "base.html" %}
{% block content %}
<h1>Add Instructor</h1>
<div class="container">
<form method="POST" element="multipart/form-data">
{% csrf_token %}
<div id="formset-container">
{{ formset.management_form }}
{% for form in formset %}
<div class="formset-row">
<div class="mb-3">
<label for="title">First Name:</label>
{{ form.first_name }}
</div>
<div class="mb-3">
<label for="title">Surname:</label>
{{ form.surname }}
</div>
<div class="mb-3">
<label for="title">Select all activities this person can instruct on:</label>
{{ form.activities }}
</div>
</div>
{% endfor %}
</div>
<button id="add-form" type="button" class="btn btn-secondary mb-3">Add Instructor</button>
<div class='mb-5'>
<a class='btn btn-secondary' href='{% url "businesshub:business_hub" slug=slug %}' role="button">Finish later</a>
<button type='submit' class='btn btn-primary'>Next</button>
</div>
</form>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
// Counter to track the number of form instances
var formCount = {{ formset.total_form_count }};
// Function to add a new form instance
function addForm() {
var formTemplate = $('#form-template').html();
var formsetContainer = $('#formset-container');
var newForm = formTemplate.replace(/__prefix__/g, formCount);
formsetContainer.append(newForm);
formCount++;
}
// Bind click event to the "Add Instructor" button
$('#add-form').click(function() {
addForm();
});
// Add initial form instances
{% for form in formset %}
addForm();
{% endfor %}
});
</script>
<script type="text/template" id="form-template">
<div class="formset-row">
<div class="mb-3">
<label for="title">First Name:</label>
{{ formset.empty_form.first_name }}
</div>
<div class="mb-3">
<label for="title">Surname:</label>
{{ formset.empty_form.surname }}
</div>
<div class="mb-3">
<label for="title">Select all activities this person can instruct on:</label>
{{ formset.empty_form.activities }}
</div>
</div>
</script>
{% endblock %}