En una forma Django, ¿cómo puedo hacer que un campo sea de solo lectura (o deshabilitado) para que no se pueda editar?


En un formulario Django, ¿cómo hago un campo de solo lectura (o deshabilitado)?

Cuando el formulario se utiliza 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 modelo Item, todos los campos deben ser editables, pero al actualizar el registro, ¿hay alguna manera de deshabilitar el campo sku 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 puede reutilizar la clase ItemForm? Lo ¿se requerirían cambios en la clase de modelo ItemForm o Item? Tengo que 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()
Author: phoenix, 2008-11-27

26 answers

Como se señaló en esta respuesta, Django 1.9 agregó el campo .atributo disabled:

El argumento booleano deshabilitado, cuando se establece en True, deshabilita un campo de formulario usando el atributo HTML deshabilitado para que no sea editable por los usuarios. Incluso si un usuario manipula el valor del campo enviado al servidor, se ignorará a favor del valor de los datos iniciales del formulario.

Con Django 1.8 y anteriores, para desactivar la entrada en el widget y evitar hacks maliciosos POST debe borrar la entrada además de establecer el atributo readonly en el campo del formulario:

class ItemForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            self.fields['sku'].widget.attrs['readonly'] = True

    def clean_sku(self):
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            return instance.sku
        else:
            return self.cleaned_data['sku']

O, reemplace if instance and instance.pk con otra condición que indique que está editando. También puede establecer el atributo disabled en el campo de entrada, en lugar de readonly.

La función clean_sku se asegurará de que el valor readonly no sea anulado por un POST.

De lo contrario, no hay un campo de formulario Django incorporado que renderice un valor mientras rechaza los datos de entrada enlazados. Si esto es lo que desea, debería crear un ModelForm separado que excluya los campos no editables, y simplemente imprimirlos dentro de su plantilla.

 360
Author: Daniel Naab,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:42

Django 1.9 agregó el Campo.atributo deshabilitado: https://docs.djangoproject.com/en/1.9/ref/forms/fields/#disabled

El argumento booleano disabled, cuando se establece en True, deshabilita un campo de formulario usando el atributo HTML disabled para que no sea editable por los usuarios. Incluso si un usuario manipula el valor del campo enviado al servidor, se ignorará a favor del valor de los datos iniciales del formulario.

 147
Author: Mike Mahmud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-31 18:05:34

La configuración de SOLO LECTURA en el widget solo hace que la entrada en el navegador sea de solo lectura. Añadiendo un clean_sku que devuelve la instancia.sku asegura que el valor del campo no cambiará en el nivel del formulario.

def clean_sku(self):
    if self.instance: 
        return self.instance.sku
    else: 
        return self.fields['sku']

De esta manera se puede utilizar el modelo (sin modificar guardar) y aviod obtener el campo de error requerido.

 91
Author: muhuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-09 17:58:10

¡La respuesta de Awalker me ayudó mucho!

He cambiado su ejemplo para trabajar con Django 1.3, usando get_readonly_fields.

Normalmente debes declarar algo como esto en app/admin.py:

class ItemAdmin(admin.ModelAdmin):
    ...
    readonly_fields = ('url',)

Me he adaptado de esta manera:

# In the admin.py file
class ItemAdmin(admin.ModelAdmin):
    ...
    def get_readonly_fields(self, request, obj=None):
        if obj:
            return ['url']
        else:
            return []

Y funciona bien. Ahora, si agrega un Elemento, el campo url es lectura-escritura, pero al cambiar se convierte en solo lectura.

 60
Author: chirale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:36

Para hacer que esto funcione para un campo ForeignKey, se deben realizar algunos cambios. En primer lugar, la etiqueta SELECT HTML no tiene el atributo readonly. Necesitamos usar disabled="disabled" en su lugar. Sin embargo, entonces el navegador no envía ningún dato de formulario para ese campo. Por lo tanto, necesitamos establecer que el campo no sea obligatorio para que el campo valide correctamente. Luego necesitamos restablecer el valor a lo que solía ser para que no esté en blanco.

