Pregunta

Tengo problemas en el ahorro de un conjunto de datos M2M, que contiene a 'a' mesa de clase. Quiero salvar a todos los miembros seleccionados (seleccionados en forma) en el medio mesa. Pero no sé cómo inicializar el 'a través de' tabla de la vista.

mi código:

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

y en la vista:

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

¿Cómo debo inicializar los miembros de la mesa de miembro de tener la razón poblada?

¿Fue útil?

Solución

En el caso de usar relación M2M normal (no a través de la mesa intermediario) podría reemplazar:

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

con

form.save_m2m()

Sin embargo, en caso de utilizar tablas intermedias que necesita para manejar manualmente los datos POST y crear objetos de miembro con todos los campos requeridos ( problema similar ). La solución más básica es la de cambiar la vista a algo como:

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

Tenga en cuenta cómo se manipula request.POST (.getlist). Esto se debe colocar objetos y obtener son QueryDict cuales tiene algunas implicaciones (request.POST [ '] miembros devolverá siempre un objeto!).

Se puede modificar este código para conseguir que sea más fiable (error de manipulación, etc.), y más detallado, por ejemplo:

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

Sin embargo, nota que está llevando a cabo algunas consultas de base de datos en un bucle que no es una buena idea en general (en términos de rendimiento).

Otros consejos

Al igual que lo hizo Dzida, pero el uso form.cleaned_data en lugar de 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())

También es necesario tener en cuenta algunos de miembro se pueden borrar, así:

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

Si utiliza formularios modelo (como en un VBC genérica: form_class=ClassroomForm), anulación y poner la lógica de ahorro anteriormente en el método save, algo como:

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

También deberá especificar la clase de la membresía:

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

supongo que también debe quitar User en accept = models.BooleanField(User). No debería ser necesario ajustar la fecha al guardar si está utilizando auto_now! Pero tal vez `auto_now_add es más probable que lo que necesita ( http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField )

Esto es cómo lo hice en una vista basada en la clase UpdateForm genérico (django 1.8) para una aplicación similar pero diferente utilizando el método 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)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top