在Django Admin中允许编辑为editable = False字段

时间:2015-03-18 17:01:42

标签: django-models django-forms django-admin django-rest-framework django-serializer

DRF will use the editable=False on a field to default the Serializer to read-only。这是我利用的一个非常有用/安全的默认值(即我不会忘记将Serializer设置为只读)。有人说,一旦我设置了editable=False,就有办法强制Django管理员允许编辑其中一个字段吗?

据推测,管理员是一个超级用户,我确实希望他能够更改字段值但是安全性我希望默认的Serializer逻辑是只读的。

更新

我实际上并不需要像#34; set-it"那样编辑字段。当我创建对象时。

2 个答案:

答案 0 :(得分:3)

你这是错误的做法。

您的模型应该是您正在建模的事物的最纯粹的实现。如果关于模型的某些内容是固定的(例如创建日期),则它不应该在模型中可编辑,如果它是可变的,则在模型中保留为可编辑。

否则,将来你(或其他人)可能会不知道为什么设置为editable=False的字段会被改变。 Especially as the documentation states

  

如果为False,该字段将不会显示在admin或任何其他ModelForm中。在模型验证过程中也会跳过它们。

如果您有一个不应该可编辑的视图(例如在API中),请在那里覆盖它。

如果您有一个模型的多个序列化器,而是创建一个带有read_only_fields集的抽象序列化器,然后将其子类化。例如:

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


class MainFooSerializer(AbstractFooSerializer):
    pass

class DifferentFooSerializer(AbstractFooSerializer):
    pass

如果你真的,真的想要使用editable=False,但允许在管理网站中仅在创建上编辑项目,那么你就会有一场上山战。

可能最好的方法是重新实现您用于管理员的AdminForm

所以而不是:

class FooAdmin(admin.ModelAdmin):

使用:

class FooAdmin(admin.ModelAdmin):
    form = MySpecialForm

然后声明表格:

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

注意:您需要手动添加字段并保存要为其执行此操作的每个readonly字段。这可能是也可能不是100%有效。

答案 1 :(得分:1)

对于那些只想在创建期间允许编辑不可编辑字段的人(尚无instance.pk):

# 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']