Jef*_*rry 33 python django django-models
当我的某个模型中的字段发生变化时,如何操作?在这种特殊情况下,我有这个模型:
class Game(models.Model):
STATE_CHOICES = (
('S', 'Setup'),
('A', 'Active'),
('P', 'Paused'),
('F', 'Finished')
)
name = models.CharField(max_length=100)
owner = models.ForeignKey(User)
created = models.DateTimeField(auto_now_add=True)
started = models.DateTimeField(null=True)
state = models.CharField(max_length=1, choices=STATE_CHOICES, default='S')
Run Code Online (Sandbox Code Playgroud)
当状态从Setup变为Active时,我想创建Units,并且'started'字段填充当前日期时间(以及其他内容).
我怀疑需要一个模型实例方法,但是文档似乎没有太多关于以这种方式使用它们的说法.
更新:我已将以下内容添加到我的Game类中:
def __init__(self, *args, **kwargs):
super(Game, self).__init__(*args, **kwargs)
self.old_state = self.state
def save(self, force_insert=False, force_update=False):
if self.old_state == 'S' and self.state == 'A':
self.started = datetime.datetime.now()
super(Game, self).save(force_insert, force_update)
self.old_state = self.state
Run Code Online (Sandbox Code Playgroud)
Dan*_*man 27
它已被回答,但这是一个使用信号,post_init和post_save的例子.
from django.db.models.signals import post_save, post_init
class MyModel(models.Model):
state = models.IntegerField()
previous_state = None
@staticmethod
def post_save(sender, **kwargs):
instance = kwargs.get('instance')
created = kwargs.get('created')
if instance.previous_state != instance.state or created:
do_something_with_state_change()
@staticmethod
def remember_state(sender, **kwargs):
instance = kwargs.get('instance')
instance.previous_state = instance.state
post_save.connect(MyModel.post_save, sender=MyModel)
post_init.connect(MyModel.remember_state, sender=MyModel)
Run Code Online (Sandbox Code Playgroud)
小智 16
Django有一个叫做信号的漂亮功能,它是在特定时间启动的有效触发器:
阅读文档以获取完整信息,但您需要做的就是创建一个接收器功能并将其注册为信号.这通常在models.py中完成.
from django.core.signals import request_finished
def my_callback(sender, **kwargs):
print "Request finished!"
request_finished.connect(my_callback)
Run Code Online (Sandbox Code Playgroud)
简单,嗯?
ars*_*ars 16
基本上,您需要覆盖该save方法,检查state字段是否已更改,started如果需要进行设置,然后让模型基类完成持久化到数据库.
棘手的部分是弄清楚场地是否已经改变.查看此问题中的mixins和其他解决方案,以帮助您解决此问题:
一种方法是为州添加一个setter.这只是一种常规方法,没什么特别的.
class Game(models.Model):
# ... other code
def set_state(self, newstate):
if self.state != newstate:
oldstate = self.state
self.state = newstate
if oldstate == 'S' and newstate == 'A':
self.started = datetime.now()
# create units, etc.
Run Code Online (Sandbox Code Playgroud)
更新:如果你想要这个被触发每当一个变化是到模型实例,您就可以(代替的set_state上面)使用__setattr__方法Game是这样的:
def __setattr__(self, name, value):
if name != "state":
object.__setattr__(self, name, value)
else:
if self.state != value:
oldstate = self.state
object.__setattr__(self, name, value) # use base class setter
if oldstate == 'S' and value == 'A':
self.started = datetime.now()
# create units, etc.
Run Code Online (Sandbox Code Playgroud)
请注意,您不会在Django文档中特别发现这一点,因为它(__setattr__)是标准的Python特性,在此处记录,并且不是特定于Django的.
注意:不知道版本低于1.2的django,但是这个代码使用__setattr__不起作用,它会在第二次if尝试访问时失败self.state.
我试过类似的东西,我试图通过强制初始化state(先在__init__那时)来解决这个问题,__new__但这会导致令人讨厌的意外行为.
我正在编辑而不是出于显而易见的原因进行评论,还有:我不会删除这段代码,因为它可能适用于较旧(或未来?)版本的django,并且可能还有另一种解决方法可以解决这个self.state问题.我不知道
小智 5
@dcramer 针对这个问题提出了一个更优雅的解决方案(在我看来)。
https://gist.github.com/730765
from django.db.models.signals import post_init
def track_data(*fields):
"""
Tracks property changes on a model instance.
The changed list of properties is refreshed on model initialization
and save.
>>> @track_data('name')
>>> class Post(models.Model):
>>> name = models.CharField(...)
>>>
>>> @classmethod
>>> def post_save(cls, sender, instance, created, **kwargs):
>>> if instance.has_changed('name'):
>>> print "Hooray!"
"""
UNSAVED = dict()
def _store(self):
"Updates a local copy of attributes values"
if self.id:
self.__data = dict((f, getattr(self, f)) for f in fields)
else:
self.__data = UNSAVED
def inner(cls):
# contains a local copy of the previous values of attributes
cls.__data = {}
def has_changed(self, field):
"Returns ``True`` if ``field`` has changed since initialization."
if self.__data is UNSAVED:
return False
return self.__data.get(field) != getattr(self, field)
cls.has_changed = has_changed
def old_value(self, field):
"Returns the previous value of ``field``"
return self.__data.get(field)
cls.old_value = old_value
def whats_changed(self):
"Returns a list of changed attributes."
changed = {}
if self.__data is UNSAVED:
return changed
for k, v in self.__data.iteritems():
if v != getattr(self, k):
changed[k] = v
return changed
cls.whats_changed = whats_changed
# Ensure we are updating local attributes on model init
def _post_init(sender, instance, **kwargs):
_store(instance)
post_init.connect(_post_init, sender=cls, weak=False)
# Ensure we are updating local attributes on model save
def save(self, *args, **kwargs):
save._original(self, *args, **kwargs)
_store(self)
save._original = cls.save
cls.save = save
return cls
return inner
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
34450 次 |
| 最近记录: |