Django m2m Form speichern „durch“ Tabelle
Frage
Ich habe Probleme in einer m2m Datensicherung, a ‚bis‘ Klassentabelle enthält. Ich möchte alle ausgewählten Elemente speichern (in Form ausgewählt) in der durch Tabelle. Aber ich weiß nicht, wie in der Ansicht die ‚durch‘ Tabelle zu initialisieren.
mein Code:
class Classroom(models.Model):
user = models.ForeignKey(User, related_name = 'classroom_creator')
classname = models.CharField(max_length=140, unique = True)
date = models.DateTimeField(auto_now=True)
open_class = models.BooleanField(default=True)
members = models.ManyToManyField(User,related_name="list of invited members", through = 'Membership')
class Membership(models.Model):
accept = models.BooleanField(User)
date = models.DateTimeField(auto_now = True)
classroom = models.ForeignKey(Classroom, related_name = 'classroom_membership')
member = models.ForeignKey(User, related_name = 'user_membership')
und in der Ansicht:
def save_classroom(request):
classroom_instance = Classroom()
if request.method == 'POST':
form = ClassroomForm(request.POST, request.FILES, user = request.user)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.user = request.user
new_obj.save()
membership = Membership(member = HERE SELECTED ITEMS FROM FORM,classroom=new_obj)
membership.save()
Wie soll ich die Mitgliedschaft initialisieren für die Mitgliedschaft Tabelle besiedeltes Recht sein?
Lösung
Bei normaler m2m Beziehung (nicht über Zwischentabelle) Sie können ersetzen:
membership = Membership(member = HERE SELECTED ITEMS FROM FORM,classroom=new_obj)
membership.save()
mit
form.save_m2m()
Aber im Fall der Verwendung Vermittler Tabellen Sie müssen manuell POST-Daten verarbeiten und erstellen Membership-Objekte mit allen erforderlichen Feldern ( ähnliches Problem ). Die einfachste Lösung ist Ihrer Ansicht nach etwas zu ändern wie:
def save_classroom(request):
if request.method == 'POST':
form = ClassroomForm(request.POST, request.FILES)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.user = request.user
new_obj.save()
for member_id in request.POST.getlist('members'):
membership = Membership.objects.create(member_id = int(member_id), classroom = new_obj)
return HttpResponseRedirect('/')
else:
form = ClassroomForm()
return render_to_response('save_classroom.html', locals())
Beachten Sie, wie request.POST manipuliert wird (.getlist). Dies liegt daran, Post und get sind QueryDict Objekte die einige Auswirkungen hat (request.POST [ 'Mitglieder'] wird immer ein Objekt zurück!).
Sie können diesen Code ändern, um es zuverlässige (Fehlerbehandlung etc.) und ausführliche, zum Beispiel:
member = get_object_or_404(User, pk = member_id)
membership = Membership.objects.create(member = member , classroom = new_obj)
Aber beachten Sie, dass Sie einige db-Abfragen in einer Schleife durchführen, die nicht eine gute Idee, im allgemeinen ist (in Bezug auf die Leistung).
Andere Tipps
Wie, was Dzida tat, aber die Verwendung form.cleaned_data statt request.post:
def save_classroom(request):
if request.method == 'POST':
form = ClassroomForm(request.POST, request.FILES)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.user = request.user
new_obj.save()
for member in form.cleaned_data['members'].all():
Membership.objects.create(member = member, classroom = new_obj)
return HttpResponseRedirect('/')
else:
form = ClassroomForm()
return render_to_response('save_classroom.html', locals())
Sie müssen auch einige Mitgliedschaften betrachten könnten gelöscht werden, so:
def save_classroom(request):
if request.method == 'POST':
form = ClassroomForm(request.POST, request.FILES)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.user = request.user
new_obj.save()
final_members = form.cleaned_data['members'].all()
initial_members = form.initial['members'].all()
# create and save new members
for member in final_members:
if member not in initial_members:
Membership.objects.create(member = member, classroom = new_obj)
# delete old members that were removed from the form
for member in initial_members:
if member not in final_members:
Membership.objects.filter(member = member, classroom = new_obj).delete()
return HttpResponseRedirect('/')
else:
form = ClassroomForm()
return render_to_response('save_classroom.html', locals())
Wenn Sie Vordrucke verwenden (wie in einem allgemeinen CBV: form_class=ClassroomForm
), überschreibt und legen die Sparlogik oben in der save
Methode, so etwas wie:
ClassroomForm(forms.ModelForm):
members = ModelMultipleChoiceField(
queryset=Classroom.objects.all(),
widget=SelectMultiple
)
def save(self, commit=True):
classroom = super().save(commit=False)
if commit:
classroom.save()
if 'members' in self.changed_data:
final_members = self.cleaned_data['members'].all()
initial_members = self.initial['members']
# create and save new members
for member in final_members:
if member not in initial_members:
Membership.objects.create(member = member, classroom = new_obj)
# delete old members that were removed from the form
for member in initial_members:
if member not in final_members:
Membership.objects.filter(member = member, classroom = new_obj).delete()
return classroom
Sie müssen auch die Klassenzimmer für die Mitgliedschaft festlegen:
membership = Membership(member = request.user,
classroom=new_obj) #if new_obj if your classroom
membership.save()
Ich denke, man sollte auch Dies ist, wie ich tat es in einer allgemeinen UpdateForm klassenbasierte Ansicht (django 1.8) für eine ähnliche noch andere Anwendung der form_valid Methode. User
in accept = models.BooleanField(User)
entfernen. Es sollte nicht notwendig sein, um das Datum einzustellen beim Speichern, wenn Sie auto_now
verwenden! Aber vielleicht `auto_now_add ist wahrscheinlicher, was Sie brauchen (
def form_valid(self, form):
"""
If the form is valid, save the associated model.
"""
self.object.members.clear()
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
list_of_members = form.cleaned_data['members']
ClassRoom.objects.bulk_create([
Membership(
Course=self.object,
member=member_person,
order=num)
for num, member_person in enumerate(list_of_members)
])
return super(ModelFormMixin, self).form_valid(form)