Allowing Edit to editable=False Fields in Django Admin

后端 未结 2 1383
礼貌的吻别
礼貌的吻别 2021-01-01 06:08

DRF will use the editable=False on a field to default the Serializer to read-only. This is a very helpful / safe default that I take advantage of (ie I won\'t forget to set

相关标签:
2条回答
  • 2021-01-01 06:13

    For those who want to allow editing of a non-editabled field only during creation (no instance.pk, yet):

    # models.py
    class Entity(Model):
        name = CharField(max_length=200, unique=True, null=False, blank=False, editable=False)
    
    # admin.py
    @register(Entity)
    class EntityAdmin(ModelAdmin):
        def get_readonly_fields(self, request, obj=None):
            if obj:  # This is the case when obj is already created i.e. it's an edit
                return ['id', 'name']
            else:
                return []
    
        # this override prevents that the new_name field shows up in the change form if it's not a creation
        def get_form(self, request, obj=None, **kwargs):
            orig_self_form = self.form
            if not obj:
                self.form = CreateEntityForm
            result = super().get_form(request, obj=obj, **kwargs)
            self.form = orig_self_form
            return result
    
    # forms.py
    class CreateEntityForm(ModelForm):
        new_name = CharField(max_length=200, min_length=2, label='Name', required=True)
    
        def clean_new_name(self):
            code = self.cleaned_data['new_name']
            # validate uniqueness - if you need
            exists = Entity.objects.filter(name=code).first()
            if exists:
                raise ValidationError('Entity with this name already exists: {}', exists)
            return name
    
        def save(self, commit=True):
            if self.instance.pk:
                raise NotImplementedError('Editing of existing Entity is not allowed!')
    
            self.instance.name = self.cleaned_data['new_name'].upper()
            return super().save(commit)
    
        class Meta:
            model = Entity
            fields = ['new_name']
            exclude = ['id', 'name']
    
    0 讨论(0)
  • 2021-01-01 06:20

    You are going about this the wrong way.

    Your models should be the most pure implementation of the things you are modelling. If something about a model is fixed (for example a creation date) it shouldn't be editable in the model, if its mutable, then leave as editable in the model.

    Otherwise, in the future you (or someone else) might be stuck wondering why a field which is set to editable=False is some how being changed. Especially as the documentation states:

    If False, the field will not be displayed in the admin or any other ModelForm. They are also skipped during model validation.

    If you have one view in which it shouldn't be editable (such as in the API), then override it there.

    If you have multiple serilaizers for a model, instead make an abstract serializer with a read_only_fields set and then subclass that. For example:

    class AbstractFooSerializer(serializers.ModelSerializer):
        class Meta:
            model = Foo
            read_only_fields = ('bar',)
    
    
    class MainFooSerializer(AbstractFooSerializer):
        pass
    
    class DifferentFooSerializer(AbstractFooSerializer):
        pass
    

    If you really, really want to use editable=False, but allow the item to be edited in the Admin site only on creation you have an up hill battle.

    Probably the best approach would be to reimplement the AdminForm you are using for the Admin

    So instead of:

    class FooAdmin(admin.ModelAdmin):
    

    Use:

    class FooAdmin(admin.ModelAdmin):
        form = MySpecialForm
    

    Then declare the form:

    class MySpecialForm(forms.Model):
        def __init__(self, *args, **kwargs):
            self.is_new = False
            if kwargs.get('instance',None) is None:
                # There is no instance, thus its a new item
                self.is_new = True
                self.fields['one_time_field'] = forms.CharField() # Or what have you.
            super(MySpecialForm, self).__init__(*args, **kwargs)
    
        def save(self, commit=True):
             instance = super(MySpecialForm, self).save(commit)
             if self.is_new:
                 instance.your_one_time_only_field = self.one_time_field
                 instance.save()
             return instance
    

    Note: you will need to manually add a field and save each readonly field that you want to do this for. This may or may not be 100% functional.

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