2010-08-17 16 views
40

En mi aplicación, quiero crear entradas en ciertas tablas cuando un nuevo usuario se registra. Por ejemplo, quiero crear un perfil de usuario que luego haga referencia a su empresa y algunos otros registros para ellos. Implementé esto con una señal de post_save:¿Cómo evito que los dispositivos entren en conflicto con el código de señal de django 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") 

Esto funciona bien cuando se ejecuta. Puedo usar el administrador para crear un nuevo usuario y las otras tres tablas también obtienen entradas con sensatez. (Excepto que es el empleado dado que el usuario.primer_nombre y usuario.último_nombre no se completan en el formulario del administrador cuando se guarda. Aún no entiendo por qué se hace así)

El problema surgió cuando Ejecuté mi suite de prueba. Antes de esto, había creado un conjunto de accesorios para crear estas entradas en las tablas. Ahora me sale un error que indica:

IntegrityError: duplicate key value violates unique constraint "core_userprofile_user_id_key" 

Creo que esto se debe a que ya ha creado una empresa, los empleados y de perfil registros en el dispositivo con el ID "1" y ahora la señal post_save está tratando de volver a crearlo.

Mis preguntas son: ¿puedo desactivar esta señal de post_save cuando se ejecutan dispositivos? ¿Puedo detectar que estoy ejecutándose como parte del conjunto de pruebas y no crear estos registros? ¿Debería eliminar estos registros de los aparatos ahora (aunque la señal solo establece los valores predeterminados no los valores con los que quiero probar)? ¿Por qué el código de carga del dispositivo simplemente sobrescribe los registros creados?

¿Cómo hace la gente esto?

Respuesta

59

Creo que descubrí una forma de hacerlo. Existe un parámetro 'en bruto' en los kwargs aprobadas en junto con las señales para que pueda sustituir a mi ensayo anterior con éste:

if (kwargs.get('created', True) and not kwargs.get('raw', False)): 

prima se utiliza cuando se está ejecutando loaddata. Esto parece hacer el truco.

Se menciona aquí: http://code.djangoproject.com/ticket/13299

sería bueno si esto fue documentado: http://docs.djangoproject.com/en/1.2/ref/signals/#django.db.models.signals.post_save

+0

+1. No lo sabía. Lo tendré en cuenta. –

+0

2 años después, esta es la mejor manera que encontré para manejar esto. Tuve que hacer esto para el modelo de perfil de usuario personalizado (AUTH_PROFILE_MODULE) donde crearía una pequeña lista de usuarios predeterminados de mi aplicación. Simplemente verifiqué dentro de mi señal personalizada si estaba 'sin procesar' en kwargs. – Jordan

+1

¡Gran solución! Me salvaste un gran dolor de cabeza esta mañana. Para simplificar esto un poco, uno podría usar el argumento nombrado 'created' y eliminar los paréntesis redundantes: ' 'si se crea y no kwargs.get ('raw', False):' ' –

1

Me enfrenté a un problema similar en uno de mis proyectos. En mi caso, las señales también estaban ralentizando las pruebas. Terminé abandonando las señales a favor de anular un método Model.save() en su lugar.

En su caso, sin embargo, no creo que tenga sentido lograr esto anulando cualquier método save(). En ese caso, es posible que desee probar esto. Advertencia, solo lo intenté una vez. Se parecía para trabajar, pero no ha sido probado a fondo.

  1. Crear your own test runner.
  2. Antes de cargar los dispositivos, disconnect la función callback_create_profile de User clase 'post_save señal.
  3. Deje que los dispositivos se carguen.
  4. Vuelva a conectar la función a la señal.
+0

Oh, que sería un enfoque así ... yo no sabía que se podía desconectar las señales. – poswald

14

Ésta es una vieja pregunta, pero la solución que he encontrado más sencilla es utilizar el argumento 'en bruto' , aprobada por datos de carga, y decorar las funciones de detector, por ejemplo:

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 

y luego

@disable_for_loaddata 
def callback_create_profile(sender, **kwargs): 
    # check if we are creating a new User 
    ... 
+0

Vale la pena señalar que el m2m las señales no proporcionan la bandera "en bruto". No estoy seguro de cómo solucionar esto. –

Cuestiones relacionadas