En un formulario de Django, ¿cómo hago un campo de solo lectura (o deshabilitado)?
Cuando se utiliza el formulario para crear una nueva entrada, todos los campos deben estar habilitados, pero cuando el registro está en modo de actualización, algunos campos deben ser de solo lectura.
Por ejemplo, al crear un nuevo Item
modelo, todos los campos deben ser editables, pero al actualizar el registro, ¿hay alguna forma de deshabilitar el sku
campo para que sea visible, pero no se pueda editar?
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
¿Se ItemForm
puede reutilizar la clase ? ¿Qué cambios serían necesarios en la clase ItemForm
o Item
modelo? ¿Necesitaría escribir otra clase, " ItemUpdateForm
", para actualizar el elemento?
def update_item_view(request):
if request.method == 'POST':
form = ItemUpdateForm(request.POST)
# Validate and save
else:
form = ItemUpdateForm()
Respuestas:
Como se señaló en esta respuesta , Django 1.9 agregó el atributo Field.disabled :
Con Django 1.8 y versiones anteriores, para deshabilitar la entrada en el widget y evitar hacks POST maliciosos, debe eliminar la entrada además de configurar el
readonly
atributo en el campo del formulario:O reemplácelo
if instance and instance.pk
con otra condición que indique que está editando. También puede establecer el atributodisabled
en el campo de entrada, en lugar dereadonly
.La
clean_sku
función asegurará que elreadonly
valor no sea anulado por aPOST
.De lo contrario, no hay un campo de formulario Django incorporado que represente un valor mientras rechaza los datos de entrada vinculados. Si esto es lo que desea, en su lugar, debe crear un campo separado
ModelForm
que excluya los campos no editables e imprimirlos dentro de su plantilla.fuente
clean_description
método a la clase de formulario.disabled
agrega un nuevo argumento de campo en Django 1.9. SiField.disabled
se establece enTrue
, entoncesField
se ignora el valor POST para eso . Entonces, si está utilizando 1.9, no hay necesidad de anularclean
, solo configuredisabled = True
. Mira esta respuesta.Django 1.9 agregó el atributo Field.disabled: https://docs.djangoproject.com/en/stable/ref/forms/fields/#disabled
fuente
disabled=True
hará que el modelo vuelva al usuario con errores de validación.La configuración
readonly
en un widget solo hace que la entrada en el navegador sea de solo lectura. Agregar un valorclean_sku
que devuelveinstance.sku
garantiza que el valor del campo no cambiará en el nivel del formulario.De esta manera, puede usar el modelo (guardar sin modificar) y evitar obtener el error de campo requerido.
fuente
return self.cleaned_data['sku']
sería tan bueno o mejor? Los documentos parecen sugerir el uso decleaned_data
: "El valor de retorno de este método reemplaza el valor existente encleaned_data
, por lo que debe ser el valor del campo decleaned_data
(incluso si este método no lo cambió) o un nuevo valor limpio".¡La respuesta de awalker me ayudó mucho!
He cambiado su ejemplo para trabajar con Django 1.3, usando get_readonly_fields .
Por lo general, debe declarar algo como esto en
app/admin.py
:Me he adaptado de esta manera:
Y funciona bien. Ahora, si agrega un elemento, el
url
campo es de lectura-escritura, pero al cambiar se convierte en solo lectura.fuente
Para que esto funcione para un
ForeignKey
campo, se deben realizar algunos cambios. En primer lugar, laSELECT HTML
etiqueta no tiene el atributo de solo lectura. Necesitamos usardisabled="disabled"
en su lugar. Sin embargo, el navegador no envía ningún dato de formulario para ese campo. Por lo tanto, debemos establecer que ese campo no sea obligatorio para que el campo se valide correctamente. Luego, necesitamos restablecer el valor a lo que solía ser para que no esté en blanco.Entonces, para las claves externas, deberá hacer algo como:
De esta forma, el navegador no permitirá que el usuario cambie el campo y siempre
POST
lo dejará en blanco. Luego anulamos elclean
método para establecer que el valor del campo sea el que originalmente estaba en la instancia.fuente
TabularInline
, pero fallé porqueattrs
se compartieron entrewidget
instancias y todas, excepto la primera fila, incluida la recién agregada, representada como solo lectura.Para Django 1.2+, puede anular el campo así:
fuente
Field
disabled
no hace lo que quiero porque deshabilita el campo, pero también elimina la etiqueta / lo hace invisible.Hice una clase MixIn que puede heredar para poder agregar un campo iterable read_only que deshabilitará y asegurará los campos en la primera edición:
(Basado en las respuestas de Daniel y Muhuk)
fuente
Acabo de crear el widget más simple posible para un campo de solo lectura. Realmente no veo por qué los formularios ya no tienen esto:
En la forma:
Muy simple, y solo me da salida. Práctico en un conjunto de formularios con un montón de valores de solo lectura. Por supuesto, también podrías ser un poco más inteligente y darle un div con los atributos para que puedas agregarle clases.
fuente
unicode(value)
en la devolución, tal vez. Asumiendo que el dun Unicode es sensato, entonces lo entenderías.Me encontré con un problema similar. Parece que pude resolverlo definiendo un método "get_readonly_fields" en mi clase ModelAdmin.
Algo como esto:
Lo bueno es que
obj
será Ninguno cuando esté agregando un nuevo Elemento, o será el objeto que se está editando cuando esté cambiando un Elemento existente.get_readonly_display se documenta aquí: http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#modeladmin-methods
fuente
Una opción simple es simplemente escribir
form.instance.fieldName
la plantilla en lugar deform.fieldName
.fuente
verbos_name
olabel
del campo? ¿Cómo puedo mostrar la etiqueta `en la plantilla django? @alzclarkeCómo lo hago con Django 1.11:
fuente
Como una adición útil a la publicación de Humphrey , tuve algunos problemas con la reversión de django, porque todavía registraba los campos deshabilitados como "cambiados". El siguiente código soluciona el problema.
fuente
Como todavía no puedo comentar ( la solución de muhuk ), responderé como una respuesta separada. Este es un ejemplo de código completo, que funcionó para mí:
fuente
Una vez más, voy a ofrecer una solución más :) Estaba usando el código de Humphrey , así que esto se basa en eso.
Sin embargo, me encontré con problemas con el campo siendo a
ModelChoiceField
. Todo funcionaría en la primera solicitud. Sin embargo, si el conjunto de formularios intentó agregar un nuevo elemento y falló la validación, algo iba mal con los formularios "existentes" donde laSELECTED
opción se restablecía a los valores predeterminados---------
.De todos modos, no pude encontrar la manera de arreglar eso. Entonces, en cambio, (y creo que esto es realmente más limpio en la forma), hice los campos
HiddenInputField()
. Esto solo significa que tienes que trabajar un poco más en la plantilla.Entonces, la solución para mí fue simplificar el formulario:
Y luego, en la plantilla, deberá realizar algunos bucles manuales del conjunto de formularios .
Entonces, en este caso, haría algo como esto en la plantilla:
Esto funcionó un poco mejor para mí y con menos manipulación de formas.
fuente
Estaba teniendo el mismo problema, así que creé un Mixin que parece funcionar para mis casos de uso.
Uso, solo defina cuáles deben ser de solo lectura:
fuente
'collections.OrderedDict' object has no attribute 'iteritems'
si necesita múltiples campos de solo lectura, puede usar cualquiera de los métodos que se detallan a continuación
Método 1
método 2
método de herencia
fuente
Dos enfoques más (similares) con un ejemplo generalizado:
1) primer enfoque: eliminar el campo en el método save (), por ejemplo (no probado;)):
2) segundo enfoque: restablecer el campo al valor inicial en el método de limpieza:
Basado en el segundo enfoque, lo generalicé así:
fuente
Para la versión Admin, creo que esta es una forma más compacta si tiene más de un campo:
fuente
Basado en la respuesta de Yamikep , encontré una solución mejor y muy simple que también maneja
ModelMultipleChoiceField
campos.Eliminar el campo
form.cleaned_data
evita que se guarden los campos:Uso:
fuente
Aquí hay una versión un poco más complicada, basada en la respuesta de christophe31 . No se basa en el atributo "solo lectura". Esto hace que sus problemas, como los cuadros de selección aún sean cambiables y los recolectores de datos que aún aparecen, desaparezcan.
En cambio, envuelve el widget de campos de formulario en un widget de solo lectura, lo que hace que el formulario aún se valide. El contenido del widget original se muestra dentro de las
<span class="hidden"></span>
etiquetas. Si el widget tiene unrender_readonly()
método, lo usa como texto visible; de lo contrario, analiza el HTML del widget original e intenta adivinar la mejor representación.fuente
¿Es esta la forma más simple?
Justo en un código de vista algo como esto:
¡Funciona bien!
fuente
Para django 1.9+
Puede usar el argumento Campos deshabilitados para deshabilitar el campo. Por ejemplo, en el siguiente fragmento de código del archivo forms.py, he desactivado el campo employee_code
Referencia https://docs.djangoproject.com/en/2.0/ref/forms/fields/#disabled
fuente
Si está trabajando con
Django ver < 1.9
(el atributo1.9
ha agregadoField.disabled
), podría intentar agregar el siguiente decorador a su__init__
método de formulario :La idea principal es que si el campo es
readonly
no necesita ningún otro valor, exceptoinitial
.PD: no te olvides de configurar
yuor_form_field.widget.attrs['readonly'] = True
fuente
Si está utilizando el administrador de Django, esta es la solución más simple.
fuente
Creo que su mejor opción sería incluir el atributo de solo lectura en su plantilla representada en un
<span>
o<p>
lugar de incluirlo en el formulario si es de solo lectura.Los formularios son para recopilar datos, no para mostrarlos. Dicho esto, las opciones para mostrar en un
readonly
widget y borrar datos POST son buenas soluciones.fuente