Así que para las claves foráneas tendrá que hacer algo como:

class ItemForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['sku'].required = False
            self.fields['sku'].widget.attrs['disabled'] = 'disabled'

    def clean_sku(self):
        # As shown in the above answer.
        instance = getattr(self, 'instance', None)
        if instance:
            return instance.sku
        else:
            return self.cleaned_data.get('sku', None)

De esta manera, el navegador no permitirá que el usuario cambie el campo, y siempre publicará como se dejó en blanco. Luego sobrescribimos el método clean para establecer el valor del campo para que sea el que estaba originalmente en la instancia.

 49
Author: Humphrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-05 16:56:17

Para Django 1.2+, puede anular el campo de la siguiente manera:

sku = forms.CharField(widget = forms.TextInput(attrs={'readonly':'readonly'}))
 22
Author: StefanNch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-15 18:26:02

Hice una clase MixIn que puede heredar para poder agregar un campo iterable read_only que deshabilitará y asegurará los campos en la edición no primera:

(Basado en las respuestas de Daniel y Muhuk)

from django import forms
from django.db.models.manager import Manager

# I used this instead of lambda expression after scope problems
def _get_cleaner(form, field):
    def clean_field():
         value = getattr(form.instance, field, None)
         if issubclass(type(value), Manager):
             value = value.all()
         return value
    return clean_field

class ROFormMixin(forms.BaseForm):
    def __init__(self, *args, **kwargs):
        super(ROFormMixin, self).__init__(*args, **kwargs)
        if hasattr(self, "read_only"):
            if self.instance and self.instance.pk:
                for field in self.read_only:
                    self.fields[field].widget.attrs['readonly'] = "readonly"
                    setattr(self, "clean_" + field, _get_cleaner(self, field))

# Basic usage
class TestForm(AModelForm, ROFormMixin):
    read_only = ('sku', 'an_other_field')
 16
Author: christophe31,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-15 18:30:27

Acabo de crear el widget más simple posible para un campo de solo lectura-Realmente no veo por qué los formularios no tienen esto ya:

class ReadOnlyWidget(widgets.Widget):
    """Some of these values are read only - just a bit of text..."""
    def render(self, _, value, attrs=None):
        return value

En la forma:

my_read_only = CharField(widget=ReadOnlyWidget())

Muy simple - y me consigue sólo la salida. Práctico en un conjunto de formularios con un montón de valores de solo lectura. Por supuesto, también podría ser un poco más inteligente y darle un div con el attrs para que pueda agregarle clases.

 10
Author: Danny Staple,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-28 11:46:27

Me encontré con un problema similar. Parece que pude resolverlo definiendo un método "get_readonly_fields" en mi clase ModelAdmin.

Algo como esto:

# In the admin.py file

class ItemAdmin(admin.ModelAdmin):

    def get_readonly_display(self, request, obj=None):
        if obj:
            return ['sku']
        else:
            return []

Lo bueno es que obj no será Ninguno cuando agregue un nuevo elemento, o será el objeto que se está editando cuando cambie un elemento existente.

Get_readonly_display está documentado aquí: http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#modeladmin-methods

 9
Author: awalker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-08 20:51:34

Como una adición útil a Humphrey's post, tuve algunos problemas con django-reversion, porque todavía registraba los campos deshabilitados como 'cambiados'. El siguiente código soluciona el problema.

class ItemForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['sku'].required = False
            self.fields['sku'].widget.attrs['disabled'] = 'disabled'

    def clean_sku(self):
        # As shown in the above answer.
        instance = getattr(self, 'instance', None)
        if instance:
            try:
                self.changed_data.remove('sku')
            except ValueError, e:
                pass
            return instance.sku
        else:
            return self.cleaned_data.get('sku', None)
 5
Author: Evan Brumley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:19

Como todavía no puedo comentar ( solución de muhuk), voy a responder como una respuesta separada. Este es un ejemplo de código completo, que funcionó para mí:

