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

431

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 Itemmodelo, todos los campos deben ser editables, pero al actualizar el registro, ¿hay alguna forma de deshabilitar el skucampo 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 ItemFormpuede reutilizar la clase ? ¿Qué cambios serían necesarios en la clase ItemFormo Itemmodelo? ¿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()
X10
fuente
Consulte también la pregunta SO: ¿Por qué los campos de formulario de solo lectura en Django son una mala idea? @ stackoverflow.com/questions/2902024 , la respuesta aceptada (por Daniel Naab) se encarga de los ataques maliciosos de POST.
X10

Respuestas:

422

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

El argumento booleano deshabilitado, cuando se establece en Verdadero, deshabilita un campo de formulario usando el atributo HTML deshabilitado para que los usuarios no puedan editarlo. 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 versiones anteriores, para deshabilitar la entrada en el widget y evitar hacks POST maliciosos, debe eliminar la entrada además de configurar el readonlyatributo 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 reemplácelo if instance and instance.pkcon otra condición que indique que está editando. También puede establecer el atributo disableden el campo de entrada, en lugar de readonly.

La clean_skufunción asegurará que el readonlyvalor no sea anulado por a POST.

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 ModelFormque excluya los campos no editables e imprimirlos dentro de su plantilla.

Daniel Naab
fuente
3
Daniel, gracias por publicar una respuesta. ¿No está claro para mí cómo usar este código? ¿No funcionaría este código para el mismo modo de actualización? ¿Puedes editar tu respuesta para dar ejemplos sobre cómo usarla para formularios nuevos y actualizados? Gracias.
X10
8
La clave del ejemplo de Daniel es probar el campo .id. Los objetos recién creados tendrán id == Ninguno. Por cierto, una de las entradas abiertas más antiguas de Django es sobre este tema. Ver code.djangoproject.com/ticket/342 .
Peter Rowell el
2
@moadeep agrega un clean_descriptionmétodo a la clase de formulario.
Daniel Naab
3
en linux (ubuntu 15) / chrome v45, solo cambia el puntero a una "mano deshabilitada" pero luego se puede hacer clic en el campo. con discapacitados funciona como se esperaba
Simone cittadini
77
Esta respuesta necesita ser actualizada. Se disabledagrega un nuevo argumento de campo en Django 1.9. Si Field.disabledse establece en True, entonces Fieldse ignora el valor POST para eso . Entonces, si está utilizando 1.9, no hay necesidad de anular clean, solo configure disabled = True. Mira esta respuesta.
narendra-choudhary 05 de
174

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

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

Mike Mahmud
fuente
¿Nada para 1.8 LTS?
dnit13
99
¿Alguna idea de cómo podemos usar esto en un UpdateView? A medida que genera los campos del modelo ...
bcsanches
66
Respuesta correcta. Mi clase de solución MyChangeForm (forms.ModelForm): def __init __ (self, * args, ** kwargs): super (MyChangeForm, self) .__ init __ (* args, ** kwargs) self.fields ['my_field']. Disabled = Cierto
Vijay Katam el
8
Esta es una respuesta problemática: la configuración disabled=Truehará que el modelo vuelva al usuario con errores de validación.
Ben
1
Sería increíble si pudieras incluir un ejemplo
geodésico el
95

La configuración readonlyen un widget solo hace que la entrada en el navegador sea de solo lectura. Agregar un valor clean_skuque devuelve instance.skugarantiza 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, puede usar el modelo (guardar sin modificar) y evitar obtener el error de campo requerido.

muhuk
fuente
15
+1 Esta es una excelente manera de evitar anulaciones más complicadas de save (). Sin embargo, querrá hacer una comprobación de instancia antes del retorno (en modo de comentario sin línea nueva): "if self.instance: return self.instance.sku; else: return self.fields ['sku']"
Daniel Naab
2
Para la última línea, ¿ return self.cleaned_data['sku']sería tan bueno o mejor? Los documentos parecen sugerir el uso de cleaned_data: "El valor de retorno de este método reemplaza el valor existente en cleaned_data, por lo que debe ser el valor del campo de cleaned_data(incluso si este método no lo cambió) o un nuevo valor limpio".
pianoJames
67

¡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:

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 urlcampo es de lectura-escritura, pero al cambiar se convierte en solo lectura.

