在我的模型中我有:

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-model-changes来做到这一点,而不需要额外的数据库查询:

from django.dispatch import receiver
from django_model_changes import ChangesMixin

class Alias(ChangesMixin, MyBaseModel):
   # your model

@receiver(pre_save, sender=Alias)
def do_something_if_changed(sender, instance, **kwargs):
    if 'remote_image' in instance.changes():
        # do something

其他回答

如果你正在使用一个表单,你可以使用表单的changed_data (docs):

class AliasForm(ModelForm):

    def save(self, commit=True):
        if 'remote_image' in self.changed_data:
            # do things
            remote_image = self.cleaned_data['remote_image']
            do_things(remote_image)
        super(AliasForm, self).save(commit)

    class Meta:
        model = Alias

作为SmileyChris回答的扩展,您可以为last_updated的模型添加一个datetime字段,并在检查更改之前为您将让它达到的最大年龄设置某种限制

另一个迟来的答案,但如果你只是想看看一个新文件是否已经上传到一个文件字段,试试这个:(改编自Christopher Adams对链接http://zmsmith.com/2010/05/django-check-if-a-field-has-changed/的评论在zach的评论这里)

更新链接:https://web.archive.org/web/20130101010327/http://zmsmith.com:80/2010/05/django-check-if-a-field-has-changed/

def save(self, *args, **kw):
    from django.core.files.uploadedfile import UploadedFile
    if hasattr(self.image, 'file') and isinstance(self.image.file, UploadedFile) :
        # Handle FileFields as special cases, because the uploaded filename could be
        # the same as the filename that's already there even though there may
        # be different file contents.

        # if a file was just uploaded, the storage model with be UploadedFile
        # Do new file stuff here
        pass

最佳的解决方案可能是在保存模型实例之前不包括额外的数据库读取操作,也不包括任何进一步的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

改进所有领域的@josh答案:

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

def __init__(self, *args, **kwargs):
    super(Person, self).__init__(*args, **kwargs)
    self._original_fields = dict([(field.attname, getattr(self, field.attname))
        for field in self._meta.local_fields if not isinstance(field, models.ForeignKey)])

def save(self, *args, **kwargs):
  if self.id:
    for field in self._meta.local_fields:
      if not isinstance(field, models.ForeignKey) and\
        self._original_fields[field.name] != getattr(self, field.name):
        # Do Something    
  super(Person, self).save(*args, **kwargs)

澄清一下,getattr的工作是获取像person.name这样带字符串的字段(即getattr(person, "name"))