def clean_sku(self):
  if self.instance and self.instance.pk:
    return self.instance.sku
  else:
    return self.cleaned_data['sku']
 5
Author: Madis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:59

Estaba entrando en el mismo problema, así que creé un Mixin que parece funcionar para mis casos de uso.

class ReadOnlyFieldsMixin(object):
    readonly_fields =()

    def __init__(self, *args, **kwargs):
        super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
        for field in (field for name, field in self.fields.iteritems() if name in self.readonly_fields):
            field.widget.attrs['disabled'] = 'true'
            field.required = False

    def clean(self):
        cleaned_data = super(ReadOnlyFieldsMixin,self).clean()
        for field in self.readonly_fields:
           cleaned_data[field] = getattr(self.instance, field)

        return cleaned_data

Uso, simplemente defina cuáles deben ser leídos solamente:

class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
    readonly_fields = ('field1', 'field2', 'fieldx')
 4
Author: Michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-09 03:02:45

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 de ser un 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 la opción SELECCIONADA se estaba restableciendo a la predeterminada "---------".

De todos modos, no podía averiguar cómo arreglar que. Así que en su lugar, (y creo que esto es en realidad más limpio en la forma), hice los campos HiddenInputField (). Esto solo significa que tienes que hacer un poco más de trabajo en la plantilla.

Así que la solución para mí fue simplificar la Forma:

class ItemForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['sku'].widget=HiddenInput()

Y luego en la plantilla, necesitarás hacer un bucle manual del conjunto de formularios.

Entonces, en este caso harías algo como esto en la plantilla:

<div>
    {{ form.instance.sku }} <!-- This prints the value -->
    {{ form }} <!-- Prints form normally, and makes the hidden input -->
</div>

Esto funcionó un poco mejor para mí y con menos forma manipulación.

 4
Author: JamesD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:54

Dos enfoques más (similares) con un ejemplo generalizado:

1) primer enfoque-eliminar campo en el método save (), por ejemplo (no probado;)):

def save(self, *args, **kwargs):
    for fname in self.readonly_fields:
        if fname in self.cleaned_data:
            del self.cleaned_data[fname]
    return super(<form-name>, self).save(*args,**kwargs)

2) segundo enfoque-restablecer el campo al valor inicial en el método limpio:

def clean_<fieldname>(self):
    return self.initial[<fieldname>] # or getattr(self.instance, fieldname)

Basado en el segundo enfoque lo generalicé así:

from functools                 import partial

class <Form-name>(...):

    def __init__(self, ...):
        ...
        super(<Form-name>, self).__init__(*args, **kwargs)
        ...
        for i, (fname, field) in enumerate(self.fields.iteritems()):
            if fname in self.readonly_fields:
                field.widget.attrs['readonly'] = "readonly"
                field.required = False
                # set clean method to reset value back
                clean_method_name = "clean_%s" % fname
                assert clean_method_name not in dir(self)
                setattr(self, clean_method_name, partial(self._clean_for_readonly_field, fname=fname))

    def _clean_for_readonly_field(self, fname):
        """ will reset value to initial - nothing will be changed 
            needs to be added dynamically - partial, see init_fields
        """
        return self.initial[fname] # or getattr(self.instance, fieldname)
 3
Author: Robert Lujo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-27 21:38:51

Una opción simple es simplemente escribir form.instance.fieldName en la plantilla en lugar de form.fieldName.

 3
Author: alzclarke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-15 18:28:11

Si necesita varios campos de solo lectura.puede utilizar cualquiera de los métodos que se indican a continuación

Método 1

class ItemForm(ModelForm):
    readonly = ('sku',)

    def __init__(self, *arg, **kwrg):
        super(ItemForm, self).__init__(*arg, **kwrg)
        for x in self.readonly:
            self.fields[x].widget.attrs['disabled'] = 'disabled'

    def clean(self):
        data = super(ItemForm, self).clean()
        for x in self.readonly:
            data[x] = getattr(self.instance, x)
        return data

Método 2

Método de herencia

