In a Django form, how do I make a field readonly (or disabled) so that it cannot be edited?

后端 未结 26 999
-上瘾入骨i
-上瘾入骨i 2020-11-22 04:09

In a Django form, how do I make a field read-only (or disabled)?

When the form is being used to create a new entry, all fields should be enabled - but when the recor

26条回答
  •  死守一世寂寞
    2020-11-22 04:18

    Here is a slightly more involved version, based on christophe31's answer. It does not rely on the "readonly" attribute. This makes its problems, like select boxes still being changeable and datapickers still popping up, go away.

    Instead, it wraps the form fields widget in a readonly widget, thus making the form still validate. The content of the original widget is displayed inside tags. If the widget has a render_readonly() method it uses that as the visible text, otherwise it parses the HTML of the original widget and tries to guess the best representation.

    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 . 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("%s" % 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("""%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)
    

提交回复
热议问题