【问题标题】:Allowing Edit to editable=False Fields in Django Admin允许在 Django Admin 中编辑可编辑 = 假字段
【发布时间】:2015-05-21 14:27:12
【问题描述】:

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

大概管理员是超级用户,我确实希望他能够更改字段值,但为了安全起见,我希望默认的 Serializer 逻辑是只读的。

更新

在创建对象时,我实际上不需要像“set-it”那样编辑字段。

【问题讨论】:

  • 您最好保留editable=True 并在序列化程序级别覆盖read_only=True。就这一点而言,DRF 比 Django 管理员灵活得多。
  • 我试图避免的问题(可能没有好的方法)是必须记住在给定模型的每个序列化器上设置read_only=True(可以有更多不止一个)。

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


【解决方案1】:

你做错了。

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

否则,将来您(或其他人)可能会想知道为什么设置为 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% 的功能。

【讨论】:

  • 我在问题中的语言有点草率,我已经更新了我的帖子。您在这里提出了一个公平的观点:“将来您(或其他人)可能会想知道为什么设置为 editable=False 的字段是如何被更改的”但是在我不想的情况下呢?编辑我希望在从管理员中创建对象时最初设置它的字段?
  • 啊!在这种情况下,这是重复的。你想要的是一个readonly 模型,可以在管理员中设置一个。我已将此标记为该问题的副本,它应该可以满足您的需要。 stackoverflow.com/questions/17613559/…
  • 无论如何我可以设置editable=False 并进行这项工作吗?
  • 我尝试了您的代码,以允许在管理视图中设置带有 editable=False 的字段。这是行不通的。最大的问题是 self.fields 在构造函数调用之前不可用。我没有进一步尝试。
【解决方案2】:

对于那些希望仅在创建期间允许编辑不可编辑字段的人(尚无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']

【讨论】:

    猜你喜欢
    • 2011-12-13
    • 1970-01-01
    • 2018-02-17
    • 2014-07-10
    • 2019-03-15
    • 2014-05-15
    • 2012-11-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多