How can I disable a model field in a django form

38,996

Solution 1

Step 1: Disable the frontend widget

Use the HTML readonly attribute:
http://www.w3schools.com/tags/att_input_readonly.asp

Or disabled attribute:
http://www.w3.org/TR/html401/interact/forms.html#adef-disabled

You can inject arbitrary HTML key value pairs via the widget attrs property:

myform.fields['status'].widget.attrs['readonly'] = True # text input
myform.fields['status'].widget.attrs['disabled'] = True # radio / checkbox

Step 2: Ensure the field is effectively disabled on backend

Override your clean method for your field so that regardless of POST input (somebody can fake a POST, edit the raw HTML, etc.) you get the field value that already exists.

def clean_status(self):
    # when field is cleaned, we always return the existing model field.
    return self.instance.status

Solution 2

From django 1.9:

from django.forms import Textarea

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = '__all__'
        widgets = {'my_field_in_my_model': Textarea(attrs={'cols':80,'rows':1}),}             

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['my_field_in_my_model'].disabled = True

Solution 3

Have you tried using the exclude function?

something like this

class PartialAuthorForm(ModelForm):
class Meta:
    model = Author
    fields = ('name', 'title')

class PartialAuthorForm(ModelForm):
class Meta:
    model = Author
    exclude = ('birth_date',)

Reference Here

Solution 4

Just customize the widget instance for the status field:

class MyModel(models.Model):
    REGULAR = 1
    PREMIUM = 2
    STATUS_CHOICES = ((REGULAR, "regular"), (PREMIUM, "premium"))
    name = models.CharField(max_length=30)
    status = models.IntegerField(choices = STATUS_CHOICES, default = REGULAR)

class MyForm(forms.ModelForm):
    status =  forms.CharField(widget=forms.TextInput(attrs={'readonly':'True'}))

    class Meta:
        model = models.MyModel

see: Django Documentation

Solution 5

There is a very easy way of doing it:

class GenerateCertificate(models.Model):

    field_name = models.CharField(
        max_length=15,
        editable=False)
    def __unicode__(self):
        return unicode(self.field_name)

The editable=False will make the field disabled for editing.

Share:
38,996

Related videos on Youtube

jammon
Author by

jammon

Updated on August 01, 2020

Comments

  • jammon
    jammon over 3 years

    I have a model like this:

    class MyModel(models.Model):
        REGULAR = 1
        PREMIUM = 2
        STATUS_CHOICES = ((REGULAR, "regular"), (PREMIUM, "premium"))
        name = models.CharField(max_length=30)
        status = models.IntegerField(choices = STATUS_CHOICES, default = REGULAR)
    
    class MyForm(forms.ModelForm):
        class Meta:
            model = models.MyModel
    

    In a view I initialize one field and try to make it non-editable:

    myform = MyForm(initial = {'status': requested_status})
    myform.fields['status'].editable = False
    

    But the user can still change that field.

    What's the real way to accomplish what I'm after?

  • jammon
    jammon about 13 years
    I wanted to alter the form per instance, so exclude doesn't do the trick for me.
  • user1066101
    user1066101 about 13 years
    @jammon: (1) "per instance" doesn't make sense. You can have several different subclasses with several different combinations of fields. (2) Please update the question to explain what you want.
  • jammon
    jammon about 13 years
    Oh no. I tried it, and readonly doesn't make a difference. And (w3schools.com/tags/att_input_readonly.asp) says 'The readonly attribute can be used with <input type="text"> or <input type="password">.' In my case it's a select or radio buttons. So any more ideas?
  • Yuji 'Tomita' Tomita
    Yuji 'Tomita' Tomita about 13 years
    Hmm, looks like the solution with those buttons is attrs['disabled'] = True w3.org/TR/html401/interact/forms.html#adef-disabled
  • jammon
    jammon about 13 years
    Works just the way I wanted. Thank you!
  • Yuji 'Tomita' Tomita
    Yuji 'Tomita' Tomita about 13 years
    No problem jammon. If you found it satisfactory, check the checkmark next to the answer.
  • nnyby
    nnyby almost 9 years
    I get the error: 'BoundField' object has no attribute 'widget'
  • Roberth Solís
    Roberth Solís over 6 years
    How i can select all fields?
  • Rafi
    Rafi over 3 years
    for selecting all fields: for key in self.fields.keys(): self.fields[key].widget.attrs['readonly'] = True self.fields[key].widget.attrs['disabled'] = True
  • Apollo Data
    Apollo Data over 3 years
    I was missing the clean part to maintain the value. A lifesaver, thanks!

Related