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

后端 未结 26 892
-上瘾入骨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:33

    To make this work for a ForeignKey field, a few changes need to be made. Firstly, the SELECT HTML tag does not have the readonly attribute. We need to use disabled="disabled" instead. However, then the browser doesn't send any form data back for that field. So we need to set that field to not be required so that the field validates correctly. We then need to reset the value back to what it used to be so it's not set to blank.

    So for foreign keys you will need to do something like:

    class ItemForm(ModelForm):
    
        def __init__(self, *args, **kwargs):
            super(ItemForm, self).__init__(*args, **kwargs)
            instance = getattr(self, 'instance', None)
            if instance and instance.id:
                self.fields['sku'].required = False
                self.fields['sku'].widget.attrs['disabled'] = 'disabled'
    
        def clean_sku(self):
            # As shown in the above answer.
            instance = getattr(self, 'instance', None)
            if instance:
                return instance.sku
            else:
                return self.cleaned_data.get('sku', None)
    

    This way the browser won't let the user change the field, and will always POST as it it was left blank. We then override the clean method to set the field's value to be what was originally in the instance.

    0 讨论(0)
  • 2020-11-22 04:33

    if your need multiple read-only fields.you can use any of methods given below

    method 1

    class ItemForm(ModelForm):
        readonly = ('sku',)
    
        def __init__(self, *arg, **kwrg):
            super(ItemForm, self).__init__(*arg, **kwrg)
            for x in self.readonly:
                self.fields[x].widget.attrs['disabled'] = 'disabled'
    
        def clean(self):
            data = super(ItemForm, self).clean()
            for x in self.readonly:
                data[x] = getattr(self.instance, x)
            return data
    

    method 2

    inheritance method

    class AdvancedModelForm(ModelForm):
    
    
        def __init__(self, *arg, **kwrg):
            super(AdvancedModelForm, self).__init__(*arg, **kwrg)
            if hasattr(self, 'readonly'):
                for x in self.readonly:
                    self.fields[x].widget.attrs['disabled'] = 'disabled'
    
        def clean(self):
            data = super(AdvancedModelForm, self).clean()
            if hasattr(self, 'readonly'):
                for x in self.readonly:
                    data[x] = getattr(self.instance, x)
            return data
    
    
    class ItemForm(AdvancedModelForm):
        readonly = ('sku',)
    
    0 讨论(0)
  • 2020-11-22 04:33

    If you are using Django admin, here is the simplest solution.

    class ReadonlyFieldsMixin(object):
        def get_readonly_fields(self, request, obj=None):
            if obj:
                return super(ReadonlyFieldsMixin, self).get_readonly_fields(request, obj)
            else:
                return tuple()
    
    class MyAdmin(ReadonlyFieldsMixin, ModelAdmin):
        readonly_fields = ('sku',)
    
    0 讨论(0)
  • 2020-11-22 04:34

    For the Admin version, I think this is a more compact way if you have more than one field:

    def get_readonly_fields(self, request, obj=None):
        skips = ('sku', 'other_field')
        fields = super(ItemAdmin, self).get_readonly_fields(request, obj)
    
        if not obj:
            return [field for field in fields if not field in skips]
        return fields
    
    0 讨论(0)
  • 2020-11-22 04:36

    As I can't yet comment (muhuk's solution), I'll response as a separate answer. This is a complete code example, that worked for me:

    def clean_sku(self):
      if self.instance and self.instance.pk:
        return self.instance.sku
      else:
        return self.cleaned_data['sku']
    
    0 讨论(0)
  • 2020-11-22 04:36

    If you are working with Django ver < 1.9 (the 1.9 has added Field.disabled attribute) you could try to add following decorator to your form __init__ method:

    def bound_data_readonly(_, initial):
        return initial
    
    
    def to_python_readonly(field):
        native_to_python = field.to_python
    
        def to_python_filed(_):
            return native_to_python(field.initial)
    
        return to_python_filed
    
    
    def disable_read_only_fields(init_method):
    
        def init_wrapper(*args, **kwargs):
            self = args[0]
            init_method(*args, **kwargs)
            for field in self.fields.values():
                if field.widget.attrs.get('readonly', None):
                    field.widget.attrs['disabled'] = True
                    setattr(field, 'bound_data', bound_data_readonly)
                    setattr(field, 'to_python', to_python_readonly(field))
    
        return init_wrapper
    
    
    class YourForm(forms.ModelForm):
    
        @disable_read_only_fields
        def __init__(self, *args, **kwargs):
            ...
    

    The main idea is that if field is readonly you don't need any other value except initial.

    P.S: Don't forget to set yuor_form_field.widget.attrs['readonly'] = True

    0 讨论(0)
提交回复
热议问题