Quais são os passos para fazer um trabalho de ModelForm com uma relação ManyToMany com um modelo intermediário no Django?

votos
30
  • Eu tenho um cliente e Groupe Modelo.
  • Um cliente pode ser parte de vários grupos .
  • Os clientes que fazem parte de um grupo pode usar taxa de aluguer livre do seu grupo a qualquer momento, mas apenas uma vez. É aí que o modelo intermediário ( ClientGroupe ) entra com que os dados extras.

Por agora, quando eu tentar salvar os dados m2m, apenas morre e diz que eu deveria usar o Gerenciador de ClientGroupe ... então o que está faltando?

Aqui estão os meus modelos:

class Groupe(models.Model):
    nom = models.CharField(max_length=1500, blank=True)

class Client(models.Model):
    nom = models.CharField(max_length=450, blank=True)
    prenom = models.CharField(max_length=450, blank=True)
    groupes = models.ManyToManyField(Groupe, null = True, blank = True, through='ClientGroupe')

class ClientGroupe(models.Model):
    client = models.ForeignKey(Client)
    groupe = models.ForeignKey(Groupe)
    dt = models.DateField(null=True, blank=True) # the date the client is using its group's free rental rate    

    class Meta:
        db_table = u'clients_groupes'

e aqui está o meu ponto de vista:

def modifier(request, id):
    client = Client.objects.get(id=id)    
    form = ClientForm(instance = client)

    dict = {
        form: form
        , instance : client
    }

    if request.method == POST:
        form = ClientForm(request.POST, instance = client)

        if form.is_valid():
            client_mod = form.save()

            id = client_mod.id
            return HttpResponseRedirect(
                /client/%(id)s/?err=success % {id : id}
            )
        else:
            return HttpResponseRedirect(
                /client/%(id)s/?err=warning % {id : id}
            )

    return render_to_response(
        client/modifier.html
        , dict
        , context_instance=RequestContext(request)
    )

EDIT :

e aqui está o código ClientForm:

class ClientForm(ModelForm):
    class Meta:
        model = Client

EDIT # 2 : aqui está a mensagem de erro:

AttributeError at /client/445/

Cannot set values on a ManyToManyField which specifies an intermediary model. Use ClientGroupe's Manager instead.

Request Method:     POST
Request URL:    http://localhost/client/445/
Exception Type:     AttributeError
Exception Value:    Cannot set values on a ManyToManyField which specifies an intermediary model.  Use ClientGroupe's Manager instead.

Exception Location:     C:\Python25\lib\site-packages\django\db\models\fields\related.py  in __set__, line 574
Python Executable:  C:\xampp\apache\bin\apache.exe
Python Version:     2.5.2
Publicado 22/12/2008 em 23:58
fonte usuário
Em outras línguas...                            


5 respostas

votos
18

Se você usar o método salvar agora, Django vai tentar salvar usando o gerente (que Django não permite). Infelizmente, o comportamento que você quer é um pouco mais complicado do que o que ModelFormfaz por padrão. O que você precisa fazer é criar um formset .

Primeiro de tudo, você vai precisar alterar as opções do seu ClientFormpara que ele não exibe o groupesatributo.

class ClientForm(ModelForm):
    class Meta:
        model = Client
        exclude = ('groupes',)

Em seguida, você deve alterar a vista para exibir o formset:

from django.forms.models import inlineformset_factory

def modifier(request, id):
    client = Client.objects.get(id=id)    
    form = ClientForm(instance = client)
    # Create the formset class
    GroupeFormset = inlineformset_factory(Client, Groupe)
    # Create the formset
    formset = GroupeFormset(instance = client)

    dict = {
        "form": form
        , "formset" : formset
        , "instance" : client
    }

    if request.method == "POST":
        form = ClientForm(request.POST, instance = client)
        formset = GroupeFormset(request.POST, instance = client)

        if form.is_valid() and formset.is_valid():
            client_mod = form.save()
            formset.save()

            id = client_mod.id
            return HttpResponseRedirect(
                "/client/%(id)s/?err=success" % {"id" : id}
            )
        else:
            return HttpResponseRedirect(
                "/client/%(id)s/?err=warning" % {"id" : id}
            )

    return render_to_response(
        "client/modifier.html"
        , dict
        , context_instance=RequestContext(request)
    )

E, obviamente, você também deve ajustar o seu modelo para renderizar o formset.

Se você precisar de qualquer outra conselhos sobre formsets, consulte estes artigos:

Modelo formsets
Formsets

Respondeu 31/05/2009 em 16:27
fonte usuário

votos
10


if form.is_valid():
    client_mod = form.save(commit=False)
    client_mod.save()
    for groupe in form.cleaned_data.get('groupes'):
        clientgroupe = ClientGroupe(client=client_mod, groupe=groupe)
        clientgroupe.save()
Respondeu 07/10/2010 em 15:41
fonte usuário

votos
4

Você provavelmente precisará remover o campo ManyToMany do seu modelo de cliente, ou então cuidadosamente excluí-lo da sua forma. Infelizmente, o widget padrão para o campo ManyToMany não pode preencher o Modelo ClientGroupe corretamente (mesmo se o campo faltando, dt, tinha sido definido para autonow = True). Isso é algo que você nem precisa sair em outra forma, ou lidar com a sua vista.

Respondeu 18/02/2009 em 04:15
fonte usuário

votos
0

Eu estou oferecendo uma solução alternativa devido a questões I encontrados com forms_valid não sendo chamado:

class SplingCreate(forms.ModelForm):
class Meta:
    model = SplingModel
    fields = ('Link', 'Genres', 'Image', 'ImageURL',)

def save(self, commit=True):
    from django.forms.models import save_instance

    if self.instance.pk is None:
        fail_message = 'created'
    else:
        fail_message = 'changed'
    fields = set(self._meta.fields) - set(('Genres',))
    instance = save_instance(self, self.instance, fields,
                             fail_message, commit, construct=False)

    genres = self.cleaned_data.get('Genres')
    for genre in genres:
        SplingGenreModel.objects.get_or_create(spling=instance, genre=genre)

    return instance

Copiei a lógica de Djangos formas / models.py, meu campo Gêneros é um ManyToMany com uma tabela intermediário - eu excluí-lo da save_instance e depois salvá-lo separadamente.

Respondeu 15/01/2014 em 19:11
fonte usuário

votos
0

Quando você salvar o seu formulário, você economiza objeto Cliente. Agora, se você deseja atribuir o cliente para o grupo que você deve fazer isso:

clientgroupe = ClientGroupe.objects.create(client=client_instance, groupe=groupe_instance, dt=datetime.datetime.now())

onde client_instance e groupe_instance seu cliente e groupe objets.

Respondeu 25/01/2009 em 22:56
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more