19

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
  • 3,404
  • 3
  • 20
  • 29

5 Answers5

49

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
Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245
12

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
xleon
  • 6,201
  • 3
  • 36
  • 52
8

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

darren
  • 18,845
  • 17
  • 60
  • 79
  • 1
    I wanted to alter the form _per instance_, so `exclude` doesn't do the trick for me. – jammon Feb 09 '11 at 15:03
  • @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. – S.Lott Feb 09 '11 at 15:12
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

Jed Fox
  • 2,979
  • 5
  • 28
  • 38
T. Christiansen
  • 1,036
  • 2
  • 19
  • 34
4

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.

Cool Breeze
  • 738
  • 2
  • 10
  • 26