Django管理员 - save_model方法 - 如何检测字段是否已更改?

43
我正在尝试重写Django管理对象上的save_model方法,以防止用户更改某个字段。 但是我找不到一种方法在此方法中找出字段是否已更改。
以下是我迄今为止的代码:
def save_model(self, request, obj, form, change):
    if change: 
        if obj.parking_location == form.cleaned_data['parking_location']:
            super(MyVehiclesAdmin, self).save_model(request, obj, form, change)
        else:
            messages.error(request, 
                "The Parking Location field cannot be changed.")

问题是obj.parking_location和form.cleaned_data['parking_location']都有新值。(这可能是Django的一个错误吗?看起来obj应该包含预保存的值)。在任何情况下,是否有另一种方法来实现这个目标? (我使用的是Django 1.2版本)
4个回答

70

首先,这不是一个bug,而是自Django 1.2版本以来的文档化行为。

Django 1.2发布说明中得知:

第一次调用ModelForm.is_valid()、访问ModelForm.errors或以其他方式触发表单验证时,您的模型将被原地清理。 这种转换过去发生在保存模型时。 如果您需要未修改的模型实例,则应将副本传递给ModelForm构造函数。

如果您想防止用户编辑特定字段,则更好的方法可能是使用ModelAdmin.readonly_fields选项。

class VehicleRegistrationAdmin(admin.ModelAdmin):
    readonly_fields = ('parking_location',)

或者,您可以用一个自定义表单替换ModelAdmin.form,并排除该字段。

class VehicleRegistrationForm(forms.ModelForm):
    class Meta:
        exclude = ('parking_location',)

class VehicleRegistrationAdmin(admin.ModelAdmin):
    form = VehicleRegistrationForm

最后,更直接地回答您的问题,您可以通过检查save_model方法中的form.changed_data来确定字段是否已更改。这是一个已更改字段名称的列表。

def save_model(self, request, obj, form, change):
    if 'parking_location' in form.changed_data:
        messages.info(request, "Parking location has changed")
    else:
        messages.info(request, "Parking location has not changed")
    super(MyVehiclesAdmin, self).save_model(request, obj, form, change)

3
哦,是的!form.changed_data是通过ModelAdminsave_model方法实现的方式。谢谢。我给你点赞 :) - nik_m

43

对于那些想了解change参数的人:

如果这是对现有模型的更改,则此参数将为True; 如果模型是新创建的实例,则为False。

要检查是否更改了任何字段: form.changed_data将包含已更改的字段,如果没有更改则为空。


4
谢谢您。这应该在文档中(具体说明更改参数的作用)。 - Yuji 'Tomita' Tomita

8

好的,我找到了一个解决方法。尽管如此,这仍然看起来像是一个bug。

def save_model(self, request, obj, form, change):
    if change: 
        vr = VehicleRegistration.objects.get(pk=obj.id)
        if vr.parking_location == form.cleaned_data['parking_location']:
            super(MyVehiclesAdmin, self).save_model(request, obj, form, change)
        else:
            messages.error(request, 
                "The Parking Location field cannot be changed.")

1
糟糕的设计决策!不等于错误 ;) - DylanYoung
这是一个很好的方式,如果您想检查先前的值和新值。 - Linh Nguyen

3

使用MyVehicles.objects.get(pk=obj.pk)可以从数据库中获取值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接