I have a form which is intended to add a number of sections to a project. I am using a CheckboxSelectMultiple with a list of choices restricted to only those that have not previously been selected.
So with a new empty project with no sections:
Project.sections.all() = null
choices = [(1, 'a'),(2, 'b'),(3, 'c')]
First time the form is submitted, adding sections a & b.
Project.sections.all() = a, b
choices = [(3, 'c')]
Second time the form is submitted, adding section c.
Project.sections.all() = c
choices = [(1, 'a'),(2, 'b')]
What I instead want is for c to be added onto the existing list of values for the project.
models.py
class Project(models.Model):
number = models.CharField(max_length=4, unique=True)
name = models.CharField(max_length=100)
sections = models.ManyToManyField(Section)
class Section(models.Model):
code = models.CharField(max_length=2)
description = models.CharField(max_length=50)
views.py
def add_section(request, project_number):
project = Project.objects.get(number=project_number)
full_section_list = Section.objects.all()
project_assigned_sections = project.sections.all().values_list('id', flat=True)
choices = list(full_section_list.exclude(pk__in=project_assigned_sections).values_list('id', 'code'))
if request.method == 'POST':
form = AddSectionForm(choices, request.POST, instance=project)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse("project-page", args=(project_number)))
else:
print("invalid")
else:
form = AddSectionForm(choices, instance=project)
return render(request, "app/add_section.html", {
"project": project,
"form": form,
"choices": choices
})
forms.py
class AddSectionForm(forms.ModelForm):
def __init__(self, choices, *args, **kwargs):
super(AddSectionForm, self).__init__(*args, **kwargs)
self.fields['sections'] = forms.MultipleChoiceField(
widget=forms.CheckboxSelectMultiple,
required=False,
choices=choices
)
class Meta:
model = Project
fields = ['sections']
CodePudding user response:
I assume that Django is doing what it's intended to do ... so, work around it.
if request.method == 'POST':
previous_sections = list( project.sections.values_list( 'pk', flat=True) )
form = AddSectionForm(choices, request.POST, instance=project)
if form.is_valid():
new = form.save( commit = False)
new.sections.add( previous_sections ) # make sure old ones are never removed
new.save()
return HttpResponseRedirect(reverse("project-page", args=(project_number)))
else:
print("invalid")
CodePudding user response:
else:
form = AddSectionForm()
dont pass instance just keep it empty form
CodePudding user response:
Ok this works and seems a bit nicer (I'm not mapping integers to the returned list anymore).
data = form.cleaned_data.get("sections") #get the list of id's from the form
sections = Section.objects.filter(pk__in=data) #gets the objects from those id's
for section in sections: #iterate through them, adding to the project.
project.sections.add(section)