Django: vários modelos em um modelo usando formulários [fechado]

114

Estou construindo um aplicativo de rastreamento de tíquetes de suporte e tenho alguns modelos que gostaria de criar em uma página. Os ingressos pertencem a um cliente por meio de uma ForeignKey. As notas também pertencem aos Tickets por meio de uma ForeignKey. Eu gostaria de ter a opção de selecionar um cliente (que é um projeto totalmente separado) OU criar um novo cliente, então criar um tíquete e finalmente criar uma nota atribuída ao novo tíquete.

Como sou bastante novo no Django, tendo a trabalhar iterativamente, testando novos recursos a cada vez. Eu joguei com ModelForms, mas quero ocultar alguns dos campos e fazer algumas validações complexas. Parece que o nível de controle que estou procurando requer conjuntos de formulários ou fazer tudo à mão, com uma página de modelo entediante e codificada à mão, que estou tentando evitar.

Há algum recurso adorável que estou perdendo? Alguém tem uma boa referência ou exemplo de uso de formsets? Passei um fim de semana inteiro nos documentos da API para eles e ainda estou sem noção. É um problema de design se eu quebrar e codificar tudo manualmente?

neoice
fonte
primeiro você deve validar seu formulário de cliente e se for válido, criar uma cópia de request.POST (new_data = request.POST.copy ()) .e então obter o id do cliente (do formulário de cliente validado) e com a atualização de new_data, faça id do cliente um valor para o campo de chave estrangeira (talvez cliente em seu modelo). E, finalmente, considere new_data para validar seu segundo formulário (Tickets)
Negar37

Respostas:

87

Isso realmente não é muito difícil de implementar com ModelForms . Então, digamos que você tenha os Formulários A, B e C. Você imprime cada um dos formulários e a página e agora precisa lidar com o POST.

if request.POST():
    a_valid = formA.is_valid()
    b_valid = formB.is_valid()
    c_valid = formC.is_valid()
    # we do this since 'and' short circuits and we want to check to whole page for form errors
    if a_valid and b_valid and c_valid:
        a = formA.save()
        b = formB.save(commit=False)
        c = formC.save(commit=False)
        b.foreignkeytoA = a
        b.save()
        c.foreignkeytoB = b
        c.save()

Aqui estão os documentos para validação personalizada.

Jason Christa
fonte
2
Aliás, não acho que os formsets sejam uma boa solução para o problema que você descreveu. Sempre os usei para representar várias instâncias de um modelo. Por exemplo, você tem um formulário de candidato e deseja 3 referências para fazer um formset que tem 3 instâncias do modelo de referência.
Jason Christa
1
observe que, da maneira como você faz isso, a chamada .is_valid () não entra em curto-circuito. Se você quiser fazer um curto-circuito, precisará atrasar a chamada da função .is_valid () até o 'e'.
Lie Ryan
66

Eu estava quase na mesma situação um dia atrás, e aqui estão meus 2 centavos:

1) Eu encontrei sem dúvida a demonstração mais curta e concisa de entrada de vários modelos em um único formulário aqui: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ .

Resumindo: Faça um formulário para cada modelo, envie os dois para o modelo em um só <form>, usando prefixkeyarg e tenha a validação do identificador de visualização. Se houver dependência, apenas certifique-se de salvar o modelo "pai" antes do dependente, e use o ID do pai para a chave estrangeira antes de salvar o modelo "filho". O link contém a demonstração.

2) Talvez os formsets possam ser batidos para fazer isso, mas pelo que eu investiguei, os formsets são principalmente para inserir múltiplos do mesmo modelo, que podem ser opcionalmente vinculados a outro modelo / modelos por chaves estrangeiras. No entanto, parece não haver opção padrão para inserir mais de um dado do modelo e não é para isso que o formset parece ser feito.

Gnudiff
fonte
26

Recentemente tive alguns problemas e descobri como fazer isso. Supondo que você tenha três classes, Primária, B, C e que B, C tenham uma chave estrangeira para primária

    class PrimaryForm(ModelForm):
        class Meta:
            model = Primary

    class BForm(ModelForm):
        class Meta:
            model = B
            exclude = ('primary',)

    class CForm(ModelForm):
         class Meta:
            model = C
            exclude = ('primary',)

    def generateView(request):
        if request.method == 'POST': # If the form has been submitted...
            primary_form = PrimaryForm(request.POST, prefix = "primary")
            b_form = BForm(request.POST, prefix = "b")
            c_form = CForm(request.POST, prefix = "c")
            if primary_form.is_valid() and b_form.is_valid() and c_form.is_valid(): # All validation rules pass
                    print "all validation passed"
                    primary = primary_form.save()
                    b_form.cleaned_data["primary"] = primary
                    b = b_form.save()
                    c_form.cleaned_data["primary"] = primary
                    c = c_form.save()
                    return HttpResponseRedirect("/viewer/%s/" % (primary.name))
            else:
                    print "failed"

        else:
            primary_form = PrimaryForm(prefix = "primary")
            b_form = BForm(prefix = "b")
            c_form = Form(prefix = "c")
     return render_to_response('multi_model.html', {
     'primary_form': primary_form,
     'b_form': b_form,
     'c_form': c_form,
      })

