2010-06-19 9 views
15

Mam problem z zapisaniem danych w formacie m2m, zawierających tabelę klasy "przez". Chcę zapisać wszystkich wybranych członków (wybranych w formularzu) w przelotowej tabeli. Ale nie wiem, jak zainicjować tabelę "przez" w widoku.Formularz Django m2m zapisz tabelę "przez"

mój kod:

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

aw widzenia:

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

Jak należy zainicjowanie członkostwa dla tabeli Membership być wypełniane w porządku?

Odpowiedz

19

W przypadku korzystania z normalną m2m relację (nie poprzez tabeli pośredniczącej) można wymienić:

membership = Membership(member = HERE SELECTED ITEMS FROM FORM,classroom=new_obj) 
membership.save() 

z

form.save_m2m() 

ale w przypadku korzystania z pośredniczących tabel należy ręcznie obsługiwać dane POST i tworzyć obiekty członkowskie z wszystkimi wymaganymi polami (similar problem). Najbardziej podstawowym rozwiązaniem jest zmiana widoku na:

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

Zobacz, jak request.POST jest manipulowany (.getlist). Dzieje się tak, ponieważ post i get są obiektami, które mają pewne implikacje (request.POST ['members'] zwróci zawsze jeden obiekt!).

Można zmodyfikować ten kod, aby uzyskać bardziej wiarygodne (obsługa błędów itd.), A bardziej gadatliwy, np:

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

Należy jednak pamiętać, że wykonujesz kilka zapytań db w pętli, która nie jest dobry pomysł w ogóle (pod względem wydajności).

+0

to było to! To naprawdę stworzyło mi pewne bóle głowy, ponieważ jestem całkiem nowy dla Pythona. Thnx dużo! – dana

+0

Pytanie: Jeśli chcę dodać twórcę klasy jako członka (required.user) i do tej listy, jak mogę to zrobić? Dzięki! – dana

+1

Nie jestem pewien, czy rozumiem. Możesz utworzyć nowy obiekt członkowski (poza pętlą), aby utworzyć nowe połączenie między użytkownikiem a klasą (członkostwo = Membership.objects.create (member = request.user, classroom = new_obj) ) lub możesz przekazać dodatkowe member_id (czyli id request.user) w POST, a relacja user-classroom zostanie utworzona przy użyciu istniejącego kodu (dla pętli). W przypadku drugiej opcji należy zapisać plik request.user.id w ukrytym polu w formularzu. – dzida

1

Należy również określić klasę do członkostwa:

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

Chyba należy również usunąć User w accept = models.BooleanField(User). Nie trzeba ustawiać daty po zapisaniu, jeśli używasz auto_now! Ale może `auto_now_add bardziej prawdopodobne jest to, czego potrzebujesz (http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField)

+0

dzięki wydaje się naturalne rozwiązanie, problemem jest to, że new_obj - moja klasa nie jest jeszcze utworzony, chyba. teraz dodałem powyższą deklarację po new_obj.save(), a nowy błąd to: Nie można ustawić wartości na ManyToManyField, który określa model pośredniczący. Zamiast tego użyj Menedżera członkostwa. :) – dana

+0

Spróbuj z Membership.objects.create(), ale nie zapomnij podać obiektu classromm! –

+0

Przepraszamy za ponowne zgłoszenie pytania sprzed lat, ale co, jeśli chcesz zaktualizować, a nie utworzyć rekord? Jeśli rekord już istnieje, nie obiektuje.tworzy dodać nowy rekord zamiast aktualizacji? – nlr25

0

Tak właśnie zrobiłem w ogólnym widoku opartym na klasie UpdateForm (django 1.8) dla podobnej, ale innej aplikacji przy użyciu metody form_valid.

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

Tak jak dzida, ale zamiast tego użyj form.cleaned_data.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()) 

Należy również rozważyć niektóre członkostwo może zostać usunięty, więc:

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

Jeśli używasz wzorów formularzy (jak w ogólnej CBV: form_class=ClassroomForm), przesłanianie i umieścić logikę oszczędzania powyżej w metodzie save, coś jak:

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 
Powiązane problemy