chirale
fuente
56

Para que esto funcione para un ForeignKeycampo, se deben realizar algunos cambios. En primer lugar, la SELECT HTMLetiqueta no tiene el atributo de solo lectura. Necesitamos usar disabled="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:

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 forma, el navegador no permitirá que el usuario cambie el campo y siempre POSTlo dejará en blanco. Luego anulamos el cleanmétodo para establecer que el valor del campo sea el que originalmente estaba en la instancia.

Humphrey
fuente
Traté de usarlo como formulario TabularInline, pero fallé porque attrsse compartieron entre widgetinstancias y todas, excepto la primera fila, incluida la recién agregada, representada como solo lectura.
dhill
¡Una gran solución (actualización)! Desafortunadamente, esto y el resto tienen problemas cuando hay errores de forma ya que todos los valores "deshabilitados" se vacían.
Michael Thompson
28

Para Django 1.2+, puede anular el campo así:

sku = forms.CharField(widget = forms.TextInput(attrs={'readonly':'readonly'}))
StefanNch
fuente
66
Esto tampoco permite que el campo se edite en el momento de agregar, que es de lo que trata la pregunta original.
Matt S.
Esta es la respuesta que estoy buscando. Field disabledno hace lo que quiero porque deshabilita el campo, pero también elimina la etiqueta / lo hace invisible.
sivabudh
18

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)

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')
christophe31
fuente
11

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:

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 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.

Danny Staple
fuente
2
Parece sexy, pero ¿cómo manejar la clave externa?
andilabs
Haga eso unicode(value)en la devolución, tal vez. Asumiendo que el dun Unicode es sensato, entonces lo entenderías.
Danny Staple
Para las claves foráneas, deberá agregar un atributo "modelo" y usar "get (value)". Comprueba mi esencia
shadi
10

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 objserá 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

awalker
fuente
6

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

alzclarke
fuente
¿Y qué tal el verbos_nameo labeldel campo? ¿Cómo puedo mostrar la etiqueta `en la plantilla django? @alzclarke
Whale 52Hz
6

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
Lucas B
fuente
esto solo bloqueará desde el frente. cualquiera puede pasar por alto. esto hará un problema de seguridad si lo haces con datos confidenciales
Sarath Ak
Es seguro; también bloquea el backend desde Django> = 1.10 docs.djangoproject.com/en/1.10/ref/forms/fields/…
timdiels
5

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.

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)
Evan Brumley
fuente
5

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í:

def clean_sku(self):
  if self.instance and self.instance.pk:
    return self.instance.sku
  else:
    return self.cleaned_data['sku']
Madis
fuente
5

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 la SELECTEDopció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:

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, deberá realizar algunos bucles manuales del conjunto de formularios .

Entonces, en este caso, haría 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 manipulación de formas.

JamesD
fuente
4

Estaba teniendo 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, solo defina cuáles deben ser de solo lectura:

class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
    readonly_fields = ('field1', 'field2', 'fieldx')
Miguel
fuente
Supongo que es un poco más legible que mi propio mixin que sugerí aquí. Incluso probablemente más eficiente, ya que esas limpiezas no generan errores de validación ...
christophe31
Me sale un error:'collections.OrderedDict' object has no attribute 'iteritems'
geoidesic
4

si necesita múltiples campos de solo lectura, puede usar cualquiera de los métodos que se detallan 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',)
Sarath Ak
fuente
3

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

1) primer enfoque: eliminar el 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 de limpieza:

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)
Robert Lujo
fuente
3

Para la versión Admin, 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
Hassek
fuente
3

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

Eliminar el campo form.cleaned_dataevita que se guarden los campos:

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')
oscuro
fuente
2

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 un render_readonly()método, 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)
Runa Kaagaard
fuente
1

¿Es esta la forma más simple?

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!

fly_frog
fuente
1

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

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

Ajinkya Bhosale
fuente
1

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

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 readonlyno necesita ningún otro valor, excepto initial.

PD: no te olvides de configurar yuor_form_field.widget.attrs['readonly'] = True

Yaroslav Varkhol
fuente
0

Si está utilizando el administrador de Django, esta es 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',)
utapyngo
fuente
0

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 readonlywidget y borrar datos POST son buenas soluciones.

austinheiman
fuente