Em um formulário do Django, como faço para um campo somente leitura (ou desativado)?
Quando o formulário está sendo usado para criar uma nova entrada, todos os campos devem ser ativados - mas quando o registro está no modo de atualização, alguns campos precisam ser somente leitura.
Por exemplo, ao criar um novo Item
modelo, todos os campos devem ser editáveis, mas durante a atualização do registro, existe uma maneira de desativar o sku
campo para que fique visível, mas não possa ser editado?
class Item(models.Model):
sku = models.CharField(max_length=50)
description = models.CharField(max_length=200)
added_by = models.ForeignKey(User)
class ItemForm(ModelForm):
class Meta:
model = Item
exclude = ('added_by')
def new_item_view(request):
if request.method == 'POST':
form = ItemForm(request.POST)
# Validate and save
else:
form = ItemForm()
# Render the view
A classe pode ItemForm
ser reutilizada? Quais alterações seriam necessárias na classe ItemForm
ou Item
modelo? Preciso escrever outra classe " ItemUpdateForm
", para atualizar o item?
def update_item_view(request):
if request.method == 'POST':
form = ItemUpdateForm(request.POST)
# Validate and save
else:
form = ItemUpdateForm()
Respostas:
Como apontado nesta resposta , o Django 1.9 adicionou o atributo Field.disabled :
Com o Django 1.8 e versões anteriores, para desativar a entrada no widget e impedir hackers POST maliciosos, você deve limpar a entrada, além de definir o
readonly
atributo no campo do formulário:Ou substitua
if instance and instance.pk
por outra condição indicando que você está editando. Você também pode definir o atributodisabled
no campo de entrada, em vez dereadonly
.A
clean_sku
função garantirá que oreadonly
valor não seja substituído por aPOST
.Caso contrário, não há nenhum campo de formulário interno do Django que renderize um valor ao rejeitar dados de entrada vinculados. Se é isso que você deseja, crie um separado
ModelForm
que exclua os campos não editáveis e imprima-os dentro do seu modelo.fonte
clean_description
método à classe do formulário.disabled
é adicionado no Django 1.9. SeField.disabled
estiver definido comoTrue
, o valor POST para issoField
é ignorado. Portanto, se você estiver usando o 1.9, não há necessidade de substituirclean
, basta definirdisabled = True
. Verifique esta resposta.O Django 1.9 adicionou o atributo Field.disabled: https://docs.djangoproject.com/en/stable/ref/forms/fields/#disabled
fonte
disabled=True
fará com que o modelo seja cuspido de volta ao usuário com erros de validação.A configuração
readonly
em um widget apenas torna a entrada no navegador somente leitura. A adição de umclean_sku
que retornainstance.sku
garante que o valor do campo não seja alterado no nível do formulário.Dessa forma, você pode usar o modelo (salvamento não modificado) e evitar o erro de campo necessário.
fonte
return self.cleaned_data['sku']
tão bom ou melhor? Os documentos parecem sugerir o usocleaned_data
: "O valor de retorno desse método substitui o valor existentecleaned_data
, portanto deve ser o valor do campocleaned_data
(mesmo que esse método não o tenha alterado) ou um novo valor limpo".A resposta do awalker me ajudou muito!
Mudei seu exemplo para trabalhar com o Django 1.3, usando get_readonly_fields .
Normalmente você deve declarar algo assim em
app/admin.py
:Eu me adaptei desta maneira:
E isso funciona bem. Agora, se você adicionar um item, o
url
campo será de leitura e gravação, mas, quando alterado, ele será somente leitura.fonte
Para fazer isso funcionar em um
ForeignKey
campo, é necessário fazer algumas alterações. Em primeiro lugar, aSELECT HTML
tag não possui o atributo readonly. Precisamos usar em seudisabled="disabled"
lugar. No entanto, o navegador não envia dados de formulário para esse campo. Portanto, precisamos definir esse campo para não ser necessário, para que o campo seja validado corretamente. Em seguida, precisamos redefinir o valor para o que costumava ser, para que não fique em branco.Portanto, para chaves estrangeiras, você precisará fazer algo como:
Dessa forma, o navegador não permitirá que o usuário altere o campo e sempre será
POST
deixado em branco. Em seguida, substituímos oclean
método para definir o valor do campo como o que estava originalmente na instância.fonte
TabularInline
, mas falhei porqueattrs
foram compartilhados entrewidget
instâncias e todos, exceto a primeira linha, incluindo os recém-adicionados, renderizados somente leitura.Para o Django 1.2+, você pode substituir o campo da seguinte maneira:
fonte
Field
disabled
não faz o que eu quero porque desativa o campo, mas também remove o rótulo / o torna invisível.Eu criei uma classe MixIn que você pode herdar para poder adicionar um campo iterável read_only que desabilitará e protegerá os campos na não primeira edição:
(Baseado nas respostas de Daniel e Muhuk)
fonte
Acabei de criar o widget mais simples possível para um campo somente leitura - eu realmente não vejo por que os formulários ainda não têm isso:
Na forma:
Muito simples - e me dá apenas saída. Útil em um formset com vários valores somente leitura. É claro - você também pode ser um pouco mais inteligente e dividir com os attrs para poder acrescentar classes a ele.
fonte
unicode(value)
no retorno, talvez. Supondo que o dunder unicode é sensato, você entenderia.Encontrei um problema semelhante. Parece que eu consegui resolvê-lo definindo um método "get_readonly_fields" na minha classe ModelAdmin.
Algo assim:
O bom é que
obj
será Nenhum quando você estiver adicionando um novo Item ou será o objeto que está sendo editado quando você estiver alterando um Item existente.get_readonly_display está documentado aqui: http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#modeladmin-methods
fonte
Uma opção simples é apenas digitar
form.instance.fieldName
o modelo em vez deform.fieldName
.fonte
verbos_name
oulabel
de campo? Como posso mostrar `label no template django? @alzclarkeComo faço com o Django 1.11:
fonte
Como uma adição útil ao post de Humphrey , tive alguns problemas com o django-reversion, porque ele ainda registrava os campos desativados como 'alterados'. O código a seguir corrige o problema.
fonte
Como ainda não posso comentar ( a solução de muhuk ), responderei como uma resposta separada. Este é um exemplo de código completo, que funcionou para mim:
fonte
Mais uma vez, vou oferecer mais uma solução :) Eu estava usando o código de Humphrey , então isso se baseia nisso.
No entanto, tive problemas com o campo sendo a
ModelChoiceField
. Tudo funcionaria no primeiro pedido. No entanto, se o conjunto de formulários tentasse adicionar um novo item e falhar na validação, algo estava errado com os formulários "existentes" em que aSELECTED
opção estava sendo redefinida para o padrão---------
.Enfim, eu não conseguia descobrir como consertar isso. Então, em vez disso (e acho que isso é realmente mais limpo no formulário), criei os campos
HiddenInputField()
. Isso significa apenas que você precisa trabalhar um pouco mais no modelo.Portanto, a correção para mim foi simplificar o formulário:
E então, no modelo, você precisará fazer um loop manual do formset .
Portanto, nesse caso, você faria algo assim no modelo:
Isso funcionou um pouco melhor para mim e com menos manipulação de formulários.
fonte
Eu estava enfrentando o mesmo problema, então criei um Mixin que parece funcionar nos meus casos de uso.
Uso, apenas defina quais devem ser somente leitura:
fonte
'collections.OrderedDict' object has no attribute 'iteritems'
se você precisar de vários campos somente leitura. você pode usar qualquer um dos métodos abaixo
Método 1
método 2
método de herança
fonte
Mais duas abordagens (semelhantes) com um exemplo generalizado:
1) primeira abordagem - remoção de campo no método save (), por exemplo (não testado;)):
2) segunda abordagem - redefinir o campo para o valor inicial no método limpo:
Com base na segunda abordagem, generalizei assim:
fonte
Para a versão Admin, acho que é uma maneira mais compacta se você tiver mais de um campo:
fonte
Com base na resposta de Yamikep , encontrei uma solução melhor e muito simples que também lida com
ModelMultipleChoiceField
campos.A remoção do campo
form.cleaned_data
impede que os campos sejam salvos:Uso:
fonte
Aqui está uma versão um pouco mais envolvente, baseada na resposta de christophe31 . Ele não depende do atributo "somente leitura". Isso causa problemas, como caixas de seleção ainda sendo alteráveis e datapickers ainda aparecendo, desaparecem.
Em vez disso, agrupa o widget de campos do formulário em um widget somente leitura, tornando o formulário ainda válido. O conteúdo do widget original é exibido dentro das
<span class="hidden"></span>
tags. Se o widget tiver umrender_readonly()
método, ele será usado como texto visível; caso contrário, ele analisa o HTML do widget original e tenta adivinhar a melhor representação.fonte
Essa é a maneira mais simples?
Em um código de exibição, algo como isto:
Funciona bem!
fonte
Para django 1.9+
Você pode usar o argumento Fields disabled para desativar o campo. Por exemplo, no seguinte trecho de código do arquivo forms.py, desabilitei o campo employee_code
Referência https://docs.djangoproject.com/en/2.0/ref/forms/fields/#disabled
fonte
Se você estiver trabalhando
Django ver < 1.9
(o atributo1.9
adicionouField.disabled
), tente adicionar o seguinte decorador ao seu__init__
método de formulário :A idéia principal é que, se o campo for,
readonly
você não precisará de nenhum outro valor, excetoinitial
.PS: Não se esqueça de definir
yuor_form_field.widget.attrs['readonly'] = True
fonte
Se você estiver usando o administrador do Django, aqui está a solução mais simples.
fonte
Acho que sua melhor opção seria incluir o atributo readonly no seu modelo renderizado em um
<span>
ou<p>
vez de incluí-lo no formulário, se for somente leitura.Os formulários são para coletar dados, não exibi-los. Dito isto, as opções para exibir em um
readonly
widget e limpar os dados do POST são boas soluções.fonte