class AdvancedModelForm(ModelForm):


    def __init__(self, *arg, **kwrg):
        super(AdvancedModelForm, self).__init__(*arg, **kwrg)
        if hasattr(self, 'readonly'):
            for x in self.readonly:
                self.fields[x].widget.attrs['disabled'] = 'disabled'

    def clean(self):
        data = super(AdvancedModelForm, self).clean()
        if hasattr(self, 'readonly'):
            for x in self.readonly:
                data[x] = getattr(self.instance, x)
        return data


class ItemForm(AdvancedModelForm):
    readonly = ('sku',)
 3
Author: Sarath Ak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-19 10:13:11

Cómo lo hago con Django 1.11:

class ItemForm(ModelForm):
    disabled_fields = ('added_by',)

    class Meta:
        model = Item
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        for field in self.disabled_fields:
            self.fields[field].disabled = True
 3
Author: Lucas B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-24 15:34:58

Para la versión de Administración, creo que esta es una forma más compacta si tiene más de un campo:

def get_readonly_fields(self, request, obj=None):
    skips = ('sku', 'other_field')
    fields = super(ItemAdmin, self).get_readonly_fields(request, obj)

    if not obj:
        return [field for field in fields if not field in skips]
    return fields
 2
Author: Hassek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-15 18:36:25

Basado en la respuesta de Yamikep, encontré una solución mejor y muy simple que también maneja los campos ModelMultipleChoiceField.

Eliminar campo de form.cleaned_data evita que los campos se guarden:

class ReadOnlyFieldsMixin(object):
    readonly_fields = ()

    def __init__(self, *args, **kwargs):
        super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
        for field in (field for name, field in self.fields.iteritems() if
                      name in self.readonly_fields):
            field.widget.attrs['disabled'] = 'true'
            field.required = False

    def clean(self):
        for f in self.readonly_fields:
            self.cleaned_data.pop(f, None)
        return super(ReadOnlyFieldsMixin, self).clean()

Uso:

class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
    readonly_fields = ('field1', 'field2', 'fieldx')
 2
Author: darklow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:33

Aquí hay una versión un poco más implicada, basada en la respuesta de christophe31. No se basa en el atributo "readonly". Esto hace que sus problemas, como los cuadros de selección todavía ser variable y datapickers sigue apareciendo, desaparece.

En su lugar, envuelve el widget campos de formulario en un widget de solo lectura, haciendo que el formulario aún se valide. El contenido del widget original se muestra dentro de las etiquetas <span class="hidden"></span>. Si el widget tiene un método render_readonly() lo usa como texto visible, de lo contrario, analiza el HTML del widget original e intenta adivinar la mejor representación.

import django.forms.widgets as f
import xml.etree.ElementTree as etree
from django.utils.safestring import mark_safe

def make_readonly(form):
    """
    Makes all fields on the form readonly and prevents it from POST hacks.
    """

    def _get_cleaner(_form, field):
        def clean_field():
            return getattr(_form.instance, field, None)
        return clean_field

    for field_name in form.fields.keys():
        form.fields[field_name].widget = ReadOnlyWidget(
            initial_widget=form.fields[field_name].widget)
        setattr(form, "clean_" + field_name, 
                _get_cleaner(form, field_name))

    form.is_readonly = True

class ReadOnlyWidget(f.Select):
    """
    Renders the content of the initial widget in a hidden <span>. If the
    initial widget has a ``render_readonly()`` method it uses that as display
    text, otherwise it tries to guess by parsing the html of the initial widget.
    """

    def __init__(self, initial_widget, *args, **kwargs):
        self.initial_widget = initial_widget
        super(ReadOnlyWidget, self).__init__(*args, **kwargs)

    def render(self, *args, **kwargs):
        def guess_readonly_text(original_content):
            root = etree.fromstring("<span>%s</span>" % original_content)

            for element in root:
                if element.tag == 'input':
                    return element.get('value')

                if element.tag == 'select':
                    for option in element:
                        if option.get('selected'):
                            return option.text

                if element.tag == 'textarea':
                    return element.text

            return "N/A"

        original_content = self.initial_widget.render(*args, **kwargs)
        try:
            readonly_text = self.initial_widget.render_readonly(*args, **kwargs)
        except AttributeError:
            readonly_text = guess_readonly_text(original_content)

        return mark_safe("""<span class="hidden">%s</span>%s""" % (
            original_content, readonly_text))

