Вдохновленный ответом воска , реализация может быть более безопасной и не пропускать другую пост-обработку, если добавлены определения, а не построены одиночные:
public interface MultiBeanFactory { // N.B. should not implement FactoryBean
T getObject(String name) throws Exception;
Class> getObjectType();
Collection getNames();
}
public class MultiBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
Map factories = beanFactory.getBeansOfType(MultiBeanFactory.class);
for (Map.Entry entry : factories.entrySet()) {
MultiBeanFactory factoryBean = entry.getValue();
for (String name : factoryBean.getNames()) {
BeanDefinition definition = BeanDefinitionBuilder
.genericBeanDefinition(factoryBean.getObjectType())
.setScope(BeanDefinition.SCOPE_SINGLETON)
.setFactoryMethod("getObject")
.addConstructorArgValue(name)
.getBeanDefinition();
definition.setFactoryBeanName(entry.getKey());
registry.registerBeanDefinition(entry.getKey() + "_" + name, definition);
}
}
}
}
@Configuration
public class Config {
@Bean
public static MultiBeanFactoryPostProcessor() {
return new MultiBeanFactoryPostProcessor();
}
@Bean
public MultiBeanFactory personFactory() {
return new MultiBeanFactory() {
public Person getObject(String name) throws Exception {
// ...
}
public Class> getObjectType() {
return Person.class;
}
public Collection getNames() {
return Arrays.asList("Joe Smith", "Mary Williams");
}
};
}
}
Названия bean все еще происходят из любого места, например, пример воска @Qualifier
. Существуют различные другие свойства в определении компонента, включая способность наследовать от самого завода.
По сути, вам необходимо переопределить метод save
, проверить, было ли изменено поле state
, установить started
, если необходимо, и затем позволить базовому классу модели завершить сохранение в базе данных.
Хитрая часть выяснить, если поле было изменено. Ознакомьтесь с миксинами и другими решениями в этом вопросе, чтобы помочь вам с этим:
У Django есть отличная особенность, называемая сигналов , которые фактически являются триггерами, которые запускаются в определенное время:
Прочитайте документы для получения полной информации, но все, что вам нужно сделать, это создать функция приемника и зарегистрировать его в качестве сигнала. Обычно это делается в models.py.
from django.core.signals import request_finished
def my_callback(sender, **kwargs):
print "Request finished!"
request_finished.connect(my_callback)
Просто, а?
@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
Один из способов - добавить установщик состояния. Это обычный метод, ничего особенного.
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.
Обновление: Если вы хотите, чтобы это запускалось всякий раз, когда вносятся изменения в экземпляр модели, вы можете ( вместо из 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.
Обратите внимание, что вы не найдете его в документации Django , поскольку он ( __ setattr __
) является стандартной функцией Python, описанной здесь , и не является специфической для Django.
примечание: не знаю о версиях django старше 1.2 , но этот код, использующий __ setattr __
, не будет работать, он выйдет из строя сразу после второго if
при попытке доступа к self.state
.