2010-06-19 21 views
15

Ich habe Probleme beim Speichern eines m2m Daten, enthält eine 'durch' Klassen-Tabelle. Ich möchte alle ausgewählten Mitglieder (ausgewählt in dem Formular) in der Durch-Tabelle speichern. Aber ich weiß nicht, wie man die 'durch' Tabelle in der Ansicht initialisiert.Django m2m Formular speichern "durch" Tabelle

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 sollte ich die Mitgliedschaft für die Mitgliedschaft Tabelle initialisieren Recht auf bevölkert?

Antwort

19

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 von Zwischentabellen müssen Sie POST-Daten manuell verarbeiten und Mitgliedschaftsobjekte mit allen erforderlichen Feldern erstellen (similar problem). Die einfachste Lösung ist Ihrer Ansicht nach so etwas wie zu ändern:

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()) 

Hinweis, wie request.POST wird (.getlist) manipuliert. Das liegt daran, dass post und get QueryDict Objekte sind, was einige Auswirkungen hat (request.POST ['members'] gibt immer ein Objekt zurück!).

Sie diesen Code ändern können, um es zuverlässiger (Fehlerbehandlung etc.) und ausführlicher, zB:

member = get_object_or_404(User, pk = member_id) 
membership = Membership.objects.create(member = member , classroom = new_obj) 

Aber beachten Sie, dass Sie in einer Schleife einige db-Abfragen ausführen ein, die nicht gute Idee im Allgemeinen (in Bezug auf die Leistung).

+0

das war es! Es hat mir wirklich Kopfschmerzen bereitet, da ich neu bei Python bin. Thnx viel! – dana

+0

Frage: Wenn ich den Ersteller des Klassenraums als Mitglied hinzufügen möchte (der required.user), und zu dieser Liste, wie kann ich das tun? Danke! – dana

+1

Nicht sicher, ob ich Ihren Standpunkt verstanden habe. Sie können entweder ein neues Mitgliedschaftsobjekt (außerhalb der Schleife) erstellen, um eine neue Verbindung zwischen Benutzer und Klassenraum zu erstellen (membership = Membership.objects.create (member = request.user, classroom = new_obj) ) oder Sie können extra member_id übergeben (was ist ID von request.user) in POST und relation user-classroom wird unter Verwendung des vorhandenen Codes (for loop) erstellt. Für die zweite Option müssen Sie request.user.id in einem versteckten Feld in Ihrem Formular speichern. – dzida

1

Sie müssen auch die Klassenzimmer für die Mitgliedschaft spezifizieren:

membership = Membership(member = request.user, 
         classroom=new_obj) #if new_obj if your classroom 
membership.save() 

Ich denke, Sie auch User in accept = models.BooleanField(User) entfernen sollte. Es sollte nicht notwendig sein, das Datum beim Speichern zu setzen, wenn Sie auto_now verwenden! Aber vielleicht `auto_now_add ist wahrscheinlicher, was Sie brauchen (http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField)

+0

dank es die natürliche Lösung scheint, ist das Problem, dass new_obj - meine Klasse noch nicht erstellt ist, denke ich. Jetzt habe ich die obige Deklaration nach new_obj.save() hinzugefügt, und der neue Fehler ist: Kann Werte auf einem ManyToManyField, das ein intermediäres Modell angibt, nicht festlegen. Verwende stattdessen den Mitglieder-Manager. :) – dana

+0

Versuchen Sie es mit Membership.objects.create(), aber vergessen Sie nicht, das classromm-Objekt anzugeben! –

+0

Es tut uns leid, eine Frage von vor Jahren erneut zu stellen, aber was, wenn Sie aktualisieren möchten, anstatt einen Datensatz zu erstellen? Wenn ein Datensatz bereits existiert, fügt objects.create keinen neuen Datensatz hinzu, anstatt zu aktualisieren? – nlr25

0

So habe ich es in einer generischen klassenbasierten UpdateForm-Sicht (django 1.8) für eine ähnliche, aber andere Anwendung mit der Methode form_valid gemacht.

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) 
1

Wie was dzida tat, aber form.cleaned_data anstelle von Anfrage verwenden.Beitrag:

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, so gelöscht werden:

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), überschreiben und die Sparlogik setzen 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 = 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 classroom