I have a view which creates and then saves a new object. Then I want to save the object’s m2m relationship. However, this gives an error. If I remove the m2m save in the view, I can manually save m2m relationship later.
Playing around with this, and shown in the code below, I now have the view create and save Student
object working because the m2m save classblock.students.add(new_student)
is commented out. I then run this view again, and since the student objects are already created, the view executes the classblock.students.add(new_student)
in the else
case, and this works. The existing students get added to the classblock m2m relationship.
Is there some kind of timing issue with when you first save an object, and then add it to a m2m relationship? How do I work around this?
models
class Student(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, primary_key=True)
student_first = models.CharField(max_length=30)
student_last = models.CharField(max_length=30)
nickname = models.CharField(max_length=31)
fullname = models.CharField(max_length=60)
attend = models.BooleanField(default=True)
do_not_pick = models.BooleanField(default=False)
student_number = models.IntegerField()
email = models.EmailField(max_length=50)
class Classroom(models.Model):
"""The gradebook is split into courses, classes and students"""
classroom_name = models.CharField(max_length=10)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
students = models.ManyToManyField(Student)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
view
def addmultistudent(request, classroom_id):
"""Add multiple students at once."""
classblock = get_object_or_404(Classroom, pk=classroom_id)
context = {'classblock': classblock}
if request.method == 'POST':
form = StudentInputForm(request.POST)
if form.is_valid():
s = form.save(commit=False)
input_list = []
input_list = s.name_list.split('\n')
# the for returns a list of students with student number, fisrt_name, last_name in each line
for line in input_list:
# remove the carriage return
line = line.strip('\r')
# removes double quotes
line = line.translate({ord(c): None for c in '"'})
# ignore the first line which is a header starting with "Pupil"
if not line.startswith("Pupil"):
# get rid of the commas from the csv file
all_names = line.split(",")
# pop the studnet number
sn = all_names.pop(0)
# make sure the student not already been added
if not Student.objects.filter(student_number=sn).exists():
first = all_names.pop(0)
last = ""
for name in all_names:
last = last + name + " "
last = last[:-1]
# create the object and then add the attributes
email_add = sn + "@bc.ca"
nick = first + last[0]
full = first + " " + last
new_student = Student(student_last=last, student_first=first,
student_number=sn, nickname=nick, fullname=full, email=email_add)
print(new_student)
print(classblock)
new_student.save()
# classblock.students.add(new_student)
# attach student to a user
if not User.objects.filter(username=sn):
user = User.objects.create_user(sn, email_add)
user.last_name = last
user.first_name = first
user.save()
# if the student has been added, we just need to add them to the new classroom
else:
new_student = Student.objects.get(student_number=sn)
classblock.students.add(new_student)
form = StudentInputForm(None)
context['form'] = form
return render(request, "gradebook/addmultistudent.html", context)
else:
context['form'] = form
return render(request, "gradebook/addmultistudent.html", context)