在我的模型中我有:

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时,我如何获取一个新的图像?其次,是否有更好的方法来缓存远程映像?


当前回答

如果您对重写save方法不感兴趣,可以这样做

  model_fields = [f.name for f in YourModel._meta.get_fields()]
  valid_data = {
        key: new_data[key]
        for key in model_fields
        if key in new_data.keys()
  }

  for (key, value) in valid_data.items():
        if getattr(instance, key) != value:
           print ('Data has changed')

        setattr(instance, key, value)

 instance.save()

其他回答

用David Cramer的解决方案怎么样:

http://cramer.io/2010/12/06/tracking-changes-to-fields-in-django/

我曾经这样成功地使用过:

@track_data('name')
class Mode(models.Model):
    name = models.CharField(max_length=5)
    mode = models.CharField(max_length=5)

    def save(self, *args, **kwargs):
        if self.has_changed('name'):
            print 'name changed'

    # OR #

    @classmethod
    def post_save(cls, sender, instance, created, **kwargs):
        if instance.has_changed('name'):
            print "Hooray!"

本质上,你想要重写模型的__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

我有点晚了,但我也找到了这个解决方案: Django脏域

现在直接回答:检查字段的值是否已经改变的一种方法是在保存实例之前从数据库获取原始数据。想想这个例子:

class MyModel(models.Model):
    f1 = models.CharField(max_length=1)

    def save(self, *args, **kw):
        if self.pk is not None:
            orig = MyModel.objects.get(pk=self.pk)
            if orig.f1 != self.f1:
                print 'f1 changed'
        super(MyModel, self).save(*args, **kw)

同样的事情也适用于处理表单。您可以在ModelForm的clean或save方法中检测到它:

class MyModelForm(forms.ModelForm):

    def clean(self):
        cleaned_data = super(ProjectForm, self).clean()
        #if self.has_changed():  # new instance or existing updated (form has data to save)
        if self.instance.pk is not None:  # new instance only
            if self.instance.f1 != cleaned_data['f1']:
                print 'f1 changed'
        return cleaned_data

    class Meta:
        model = MyModel
        exclude = []

自从Django 1.8发布以来,你可以使用from_db类方法来缓存remote_image的旧值。然后在保存方法中,您可以比较字段的旧值和新值,以检查该值是否已更改。

@classmethod
def from_db(cls, db, field_names, values):
    new = super(Alias, cls).from_db(db, field_names, values)
    # cache value went from the base
    new._loaded_remote_image = values[field_names.index('remote_image')]
    return new

def save(self, force_insert=False, force_update=False, using=None,
         update_fields=None):
    if (self._state.adding and self.remote_image) or \
        (not self._state.adding and self._loaded_remote_image != self.remote_image):
        # If it is first save and there is no cached remote_image but there is new one, 
        # or the value of remote_image has changed - do your stuff!