在我的模型中我有:

class Alias(MyBaseModel):
remote_image = models.URLField(
    max_length=500, null=True,
    help_text='''
        A URL that is downloaded and cached for the image.
        Only used when the alias is made
    '''
)
    image = models.ImageField(
        upload_to='alias', default='alias-default.png',
        help_text="An image representing the alias"
    )

    
    def save(self, *args, **kw):
        if (not self.image or self.image.name == 'alias-default.png') and self.remote_image :
            try :
                data = utils.fetch(self.remote_image)
                image = StringIO.StringIO(data)
                image = Image.open(image)
                buf = StringIO.StringIO()
                image.save(buf, format='PNG')
                self.image.save(
                    hashlib.md5(self.string_id).hexdigest() + ".png", ContentFile(buf.getvalue())
                )
            except IOError :
                pass

这在remote_image第一次改变的时候工作得很好。

当有人修改了别名上的remote_image时,我如何获取一个新的图像?其次,是否有更好的方法来缓存远程映像?


当前回答

从Django 1.8开始,就有了from_db方法,Serge提到过。事实上,Django文档中包含了这样一个特定的用例:

https://docs.djangoproject.com/en/dev/ref/models/instances/#customizing-model-loading

下面是一个示例,展示如何记录从数据库加载的字段的初始值

其他回答

最佳的解决方案可能是在保存模型实例之前不包括额外的数据库读取操作,也不包括任何进一步的django库。这就是为什么拉弗斯特的解决方案更可取。在管理站点的上下文中,可以简单地覆盖save_model-方法,并调用表单的has_changed方法,就像上面Sion的回答一样。你得到了类似这样的东西,利用Sion的例子设置,但使用changed_data来获得每一个可能的变化:

class ModelAdmin(admin.ModelAdmin):
   fields=['name','mode']
   def save_model(self, request, obj, form, change):
     form.changed_data #output could be ['name']
     #do somethin the changed name value...
     #call the super method
     super(self,ModelAdmin).save_model(request, obj, form, change)

覆盖save_model:

https://docs.djangoproject.com/en/1.10/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model

为字段内置changed_data-method:

https://docs.djangoproject.com/en/1.10/ref/forms/api/#django.forms.Form.changed_data

本质上,你想要重写模型的__init__方法。模型,以便保留原始值的副本。这使得您不必再执行另一个DB查找(这总是一件好事)。

    class Person(models.Model):
        name = models.CharField()

        __original_name = None

        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.__original_name = self.name

        def save(self, force_insert=False, force_update=False, *args, **kwargs):
            if self.name != self.__original_name:
                # name changed - do something here

            super().save(force_insert, force_update, *args, **kwargs)
            self.__original_name = self.name

有一个属性__dict__,它将所有字段作为键,并将值作为字段值。所以我们可以比较其中两个

只需将模型的save函数更改为下面的函数即可

def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
    if self.pk is not None:
        initial = A.objects.get(pk=self.pk)
        initial_json, final_json = initial.__dict__.copy(), self.__dict__.copy()
        initial_json.pop('_state'), final_json.pop('_state')
        only_changed_fields = {k: {'final_value': final_json[k], 'initial_value': initial_json[k]} for k in initial_json if final_json[k] != initial_json[k]}
        print(only_changed_fields)
    super(A, self).save(force_insert=False, force_update=False, using=None, update_fields=None)

使用示例:

class A(models.Model):
    name = models.CharField(max_length=200, null=True, blank=True)
    senior = models.CharField(choices=choices, max_length=3)
    timestamp = models.DateTimeField(null=True, blank=True)

    def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
        if self.pk is not None:
            initial = A.objects.get(pk=self.pk)
            initial_json, final_json = initial.__dict__.copy(), self.__dict__.copy()
            initial_json.pop('_state'), final_json.pop('_state')
            only_changed_fields = {k: {'final_value': final_json[k], 'initial_value': initial_json[k]} for k in initial_json if final_json[k] != initial_json[k]}
            print(only_changed_fields)
        super(A, self).save(force_insert=False, force_update=False, using=None, update_fields=None)

产生只包含已更改字段的输出

{'name': {'initial_value': '1234515', 'final_value': 'nim'}, 'senior': {'initial_value': 'no', 'final_value': 'yes'}}

这是另一种方法。

class Parameter(models.Model):

    def __init__(self, *args, **kwargs):
        super(Parameter, self).__init__(*args, **kwargs)
        self.__original_value = self.value

    def clean(self,*args,**kwargs):
        if self.__original_value == self.value:
            print("igual")
        else:
            print("distinto")

    def save(self,*args,**kwargs):
        self.full_clean()
        return super(Parameter, self).save(*args, **kwargs)
        self.__original_value = self.value

    key = models.CharField(max_length=24, db_index=True, unique=True)
    value = models.CharField(max_length=128)

根据文档:验证对象

full_clean()执行的第二步是调用Model.clean()。应该重写此方法,以便在模型上执行自定义验证。 此方法应用于提供自定义模型验证,并根据需要修改模型上的属性。例如,你可以使用它来自动为一个字段提供一个值,或者进行验证,需要访问多个字段:

在我的解决方案是覆盖目标字段类的pre_save()方法之前,我有这种情况,它只在字段被更改时才会被调用 FileField有用 例子:

class PDFField(FileField):
    def pre_save(self, model_instance, add):
        # do some operations on your file 
        # if and only if you have changed the filefield

劣势: 如果你想做任何(post_save)操作,比如在某些作业中使用创建的对象(如果某些字段已更改),则无用