내 모델에는 다음이 있습니다.
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
별칭을 수정했을 때 새 이미지를 가져 오려면 어떻게 해야합니까? 둘째, 원격 이미지를 캐시하는 더 좋은 방법이 있습니까?
답변
기본적으로 원래 값의 사본을 유지하도록 __init__
메소드 를 대체하려고 models.Model
합니다. 따라서 다른 DB 조회를 수행 할 필요가 없습니다 (항상 좋은 것임).
class Person(models.Model):
name = models.CharField()
__original_name = None
def __init__(self, *args, **kwargs):
super(Person, self).__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(Person, self).save(force_insert, force_update, *args, **kwargs)
self.__original_name = self.name
답변
다음 믹스 인을 사용합니다.
from django.forms.models import model_to_dict
class ModelDiffMixin(object):
"""
A model mixin that tracks model fields' values and provide some useful api
to know what fields have been changed.
"""
def __init__(self, *args, **kwargs):
super(ModelDiffMixin, self).__init__(*args, **kwargs)
self.__initial = self._dict
@property
def diff(self):
d1 = self.__initial
d2 = self._dict
diffs = [(k, (v, d2[k])) for k, v in d1.items() if v != d2[k]]
return dict(diffs)
@property
def has_changed(self):
return bool(self.diff)
@property
def changed_fields(self):
return self.diff.keys()
def get_field_diff(self, field_name):
"""
Returns a diff for field if it's changed and None otherwise.
"""
return self.diff.get(field_name, None)
def save(self, *args, **kwargs):
"""
Saves model and set initial state.
"""
super(ModelDiffMixin, self).save(*args, **kwargs)
self.__initial = self._dict
@property
def _dict(self):
return model_to_dict(self, fields=[field.name for field in
self._meta.fields])
용법:
>>> p = Place()
>>> p.has_changed
False
>>> p.changed_fields
[]
>>> p.rank = 42
>>> p.has_changed
True
>>> p.changed_fields
['rank']
>>> p.diff
{'rank': (0, 42)}
>>> p.categories = [1, 3, 5]
>>> p.diff
{'categories': (None, [1, 3, 5]), 'rank': (0, 42)}
>>> p.get_field_diff('categories')
(None, [1, 3, 5])
>>> p.get_field_diff('rank')
(0, 42)
>>>
노트
이 솔루션은 현재 요청의 상황에서만 잘 작동합니다. 따라서 주로 간단한 경우에 적합합니다. 여러 요청이 동일한 모델 인스턴스를 동시에 조작 할 수있는 동시 환경에서는 반드시 다른 접근 방식이 필요합니다.
답변
가장 좋은 방법은 pre_save
신호입니다. 이 질문에 대한 답을 받았을 때 ’09 년에 옵션이되지 않았을 수도 있지만, 오늘 이것을 보는 사람은 다음과 같이해야합니다.
@receiver(pre_save, sender=MyModel)
def do_something_if_changed(sender, instance, **kwargs):
try:
obj = sender.objects.get(pk=instance.pk)
except sender.DoesNotExist:
pass # Object is new, so field hasn't technically changed, but you may want to do something else here.
else:
if not obj.some_field == instance.some_field: # Field has changed
# do something
답변
이제 직접 답하십시오. 필드 값이 변경되었는지 확인하는 한 가지 방법은 인스턴스를 저장하기 전에 데이터베이스에서 원본 데이터를 가져 오는 것입니다. 이 예제를 고려하십시오.
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!
답변
필드 변경 내용 추적은 django-model-utils에서 사용할 수 있습니다.
https://django-model-utils.readthedocs.org/en/latest/index.html
답변
양식을 사용하는 경우 양식의 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
