Skip to content

Instantly share code, notes, and snippets.

@shadiakiki1986
Created July 4, 2017 14:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shadiakiki1986/e27edd06f2cb3ad4110405235849ebfb to your computer and use it in GitHub Desktop.
Save shadiakiki1986/e27edd06f2cb3ad4110405235849ebfb to your computer and use it in GitHub Desktop.
Django simplest read-only widget
# Based on the below answer on the SO question:
# In a Django form, how do I make a field readonly (or disabled) so that it cannot be edited?
# https://stackoverflow.com/a/15134622/4126114
#
# Usage of either widget classes below:
# class MyForm(forms.ModelForm):
# class Meta:
# widgets = {
# 'field': ReadOnlyWidgetSimple(),
# 'foreign_key': ReadOnlyWidgetMyModel()
# }
from django.forms import widgets
# For non-foreign-key fields, display them in <p>...</p>
# Note the "str(...)" necessary for things like datetime objects
# Also, without the <p>...</p>, the field in bootstrap_form does not show up aligned properly
class ReadOnlyWidgetSimple(widgets.Widget):
"""Some of these values are read only - just a bit of text..."""
def render(self, _, value, attrs=None):
return "<p>"+str(value)+"</p>"
# For foreign-key fields, display their __str()__ result
# Usage: Define a class inheriting from this, and which defines the "model" attribute
# e.g.
# class ReadOnlyWidgetMyModel(ReadOnlyWidgetModel):
# model = MyModel # <<<<< put your model class here
class ReadOnlyWidgetModel(widgets.Widget):
model = None
"""Some of these values are read only - just a bit of text..."""
def render(self, _, value, attrs=None):
if not self.model: raise Exception("Define model")
v2 = self.model.objects.get(id=value)
v2 = "<p>"+str(v2)+"</p>"
return v2
@nameisnot
Copy link

I tried the gist for my project but coming up with errors like "Type Error" render() got an unexpected keyword argument 'name'.

Background:
My Original Form class:

class ComplaintEditForm(forms.ModelForm):

    class Meta:
        model = complaint
        fields = ['unit_number', 'complaint_short_desc', 'complaint_long_text', 'assigned_to']
    complaint_short_desc = forms.CharField(widget=forms.TextInput(attrs={'size': '60'}), disabled=True)
    complaint_long_text = forms.CharField(widget=forms.Textarea, disabled=True)

Field "unit_number" is an FK field to another model from where it inherits the __str__() value.

With this line of approach I am able to disable the fields complaint_short_desc and complaint_long_text. But the FK field "unit_number" is displayed as a normal drop down choice field which allows the user to select the choices available and change the value. I tried using the ChoiceField for the FK field unit_number but I get an empty drop down widget.

While searching I came across your gist but unable to implement it.

How I am trying to adopt your code:
The FK Read Only widget class:

class ReadOnlyWidgetModel(widgets.Widget):

    model = None # complaint
    """Some of the values are read only - just a bit of text... """
    def render(self, _, value, attrs=None):
        if not self.model: raise Exception("Define model")
        v2 = self.model.objects.get(id=value)
        v2 = "<p>"+str(v2)+"</p>"
        return v2

Note: I had first tried to put my model name (which is complaint) but came up with error so I changed it back to your code with None.

class ReadOnlyWidgetMyModel(ReadOnlyWidgetModel):

    class Meta:
        model = complaint

Finally I modified my Form class as:

class ComplaintEditForm(forms.ModelForm):
    class Meta:
        model = complaint
        fields = ['unit_number', 'complaint_short_desc', 'complaint_long_text', 'assigned_to']
        widgets = {
            'unit_number': ReadOnlyWidgetMyModel(),
            }
    complaint_short_desc = forms.CharField(widget=forms.TextInput(attrs={'size': '60'}), disabled=True)
    complaint_long_text = forms.CharField(widget=forms.Textarea, disabled=True)


At this point I am getting an error saying (cited at the beginning):

TypeError at /maint/complaint/19/
render() got an unexpected keyword argument 'name'

I am using Django 2.0.6 / Python 3.6.4.

Can you provide a guidance as to what I am missing here?

Thanks.

@lanningspencer
Copy link

Over a year, so I am betting you have found a workaround or simply just moved on.

But I believe the problem with your code lies in the way you have tried to override the render function.
The render() method takes in 5 parameters, causing some backward compatibility issues with older versions of Django.

def render(self, name, value, attrs=None, renderer=None):

@shadiakiki1986
Copy link
Author

Better later than never 😄
@nameisnot sorry I hadn't seen your comment earlier. @lanningspencer is probably right about this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment