Как предотвратить конфликты приборов с конфликтом с сигнальным кодом django post_save?
В моем приложении я хочу создавать записи в определенных таблицах при подписке нового пользователя. Например, я хочу создать userprofile, который затем будет ссылаться на их компанию и некоторые другие записи для них. Я реализовал это с помощью сигнала post_save:
def callback_create_profile(sender, **kwargs):
# check if we are creating a new User
if kwargs.get('created', True):
user = kwargs.get('instance')
company = Company.objects.create(name="My Company")
employee = Employee.objects.create(company=company, name_first=user.first_name, name_last=user.last_name)
profile = UserProfile.objects.create(user=user, employee=employee, partner=partner)
# Register the callback
post_save.connect(callback_create_profile, sender=User, dispatch_uid="core.models")
Это хорошо работает при запуске. Я могу использовать администратора для создания нового пользователя, а остальные три таблицы также получают заметки. (За исключением того, что сотрудник, так как user.first_name и user.last_name не заполняются в форме администратора при его сохранении. Я все еще не понимаю, почему это делается так)
Проблема возникла, когда я запустил свой тестовый пакет. До этого я создал кучу светильников для создания этих записей в таблицах. Теперь я получаю сообщение об ошибке:
IntegrityError: duplicate key value violates unique constraint "core_userprofile_user_id_key"
Я думаю, это связано с тем, что я уже создал записи компании, сотрудника и профиля в устройстве с идентификатором "1", и теперь сигнал post_save пытается воссоздать его.
Мои квесты: могу ли я отключить этот сигнал post_save при работе светильников? Могу ли я обнаружить, что я запускаю часть тестового набора и не создаю эти записи? Должен ли я удалить эти записи из светильников сейчас (хотя сигнал устанавливает только значения по умолчанию, а не те значения, которые я хочу тестировать)? Почему код загрузки устройства не перезаписывает созданные записи?
Как люди это делают?
Ответы
Ответ 1
Думаю, я понял, как это сделать. В kwargs, переданном вместе с сигналами, есть "сырой" параметр, поэтому я могу заменить мой тест выше на этот:
if (kwargs.get('created', True) and not kwargs.get('raw', False)):
Raw используется при запуске loaddata. Это похоже на трюк.
Здесь упоминается: http://code.djangoproject.com/ticket/13299
Было бы неплохо, если бы это было задокументировано: http://docs.djangoproject.com/en/1.2/ref/signals/#django.db.models.signals.post_save
Ответ 2
Это старый вопрос, но решение, которое я нашел наиболее простым, - это использовать "сырой" аргумент, переданный данными загрузки, и украсить функции слушателя, например:
from functools import wraps
def disable_for_loaddata(signal_handler):
@wraps(signal_handler)
def wrapper(*args, **kwargs):
if kwargs['raw']:
print "Skipping signal for %s %s" % (args, kwargs)
return
signal_handler(*args, **kwargs)
return wrapper
а затем
@disable_for_loaddata
def callback_create_profile(sender, **kwargs):
# check if we are creating a new User
...
Ответ 3
Простое решение, добавьте это в начало своей функции post_save:
if kwargs.get('raw', False):
return False
Это приведет к выходу этой функции при загрузке прибора.
Смотрите: https://docs.djangoproject.com/en/dev/ref/signals/#post-save
Ответ 4
Я столкнулся с аналогичной проблемой в одном из моих проектов. В моем случае сигналы также замедляли тесты. В итоге я отказался от сигналов в пользу переопределения метода Model.save()
.
В вашем случае, однако, я не думаю, что имеет смысл достичь этого, переопределив любые методы save()
. В этом случае вы можете попробовать это. Предупреждение, я попробовал только один раз. Казалось, что он работает, но не полностью протестирован.