在我的模型中我有:
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
这是另一种方法。
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()。应该重写此方法,以便在模型上执行自定义验证。
此方法应用于提供自定义模型验证,并根据需要修改模型上的属性。例如,你可以使用它来自动为一个字段提供一个值,或者进行验证,需要访问多个字段:
有时我想检查多个共享这些字段的模型上相同特定字段的更改,因此我定义了这些字段的列表并使用一个信号。在这种情况下,只有当某些东西发生了变化,或者条目是新的时,地理编码才会寻址:
from django.db.models.signals import pre_save
from django.dispatch import receiver
@receiver(pre_save, sender=SomeUserProfileModel)
@receiver(pre_save, sender=SomePlaceModel)
@receiver(pre_save, sender=SomeOrganizationModel)
@receiver(pre_save, sender=SomeContactInfoModel)
def geocode_address(sender, instance, *args, **kwargs):
input_fields = ['address_line', 'address_line_2', 'city', 'state', 'postal_code', 'country']
try:
orig = sender.objects.get(id=instance.id)
if orig:
changes = 0
for field in input_fields:
if not (getattr(instance, field)) == (getattr(orig, field)):
changes += 1
if changes > 0:
# do something here because at least one field changed...
my_geocoder_function(instance)
except:
# do something here because there is no original, or pass.
my_geocoder_function(instance)
只编写一次并使用“@receiver”附加当然胜过重写多个模型保存方法,但也许其他人有更好的想法。
我对@iperelivskiy的解决方案的看法是:在大规模的情况下,为每个__init__创建_initial字典是昂贵的,而且大多数时候是不必要的。我稍微改变了mixin,这样它只在你显式地告诉它这样做(通过调用instance.track_changes)时才记录更改:
from typing import KeysView, Optional
from django.forms import model_to_dict
class TrackChangesMixin:
_snapshot: Optional[dict] = None
def track_changes(self):
self._snapshot = self.as_dict
@property
def diff(self) -> dict:
if self._snapshot is None:
raise ValueError("track_changes wasn't called, can't determine diff.")
d1 = self._snapshot
d2 = self.as_dict
diffs = [(k, (v, d2[k])) for k, v in d1.items() if str(v) != str(d2[k])]
return dict(diffs)
@property
def has_changed(self) -> bool:
return bool(self.diff)
@property
def changed_fields(self) -> KeysView:
return self.diff.keys()
@property
def as_dict(self) -> dict:
return model_to_dict(self, fields=[field.name for field in self._meta.fields])
改进所有领域的@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"))