суббота, 31 октября 2009 г.

Допиливаем "красивую композицию"

Недавно наткнулся на заметку Красивая композиция от Александра Кошелева (репка) всё было замечательно до момента, когда есть зависимая модель, но определена она ниже по коду. То тут возникает трудность пока модель не подхвачена джангой к модели нельзя подключить обработчик/триггер. Собственно, я немного покопавшись в коде написал небольшой хак, который устраняет данную проблему:
  1. так как указать модель можно sender_model='app_label.Model', то при поиске данной модели функция models.get_model вернет None, т.к. модель не была найдена.
  2. для устранения проблемы нужно все триггеры, которые не нашли модель для подключения внести в список ожидания.
  3. а для того чтобы присоединить ожидающие триггеры нужно подключится к сигналу class_prepared и ждать когда искомая модель будет найдена.
выкладываю код для trigger.py:


from django.db import models
from django.db.models.signals import class_prepared
from django.utils.itercompat import is_iterable

_wait_triggers = []

def _connect_trigger(sender, **kwargs):
connected = []
for trigger in _wait_triggers:
model = models.get_model(*trigger.sender_model.split('.', 1))
if model:
trigger.sender = model
trigger.sender_model = model
trigger.wait_connect = False
trigger.connect()
connected.append(trigger)

for trigger in connected:
_wait_triggers.remove(trigger)

class_prepared.connect(_connect_trigger)

class Trigger(object):
def __init__(self, do, on, field_name, sender, sender_model, commit,\
field_holder_getter):
self.freeze = False
self.field_name = field_name
self.commit = commit
self.wait_connect = False

if sender_model and not sender:
if isinstance(sender_model, basestring):
model = models.get_model(*sender_model.split(".", 1))

if model is None:
self.wait_connect = True
_wait_triggers.append(self)
else:
sender = sender_model = model

self.sender = sender
self.sender_model = sender_model
else:
self.sender = sender
self.sender_model = sender_model

if not do:
raise ValueError("`do` action not defined for trigger")
self.do = do

if not is_iterable(on):
on = [on]
self.on = on

self.field_holder_getter = field_holder_getter

def connect(self):
"""
Connects trigger's handler to all of its signals
"""
if not self.wait_connect:
for signal in self.on:
signal.connect(self.handler, sender=self.sender)

def handler(self, signal, instance=None, **kwargs):
"""
Signal handler
"""
if self.freeze:
return

objects = self.field_holder_getter(instance)
if not is_iterable(objects):
objects = [objects]

for obj in objects:
setattr(obj, self.field_name, self.do(obj, instance, signal))

if self.commit:
obj.save()

2 коммент.:

Александр Кошелев комментирует...

А оно вообще работает?)

Jungle комментирует...

да сам проверял, всё работает
а что должно здесь не работать?