Este método deve permitir que você faça qualquer validação necessária, bem como gerar todos os três objetos na mesma página. Também usei javascript e campos ocultos para permitir a geração de vários objetos B, C na mesma página.


fonte
3
Neste exemplo, como você está definindo as chaves estrangeiras dos modelos B e C para apontar para o modelo Primário?
Usuário
Só tenho dois modelos que quero mostrar no mesmo formulário. Mas não recebo a instrução exclude = ('primary',). O que é primário? Tem 2 modelos CustomerConfig e Contract. O contrato tem a chave estrangeira para CustomerConfig. Como customer_config = models.ForeignKey ('CustomerPartnerConfiguration') O que é 'principal'?
pitchblack408 de
10

O MultiModelForm de django-betterformsé um wrapper conveniente para fazer o que é descrito na resposta de Gnudiff . Ele envolve ModelForms regulares em uma única classe que é transparente (pelo menos para o uso básico) usado como um único formulário. Copiei um exemplo de seus documentos abaixo.

# forms.py
from django import forms
from django.contrib.auth import get_user_model
from betterforms.multiform import MultiModelForm
from .models import UserProfile

User = get_user_model()

class UserEditForm(forms.ModelForm):
    class Meta:
        fields = ('email',)

class UserProfileForm(forms.ModelForm):
    class Meta:
        fields = ('favorite_color',)

class UserEditMultiForm(MultiModelForm):
    form_classes = {
        'user': UserEditForm,
        'profile': UserProfileForm,
    }

# views.py
from django.views.generic import UpdateView
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect
from django.contrib.auth import get_user_model
from .forms import UserEditMultiForm

User = get_user_model()

class UserSignupView(UpdateView):
    model = User
    form_class = UserEditMultiForm
    success_url = reverse_lazy('home')

    def get_form_kwargs(self):
        kwargs = super(UserSignupView, self).get_form_kwargs()
        kwargs.update(instance={
            'user': self.object,
            'profile': self.object.profile,
        })
        return kwargs
jozxyqk
fonte
Acabei de ver django-betterformse sua classe MultiModelForm antes de encontrar sua resposta. A solução deles parece muito boa, mas parece que não é atualizada há algum tempo. Você ainda está usando @jozxyqk? Quaisquer problemas?
enchance
@enchance já faz alguns anos. Naquela época, eu achei conveniente e uma das melhores opções que existem. Se você não ficar muito elaborado, economiza algum tempo. Posso imaginar que quando você quiser começar a customizar e fazer formulários não triviais, será mais fácil criar o seu próprio. Misturar facilmente formas e contextos em visualizações é o primeiro recurso que realmente acho que perdi no django.
jozxyqk
Obrigado pela resposta cara. Estou pensando em apenas bifurcar e talvez atualizar algumas coisas ao longo do caminho. Pelo que vi até agora, está funcionando muito bem. Você está certo, economiza muito tempo.
enchance
5

Atualmente, tenho uma solução alternativa funcional (ela passa nos testes de unidade). É uma boa solução para minha opinião quando você deseja apenas adicionar um número limitado de campos de outros modelos.

Estou faltando alguma coisa aqui?

class UserProfileForm(ModelForm):
    def __init__(self, instance=None, *args, **kwargs):
        # Add these fields from the user object
        _fields = ('first_name', 'last_name', 'email',)
        # Retrieve initial (current) data from the user object
        _initial = model_to_dict(instance.user, _fields) if instance is not None else {}
        # Pass the initial data to the base
        super(UserProfileForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs)
        # Retrieve the fields from the user model and update the fields with it
        self.fields.update(fields_for_model(User, _fields))

    class Meta:
        model = UserProfile
        exclude = ('user',)

    def save(self, *args, **kwargs):
        u = self.instance.user
        u.first_name = self.cleaned_data['first_name']
        u.last_name = self.cleaned_data['last_name']
        u.email = self.cleaned_data['email']
        u.save()
        profile = super(UserProfileForm, self).save(*args,**kwargs)
        return profile
Paul Bormans
fonte
3

"Eu quero ocultar alguns dos campos e fazer algumas validações complexas."

Começo com a interface de administração integrada.

  1. Construa o ModelForm para mostrar os campos desejados.

  2. Estenda o formulário com as regras de validação dentro do formulário. Normalmente, este é um cleanmétodo.

    Certifique-se de que esta parte funcione razoavelmente bem.

Depois de fazer isso, você pode sair da interface administrativa integrada.

Então, você pode brincar com vários formulários parcialmente relacionados em uma única página da web. Este é um monte de coisas de modelo para apresentar todos os formulários em uma única página.

Então você tem que escrever a função de visualização para ler e validar as várias coisas do formulário e fazer os vários objetos salvos ().

"É um problema de design se eu quebrar e codificar tudo manualmente?" Não, é apenas muito tempo para não trazer muitos benefícios.

S.Lott
fonte
Não sei como, portanto, não faça isso
orokusaki
1
@orokusaki: O que mais você gostaria? Isso parece descrever uma solução. O que mais deve ser dito? A questão é vaga, por isso é difícil fornecer o código real. Em vez de reclamar, forneça uma sugestão de melhoria. O que você sugere?
S.Lott