Digamos que tenho o seguinte no meu models.py
:
class Company(models.Model):
name = ...
class Rate(models.Model):
company = models.ForeignKey(Company)
name = ...
class Client(models.Model):
name = ...
company = models.ForeignKey(Company)
base_rate = models.ForeignKey(Rate)
Ou seja, existem vários Companies
, cada um com um intervalo de Rates
e Clients
. Cada um Client
deve ter uma base Rate
escolhida de seu pai Company's Rates
, não outra Company's Rates
.
Ao criar um formulário para adicionar um Client
, eu gostaria de remover as Company
opções (como isso já foi selecionado por meio de um botão "Adicionar cliente" na Company
página) e limitar as Rate
opções a isso Company
também.
Como eu faço isso no Django 1.0?
Meu forms.py
arquivo atual é apenas clichê no momento:
from models import *
from django.forms import ModelForm
class ClientForm(ModelForm):
class Meta:
model = Client
E o views.py
também é básico:
from django.shortcuts import render_to_response, get_object_or_404
from models import *
from forms import *
def addclient(request, company_id):
the_company = get_object_or_404(Company, id=company_id)
if request.POST:
form = ClientForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(the_company.get_clients_url())
else:
form = ClientForm()
return render_to_response('addclient.html', {'form': form, 'the_company':the_company})
No Django 0.96, eu pude invadir isso fazendo algo como o seguinte antes de renderizar o modelo:
manipulator.fields[0].choices = [(r.id,r.name) for r in Rate.objects.filter(company_id=the_company.id)]
ForeignKey.limit_choices_to
parece promissor, mas não sei como passar the_company.id
e não sei se isso funcionará fora da interface do administrador.
Obrigado. (Parece uma solicitação bastante básica, mas se eu deveria redesenhar algo, estou aberto a sugestões.)
Respostas:
ForeignKey é representado por django.forms.ModelChoiceField, que é um ChoiceField cujas opções são um modelo QuerySet. Veja a referência para ModelChoiceField .
Portanto, forneça um QuerySet ao
queryset
atributo do campo . Depende de como seu formulário é criado. Se você criar um formulário explícito, terá campos nomeados diretamente.Se você pegar o objeto ModelForm padrão,
form.fields["rate"].queryset = ...
Isso é feito explicitamente na exibição. Não hackear.
fonte
__init__
método do formulário ?Além da resposta de S.Lott e como se tornar o Guru mencionado nos comentários, é possível adicionar os filtros do conjunto de consultas substituindo a
ModelForm.__init__
função. (Isso pode se aplicar facilmente a formulários regulares), pode ajudar na reutilização e manter a função de visualização organizada.Isso pode ser útil para reutilizar, digamos, se você possui filtros comuns necessários em muitos modelos (normalmente eu declaro uma classe Form abstrata). Por exemplo
Fora isso, estou apenas reafirmando o material do blog Django, do qual existem muitos bons por aí.
fonte
Isso é simples e funciona com o Django 1.4:
Você não precisa especificar isso em uma classe de formulário, mas pode fazê-lo diretamente no ModelAdmin, pois o Django já inclui este método interno no ModelAdmin (nos documentos):
Uma maneira ainda mais interessante de fazer isso (por exemplo, na criação de uma interface administrativa de front-end que os usuários possam acessar) é subclassificar o ModelAdmin e alterar os métodos abaixo. O resultado líquido é uma interface do usuário que APENAS mostra o conteúdo relacionado a eles, permitindo que você (um superusuário) veja tudo.
Eu substituí quatro métodos, os dois primeiros tornam impossível para um usuário excluir qualquer coisa e também remove os botões de exclusão do site de administração.
A terceira substituição filtra qualquer consulta que contenha uma referência a (no exemplo 'usuário' ou 'porco-espinho' (apenas como ilustração).
A última substituição filtra qualquer campo de chave estrangeira no modelo para filtrar as opções disponíveis da mesma forma que o conjunto de consultas básico.
Dessa forma, você pode apresentar um site de administração fácil de gerenciar, que permite que os usuários mexam com seus próprios objetos, e você não precisa se lembrar de digitar os filtros ModelAdmin específicos sobre os quais falamos acima.
remova os botões "excluir":
impede permissão de exclusão
filtra objetos que podem ser visualizados no site de administração:
filtra as opções de todos os campos de chave estrangeira no site de administração:
fonte
Para fazer isso com uma exibição genérica, como CreateView ...
a parte mais importante disso ...
, leia meu post aqui
fonte
Se você não criou o formulário e deseja alterar o conjunto de consultas, pode fazer:
Isso é bastante útil quando você está usando visualizações genéricas!
fonte
Então, eu realmente tentei entender isso, mas parece que o Django ainda não torna isso muito simples. Não sou tão burra assim, mas simplesmente não consigo ver nenhuma solução (um tanto) simples.
Acho geralmente muito feio ter que substituir as visualizações de administrador por esse tipo de coisa, e todos os exemplos que eu acho nunca se aplicam totalmente às visualizações de administrador.
Essa é uma circunstância tão comum nos modelos que eu acho que é terrível que não haja uma solução óbvia para isso ...
Eu tenho essas classes:
Isso cria um problema ao configurar o Admin for Company, porque ele possui linhas para Contrato e Local, e as opções m2m do Contrato para Local não são filtradas corretamente de acordo com a Empresa que você está editando no momento.
Em resumo, eu precisaria de alguma opção de administrador para fazer algo assim:
Por fim, eu não me importaria se o processo de filtragem fosse colocado no CompanyAdmin base ou se fosse no ContractInline. (Colocá-lo na linha faz mais sentido, mas dificulta a referência ao contrato base como 'próprio'.)
Existe alguém por aí que sabe de algo tão simples quanto esse atalho tão necessário? Quando eu criei administradores PHP para esse tipo de coisa, isso era considerado funcionalidade básica! Na verdade, era sempre automático e precisava ser desativado se você realmente não o quisesse!
fonte
Uma maneira mais pública é chamar get_form nas classes Admin. Também funciona para campos que não são do banco de dados. Por exemplo, aqui eu tenho um campo chamado '_terminal_list' no formulário que pode ser usado em casos especiais para escolher vários itens de terminal de get_list (request) e filtrar com base em request.user:
fonte