Login

Readonly Select Widget Replacement

Author:
colinkingswood
Posted:
December 10, 2014
Language:
Python
Version:
1.6
Score:
1 (after 1 ratings)

This is for situations where a ModelForm is needed on an existing object, but we want to disable ForeignKey widgets, and only use them as a display.

Usage: save the code in the top half of the example above into a file somewhere in your django project, in this example we will call it customwidget.py

The second half shows an example usage, but setting the widget and queryset in the init method of the form. If the queryset returned by the ForeignKey model (Example.objects.all() ) is small, you can omit the init code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    from django.forms import  Select
    from django.utils.html import format_html
    from django.utils.safestring import mark_safe
    from django.forms.utils import flatatt
    from itertools import chain



    class ReadOnlySelect(Select):
        """
        This should replace the Select widget with a disabled text widget displaying the value, 
        and hidden field with the actual id
        """
        def render(self, name, value, attrs=None, choices=()):
            final_attrs = self.build_attrs(attrs, name=name)
            display = "None"
            for option_value, option_label in chain(self.choices, choices):
                if str(option_value) == (value) : 
                    display = option_label 
            output = format_html('<input type=text value="%s" disabled="disabled" ><input type="hidden" value="%s"  %s> ' % (display, value, flatatt(final_attrs)))
        
            return mark_safe(output)

#-------------------------------------------------------------------------------------------------------------------

Example useage in in  forms.py

from sequencing.customwidgets import ReadOnlySelect

class LibraryUploadFormSetForm(ModelForm):
        example =forms.ModelChoiceField(queryset=Example.objects.none() , widget=ReadOnlySelect)

    def __init__(self,  **kwargs):    
        super(LibraryUploadFormSetForm, self).__init__(**kwargs) 
        initial = kwargs['initial']
        self.fields['example'].queryset = Sample.objects.filter(pk=initial['example]) 
        self.fields['example'].initial = initial['example'] 
 

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 3 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 3 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 10 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 11 months ago
  5. Help text hyperlinks by sa2812 12 months ago

Comments

infinite-dao (on May 3, 2016):
  • in ReadOnlySelect(Select), line 18 should be
    if str(option_value) == str(value):
    

Right?

  • line 36 has no closing single quote:
    self.fields['example'].queryset = Sample.objects.filter(pk=initial['example'])
    

#

logikonabstractions (on September 29, 2021):

Tested and this works (considering both corrections already mentioned in comments). I have added to that somewhat:

  • line 15 in django 3.2 at least now reads (check source code for build_attrs): final_attrs = self.build_attrs(attrs, extra_attrs={"name":name})

  • line 20 ouput now reads (with the nicer f"...{}.." syntaxe for mixt text/variables) : output = format_html(f'<input {flatatt(self.attrs)} type="text" value="{display}" disabled={True}><input type="hidden" value={value} {flatatt(final_attrs)}>')

Because this allows me the following use case in forms.py:

class WarehouseUpdateForm(ReadOnlyForms): class Meta: widgets = {"fk_terme":ReadOnlySelect(attrs={"class":"readonly"})}

E.g. in my case I'm using a "readonly" class to style form elements the user can't edit differently. In fact this way, basically any attrs you add will be passed on to your displayed input.

#

Please login first before commenting.