2012-09-01 29 views
5

Tengo una relación M2M entre dos modelos que usa un modelo intermedio. Por el bien de la discusión, vamos a utilizar el ejemplo del manual:Vistas basadas en clases para la relación M2M con el modelo intermedio

class Person(models.Model): 
    name = models.CharField(max_length=128) 

    def __unicode__(self): 
     return self.name 

class Group(models.Model): 
    name = models.CharField(max_length=128) 
    members = models.ManyToManyField(Person, through='Membership') 

    def __unicode__(self): 
     return self.name 

class Membership(models.Model): 
    person = models.ForeignKey(Person) 
    group = models.ForeignKey(Group) 
    date_joined = models.DateField() 
    invite_reason = models.CharField(max_length=64) 

me gustaría hacer uso de puntos de vista basados ​​en la clase de Django, para evitar escribir vistas CRUD-manipulación. Sin embargo, si trato de utilizar el CreateView defecto, que no funciona:

class GroupCreate(CreateView): 
    model=Group 

Esto hace que un formulario con todos los campos en el objeto de grupo, y le da una caja de selección múltiple para el campo de los miembros, lo cual sería correcto para una simple relación M2M. Sin embargo, no hay forma de especificar la fecha_unidada o el motivo_invitado, y al enviar el formulario se obtiene el siguiente AttributeError:

"No se pueden establecer valores en ManyToManyField que especifique un modelo intermediario. En su lugar, use el Administrador de Membresía".

¿Existe alguna manera de sobrescribir parte del CreateView genérico o redactar mi propia vista personalizada para hacer esto con mixins? Parece que esto debería ser parte del marco, ya que la interfaz de administración maneja las relaciones M2M con intermedios utilizando inlines.

+0

posible duplicado de [django No se pueden establecer valores en ManyToManyField que especifica un modelo intermediario. Use Manager en su lugar] (http://stackoverflow.com/questions/3091328/django-cannot-set-values-on-a-manytomanyfield-which-specifies-an-intermediary-mo) – juliocesar

Respuesta

6

debe extender CreateView:

from django.views.generic import CreateView 

class GroupCreate(CreateView): 
    model=Group 

y anular la form_valid():

from django.views.generic.edit import ModelFormMixin 
from django.views.generic import CreateView 

class GroupCreate(CreateView): 
    model = Group 

    def form_valid(self, form): 
     self.object = form.save(commit=False) 
     for person in form.cleaned_data['members']: 
      membership = Membership() 
      membership.group = self.object 
      membership.person = person 
      membership.save() 
     return super(ModelFormMixin, self).form_valid(form) 

como dice el documentation, debe crear nueva membership s para cada relación entre group y person.

vi la anulación form_valid aquí: Using class-based UpdateView on a m-t-m with an intermediary model

+0

No he tenido la oportunidad de probar esto (y terminé yendo por una ruta diferente), pero parece el método correcto. ¡Aclamaciones! – Symmetric

+0

Esto no te da ninguna posibilidad de ingresar 'date_joined' y' invite_reason' ... simplemente los guarda vacíos ... –

0

Hace un par de días me enfrentaba el mismo problema. Django tiene problemas para procesar las relaciones intermedias m2m.

Esta es la soluciones de lo que he encontrado útil:

1. Define new CreateView 
class GroupCreateView(CreateView): 
    form_class = GroupCreateForm 
    model = Group 
    template_name = 'forms/group_add.html' 
    success_url = '/thanks' 

Entonces modificar el método guardar de forma definida - GroupCreateForm. Save es responsable de hacer cambios permanentes a DB. Yo no era capaz de hacer este trabajo sólo a través de ORM, por lo que he usado SQL prima también:

1. Define new CreateView 
class GroupCreateView(CreateView): 


class GroupCreateForm(ModelForm): 
    def save(self): 
     # get data from the form 
     data = self.cleaned_data 
     cursor = connection.cursor() 
     # use raw SQL to insert the object (in your case Group) 
     cursor.execute("""INSERT INTO group(group_id, name) 
          VALUES (%s, %s);""" (data['group_id'],data['name'],)) 
     #commit changes to DB 
     transaction.commit_unless_managed() 
     # create m2m relationships (using classical object approach) 
     new_group = get_object_or_404(Group, klient_id = data['group_id']) 
     #for each relationship create new object in m2m entity 
     for el in data['members']: 
      Membership.objects.create(group = new_group, membership = el) 
     # return an object Group, not boolean! 
     return new_group 

Nota: He cambiado el modelo un poco, como se puede ver (tengo propio y único IntegerField para la clave principal, no usar en serie Esa es la forma en que se metió en get_object_or_404

+0

Esta respuesta no ayuda mucho. ¿Puedes mostrar tu modelo? Cambiar el pk de "id" a "group_id" no tiene mucho sentido. – Timo

0

'para tener una referencia, yo no las usamos una visión basada en la clase, en vez hice algo como esto:.

def group_create(request): 
    group_form = GroupForm(request.POST or None) 
    if request.POST and group_form.is_valid(): 
     group = group_form.save(commit=False) 
     membership_formset = MembershipFormSet(request.POST, instance=group) 
     if membership_formset.is_valid(): 
      group.save() 
      membership_formset.save() 
      return redirect('success_page.html') 
    else: 
     # Instantiate formset with POST data if this was a POST with an invalid from, 
     # or with no bound data (use existing) if this is a GET request for the edit page. 
     membership_formset = MembershipFormSet(request.POST or None, instance=Group()) 

    return render_to_response(
     'group_create.html', 
     { 
      'group_form': recipe_form, 
      'membership_formset': membership_formset, 
     }, 
     context_instance=RequestContext(request), 
    ) 

Este puede ser un punto de partida para una implementación basada en clases, pero es tan simple que no ha sido w Orth mi tiempo para tratar de calzador esto en el paradigma basado en la clase.

0

Solo un comentario, cuando se utiliza CBV es necesario guardar el formulario con cometer = verdad, por lo que se crea el grupo y se le da un identificador que se puede utilizar para crea las membresías. De lo contrario, con commit = False, el objeto de grupo aún no tiene una identificación y se ha producido un error.

+0

¿Tienes alguna posibilidad de obtener más información sobre esto ...? Siguiendo la respuesta aceptada, la única forma en que puedo acceder al código para guardar la 'Membresía' es usando 'commit = False', de lo contrario arroja el 'AttributeError' que se muestra en la pregunta. De hecho, incluso si trato de usar 'form.save()' después de guardar la 'Membresía', todavía obtengo ese' AttributeError'. Sin embargo, como dices, no hay 'id' para' group', por lo que 'Membership' no se guarda correctamente de todos modos ... –

+0

Lo que quise decir es que la solución propuesta no iba a funcionar, porque como lo verificaste , la instancia del grupo aún no se ha guardado en la base de datos, por lo que no tiene una identificación. Lo que tienes que hacer es crear un nuevo grupo solo con un nombre y guardarlo (commit = True). Ahora tiene una identificación y puede crear nuevos objetos de Membresía. Sobre el error, no probé el código, así que no sé la razón exacta. Tal vez, el formulario autogenerado incluya un campo obligatorio con miembros ... ¿Tiene más detalles sobre esto? – kiril

+0

La creación de un nuevo grupo con una identificación nueva parece ser buena, pero ¿cómo puedo ingresar la fecha_unidada y la invitación_razón como dijo Zad-Man? Tiene a alguien una solución completa con vista, modelo. Creo que podría ser útil ver cómo la resolvió la aplicación "admin". – Timo

2
class GroupCreate(CreateView): 
    model = Group 

    def form_valid(self, form): 
     self.object = form.save(commit=False) 

     ### delete current mappings 
     Membership.objects.filter(group=self.object).delete() 

     ### find or create (find if using soft delete) 
     for member in form.cleaned_data['members']: 
      x, created = Membership.objects.get_or_create(group=self.object, person=member) 
      x.group = self.object 
      x.person = member 
      #x.alive = True # if using soft delete 
      x.save() 
     return super(ModelFormMixin, self).form_valid(form) 
Cuestiones relacionadas