Reputation: 1860
I have two models with ManyToMany relationship as this:
class Subject(models.Model):
teachers = models.ManyToManyField(
Teacher
)
subject = models.CharField(
'Materia',
max_length = 50,
unique = True
)
level = models.CharField(
'Nivel',
max_length = 3,
choices = LEVEL_CHOICES
)
class Teacher(Person):
# fields definition
Following the docs I know I can save Subject model without saving Teacher field. That's fine.
Now I want to add a Subject to Teacher, so I need a form with selected teacher, and a choice field with all the subjects.
I found this question that relates to what I want to accomplish, but I think it doesn't address what I'm looking for.
First I populate the subject choice filed, as shown in the ModelForm code below.
forms.py
class SubjectTeacherForm(ModelForm):
subject = forms.ChoiceField(choices=[(m.id, m.subject) for m in Subject.objects.all()])
class Meta:
model = Subject
fields = ['teachers', 'subject']
urls.py
#here I send the teacher id to the view
url(r'^materias/asignar/profesor/(?P<teacher_id>\d+)/$',MateriaTeacherCreateView.as_view(), name='materia-create-teacher'),
views.py
class SubjectTeacherCreateView(SuccessMessageMixin, CreateView):
model = Subject
template_name = 'edu/subject/subject_create.html'
form_class = SubjectTeacherForm
def form_valid(self, form):
# here I want to add the relation
teacher = Teacher.objects.get(pk=self.kwargs['teacher_id'])
# I understad that object here is the ModelForm, not the Model, so this is wrong
self.object.teachers.add(teacher)
return redirect(self.get_success_url())
def get_context_data(self, **kwargs):
context = super(SubjectTeacherCreateView, self).get_context_data(**kwargs)
# here I define the selected teacher to pass to the template
context['teacher'] = Teacher.objects.get(pk=self.kwargs['teacher_id'])
return context
the template
<form method="post" class="form-horizontal">
{% csrf_token %}
{{ form.teacher }}
{{ form.subject }}
As expected, this is not working, the form doesn't save, it throws the error:
AttributeError at /edu/materias/asignar/profesor/11/
'NoneType' object has no attribute 'teachers'
So, obviously I'm doing something wrong.
My question is if I can use class based views to accomplish this, or I should write a method to save the relationship, and what should I change in my code.
I expect I made myself clear enough, thanks.
Upvotes: 1
Views: 1248
Reputation: 7717
you need call super(SubjectTeacherCreateView, self).form_valid(form)
in your form_valid
then you can get self.object
,this is source form_vaild
method before you override:
def form_valid(self, form):
"""
If the form is valid, save the associated model.
"""
self.object = form.save()
return super(ModelFormMixin, self).form_valid(form)
but as your condition ,this is the right way:
def form_valid(self, form):
try:
subject = Subject.object.get(id=form.cleaned_data['subject'])
teacher = Teacher.objects.get(pk=self.kwargs['teacher_id'])
subject.teachers.add(teacher)
except Subject.DoesNotExist:
# handle it youself
return redirect(self.get_success_url()
Upvotes: 3
Reputation: 4194
I think you need a specific get_object()
to find the object of subject
from the teacher_id
.
def get_object(self):
teacher_id = self.kwargs['teacher_id']
subject = get_object_or_404(self.model, teachers__id=teacher_id)
return subject
and also, if self.object.teachers.add(teacher)
doesn't work, try to assign as a list self.object.teachers.add([teacher])
.
Upvotes: 0