如何获得Django管理员的“另存为新”以使用只读字段?

时间:2019-04-09 20:11:57

标签: django django-admin

我想在Django管理员中为这种模型实现“另存为新”功能:

class Plasmid (models.Model):
    name = models.CharField("Name", max_length = 255, blank=False)
    other_name = models.CharField("Other Name", max_length = 255, blank=True)
    selection = models.CharField("Selection", max_length = 50, blank=False)
    created_by = models.ForeignKey(User)

在管理员中,如果请求质粒对象的用户与创建该对象的用户不同,则上述某些字段将设置为只读。如果用户相同,则他们都是可编辑的。例如:

class PlasmidPage(admin.ModelAdmin):

    def get_readonly_fields(self, request, obj=None):

        if obj:
            if not request.user == obj.created_by:
                return ['name', 'created_by',]
            else:
                return ['created_by',]
        else:
            return []

    def change_view(self,request,object_id,extra_context=None):

        self.fields = ('name', 'other_name', 'selection', 'created_by',)
        return super(PlasmidPage,self).change_view(request,object_id)

我遇到的问题是,当字段为只读并且用户单击“另存为新”按钮时,该字段的值不会“传输”到新对象。另一方面,将传输非只读字段的值。

有人吗,或者我怎么能解决这个问题?我想将只读和非只读字段的值都传输到新对象。

2 个答案:

答案 0 :(得分:0)

您尝试使用 Field.disabled属性吗?

  

禁用的布尔参数设置为True时,将使用禁用的HTML属性禁用表单字段,以便用户无法对其进行编辑。即使用户篡改了提交给服务器的字段的值,也将忽略该字段,而使用表单的初始数据中的值。

我在项目中进行了快速测试。当我添加新条目时,禁用的字段已发送到服务器。 所以这样的事情应该为您工作:


class PlasmidPage(admin.ModelAdmin):

    def get_form(self, request, *args, **kwargs):
        form = super(PlasmidPage, self).get_form(request, *args, **kwargs)
        if not request.user == self.cleaned_data['created_by'].:
            form.base_fields['created_by'].disabled = True
            form.base_fields['name'].disabled = True

    def change_view(self,request,object_id,extra_context=None):

        self.fields = ('name', 'other_name', 'selection', 'created_by',)
        return super(PlasmidPage,self).change_view(request,object_id)

答案 1 :(得分:0)

发生这种情况是因为Django使用request.POST数据来构建新对象,但是只读字段未随请求主体一起发送。您可以通过以下方式克服此问题:将小部件设置为只读,而不是将字段本身设置为:

form.fields['name'].widget.attrs = {'readonly': True}

这有一个缺点:仍然可以通过篡改表单来更改字段值(例如,如果您使用devtools控制台从窗口小部件中删除了该只读属性)。您可以通过检查clean()方法中的值实际上没有更改来对此加以保护。

因此完整的解决方案将是:

class PlasmidForm(models.ModelForm):
    class Meta:
        model = Plasmid

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.instance and not self.instance.created_by == request.user:
            self.fields['name'].widget.attrs = {'readonly': True}

    def clean(self):
        cleaned_data = super().clean()
        if self.instance and not self.instance.created_by == request.user:
            self.cleaned_data['name'] = instance.name  # just in case user tampered with the form
        return cleaned_data


class PlasmidAdmin(admin.ModelAdmin):
    form = PlasmidForm
    readonly_fields = ('created_by',)

    def save_model(self, request, obj, form, change):
        if obj.created_by is None:
            obj.created_by = request.user
        super().save_model(request, obj, form, change)

注意,我将created_by保留为只读状态,而是在保存对象时使用当前用户填充它。我不认为您真的要从另一个对象转移此属性。