Действия, инициированные полем, изменяются в Django

Вдохновленный ответом воска , реализация может быть более безопасной и не пропускать другую пост-обработку, если добавлены определения, а не построены одиночные:

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. Существуют различные другие свойства в определении компонента, включая способность наследовать от самого завода.

30
задан Jeff Bradberry 29 July 2009 в 21:58
поделиться

4 ответа

По сути, вам необходимо переопределить метод save, проверить, было ли изменено поле state, установить started, если необходимо, и затем позволить базовому классу модели завершить сохранение в базе данных.

Хитрая часть выяснить, если поле было изменено. Ознакомьтесь с миксинами и другими решениями в этом вопросе, чтобы помочь вам с этим:

13
ответ дан Community 11 October 2019 в 13:19
поделиться

У Django есть отличная особенность, называемая сигналов , которые фактически являются триггерами, которые запускаются в определенное время:

  • До / после метод сохранения модели называется
  • До / после вызова метода удаления модели
  • До / после выполнения HTTP-запроса

Прочитайте документы для получения полной информации, но все, что вам нужно сделать, это создать функция приемника и зарегистрировать его в качестве сигнала. Обычно это делается в models.py.

from django.core.signals import request_finished

def my_callback(sender, **kwargs):
    print "Request finished!"

request_finished.connect(my_callback)

Просто, а?

16
ответ дан 11 October 2019 в 13:19
поделиться

@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
4
ответ дан lucmult 11 October 2019 в 13:19
поделиться

Один из способов - добавить установщик состояния. Это обычный метод, ничего особенного.

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 .

8
ответ дан 27 November 2019 в 18:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: