Isso foi corrigido no Django 1.9 com form_kwargs .
Eu tenho um formulário do Django que se parece com isso:
class ServiceForm(forms.Form):
option = forms.ModelChoiceField(queryset=ServiceOption.objects.none())
rate = forms.DecimalField(widget=custom_widgets.SmallField())
units = forms.IntegerField(min_value=1, widget=custom_widgets.SmallField())
def __init__(self, *args, **kwargs):
affiliate = kwargs.pop('affiliate')
super(ServiceForm, self).__init__(*args, **kwargs)
self.fields["option"].queryset = ServiceOption.objects.filter(affiliate=affiliate)
Eu chamo este formulário com algo assim:
form = ServiceForm(affiliate=request.affiliate)
Onde request.affiliate
está o usuário conectado. Isso funciona como pretendido.
Meu problema é que agora quero transformar esse formulário único em um formset. O que não consigo descobrir é como posso passar as informações do afiliado para os formulários individuais ao criar o conjunto de formulários. De acordo com os documentos para criar um formset, preciso fazer algo assim:
ServiceFormSet = forms.formsets.formset_factory(ServiceForm, extra=3)
E então eu preciso criá-lo assim:
formset = ServiceFormSet()
Agora, como posso passar o afiliado = request.affiliate para os formulários individuais dessa maneira?
fonte
functools.partial
vez do Djangodjango.utils.functional.curry
. Eles fazem a mesma coisa, exceto quefunctools.partial
retorna um tipo de chamada distinto, em vez de uma função Python regular, e opartial
tipo não é vinculado como um método de instância, que resolve perfeitamente o problema que esse segmento de comentário foi amplamente dedicado à depuração.Documento Oficial Way
Django 2.0:
https://docs.djangoproject.com/en/2.0/topics/forms/formsets/#passing-custom-parameters-to-formset-forms
fonte
Eu criaria a classe de formulário dinamicamente em uma função, para que ela tenha acesso ao afiliado por meio do fechamento:
Como bônus, você não precisa reescrever o conjunto de consultas no campo de opções. A desvantagem é que a subclasse é um pouco descolada. (Qualquer subclasse deve ser feita de maneira semelhante.)
editar:
Em resposta a um comentário, você pode chamar esta função sobre qualquer lugar em que usaria o nome da classe:
fonte
Isto é o que funcionou para mim, Django 1.7:
Espero que ajude alguém, demorei o suficiente para descobrir;)
fonte
staticmethod
é necessário aqui?Eu gosto da solução de fechamento por ser "mais limpa" e mais Pythonic (então marque com +1 para responder à mmarshall), mas os formulários do Django também têm um mecanismo de retorno de chamada que você pode usar para filtrar conjuntos de consultas em conjuntos de formulários.
Também não está documentado, o que eu acho que é um indicador que os desenvolvedores do Django podem não gostar tanto.
Então, você basicamente cria o seu formset da mesma forma, mas adiciona o retorno de chamada:
Isso está criando uma instância de uma classe que se parece com isso:
Isso deve lhe dar uma idéia geral. É um pouco mais complexo transformar o retorno de chamada em um método de objeto como esse, mas oferece um pouco mais de flexibilidade em vez de executar um retorno de chamada de função simples.
fonte
Eu queria colocar isso como um comentário na resposta de Carl Meyers, mas como isso exige pontos, eu apenas o coloquei aqui. Levei duas horas para descobrir, então espero que ajude alguém.
Uma observação sobre o uso do inlineformset_factory.
Usei essa solução sozinho e funcionou perfeitamente, até que tentei com o inlineformset_factory. Eu estava executando o Django 1.0.2 e recebi alguma exceção estranha do KeyError. Atualizei para o tronco mais recente e funcionou diretamente.
Agora posso usá-lo semelhante a este:
fonte
modelformset_factory
. Obrigado por esta resposta!A partir da confirmação e091c18f50266097f648efc7cac2503968e9d217 em terça-feira, 14 de agosto às 23:44:46 2012 +0200, a solução aceita não pode mais funcionar.
A versão atual da função django.forms.models.modelform_factory () usa uma "técnica de construção de tipo", chamando a função type () no formulário passado para obter o tipo de metaclasse e, em seguida, usando o resultado para construir um objeto de classe digite em tempo real ::
Isso significa que mesmo um
curry
ed oupartial
objeto passado em vez de um formulário "faz com que o pato o morde" por assim dizer: chamará uma função com os parâmetros de construção de umModelFormClass
objeto, retornando a mensagem de erro:Para contornar esse eu escrevi uma função de gerador que usa um fecho para retornar uma subclasse de qualquer classe especificada como primeiro parâmetro, que em seguida, chama
super.__init__
apósupdate
ing os kwargs com as fornecidas na chamada da função gerador ::Em seu código, você chamará a fábrica de formulários como:
ressalvas:
fonte
A solução de Carl Meyer parece muito elegante. Eu tentei implementá-lo para modelformsets. Fiquei com a impressão de que não podia chamar métodos estáticos em uma classe, mas o seguinte funciona inexplicavelmente:
Na minha opinião, se eu fizer algo assim:
Em seguida, a palavra-chave "request" é propagada para todos os formulários membros do meu formset. Estou satisfeito, mas não tenho ideia de por que isso está funcionando - parece errado. Alguma sugestão?
fonte
MyFormSet.form.Meta.model
.MyFormSet.form().Meta.model
. Óbvio mesmo.Passei algum tempo tentando descobrir esse problema antes de ver esta postagem.
A solução que eu encontrei foi a solução de fechamento (e é uma solução que eu usei antes com os formulários de modelo do Django).
Eu tentei o método curry () como descrito acima, mas simplesmente não consegui fazê-lo funcionar com o Django 1.0, então, no final, voltei ao método de fechamento.
O método de fechamento é muito elegante e a única pequena estranheza é que a definição de classe está aninhada dentro da visualização ou de outra função. Eu acho que o fato de isso parecer estranho para mim é uma dificuldade da minha experiência anterior em programação e acho que alguém com experiência em linguagens mais dinâmicas não se incomodaria!
fonte
Eu tive que fazer uma coisa semelhante. Isso é semelhante à
curry
solução:fonte
Com base nesta resposta , encontrei uma solução mais clara:
E execute-o como
fonte
Eu sou um novato aqui, então não posso adicionar comentários. Espero que este código funcione também:
como adicionar parâmetros adicionais ao formset em
BaseFormSet
vez de ao form.fonte