# Usage example 1.
self.fields['my_field'].widget = ReadOnlyWidget(self.fields['my_field'].widget)

# Usage example 2.
form = MyForm()
make_readonly(form)
 2
Author: Rune Kaagaard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-18 23:28:48

¿Es esta la forma más sencilla?

Justo en un código de vista algo como esto:

def resume_edit(request, r_id):
    .....    
    r = Resume.get.object(pk=r_id)
    resume = ResumeModelForm(instance=r)
    .....
    resume.fields['email'].widget.attrs['readonly'] = True 
    .....
    return render(request, 'resumes/resume.html', context)

Funciona bien!

 1
Author: fly_frog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-11 21:17:06

Resolví este problema así:

    class UploadFileForm(forms.ModelForm):
     class Meta:
      model = FileStorage
      fields = '__all__'
      widgets = {'patient': forms.HiddenInput()}

En vistas:

form = UploadFileForm(request.POST, request.FILES, instance=patient, initial={'patient': patient})

Es todo.

 1
Author: Pavel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-08 10:52:57

Para django 1.9 +
Puede usar el argumento Campos deshabilitados para hacer que el campo deshabilite. por ejemplo, En el siguiente fragmento de código de forms.py archivo, he hecho campo employee_code deshabilitado

class EmployeeForm(forms.ModelForm):
    employee_code = forms.CharField(disabled=True)
    class Meta:
        model = Employee
        fields = ('employee_code', 'designation', 'salary')

Referencia https://docs.djangoproject.com/en/2.0/ref/forms/fields/#disabled

 1
Author: Ajinkya Bhosale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-26 08:15:13

Si está utilizando Django admin, aquí está la solución más simple.

class ReadonlyFieldsMixin(object):
    def get_readonly_fields(self, request, obj=None):
        if obj:
            return super(ReadonlyFieldsMixin, self).get_readonly_fields(request, obj)
        else:
            return tuple()

class MyAdmin(ReadonlyFieldsMixin, ModelAdmin):
    readonly_fields = ('sku',)
 0
Author: utapyngo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-24 06:37:40

Creo que su mejor opción sería incluir el atributo readonly en su plantilla renderizada en un <span> o <p> en lugar de incluirlo en el formulario si es readonly.

Los formularios son para recopilar datos, no para mostrarlos. Dicho esto, las opciones para mostrar en un widget readonly y los datos de scrub POST son soluciones finas.

 0
Author: austinheiman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-15 18:40:27

Si está trabajando con Django ver < 1.9 (el 1.9 ha agregado el atributo Field.disabled) podría intentar agregar el siguiente decorador a su método form __init__:

def bound_data_readonly(_, initial):
    return initial


def to_python_readonly(field):
    native_to_python = field.to_python

    def to_python_filed(_):
        return native_to_python(field.initial)

    return to_python_filed


def disable_read_only_fields(init_method):

    def init_wrapper(*args, **kwargs):
        self = args[0]
        init_method(*args, **kwargs)
        for field in self.fields.values():
            if field.widget.attrs.get('readonly', None):
                field.widget.attrs['disabled'] = True
                setattr(field, 'bound_data', bound_data_readonly)
                setattr(field, 'to_python', to_python_readonly(field))

    return init_wrapper


class YourForm(forms.ModelForm):

    @disable_read_only_fields
    def __init__(self, *args, **kwargs):
        ...

La idea principal es que si el campo es readonly no necesita ningún otro valor excepto initial.

P.d.: No se olvide de establecer yuor_form_field.widget.attrs['readonly'] = True

 0
Author: Yaroslav Varkhol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-04 05:45:50