2011-01-18 22 views

Respuesta

10

si está utilizando el sitio de administración de por qué no utilizar un modelo de administración cutom

class MyModelAdmin(admin.ModelAdmin): 
    def save_model(self, request, obj, form, change): 
     #pre save stuff here 
     obj.save() 
     #post save stuff here 



admin.site.register(MyModel, MyModelAdmin) 

una señal es algo que se dispara cada vez que se guarda el objeto sin importar si lo está haciendo el administrador o algún proceso que no está vinculado a una solicitud y no es realmente un lugar apropiado para ser haciendo acciones basadas en solicitud

+0

En febrero de 2015. ¿Sigue siendo esta la única forma de obtener la solicitud del usuario en la señal de Django? –

+0

Esta es la respuesta correcta, junto con [ModelAdmin.exclude] (https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.exclude) para los campos prepoblados necesarios es muy útil. – chirale

+0

Esto funcionará para algunos casos, pero no para todos, p. los cambios en las relaciones 'm2m' solo pueden engancharse mediante señales. – Ben

1

En ambas señales, la señal de enviar tres argumentos,

  • Sender
  • Instancia
  • Usando

Lo que necesita se está modificando la instantánea ...

def signal_catcher(sender, instance, **kwargs): 
    uname = instance.username 

http://docs.djangoproject.com/en/dev/ref/signals/#pre-save

+0

Idea correcta, pero los argumentos son al revés. 'sender' (la clase que envía la señal) siempre es primero. –

+0

Corregido, gracias ... Lo que dices es el estándar djanog, pero (por lo que lo usé) usando los parámetros en (instancia, remitente) el orden también funciona (al menos en mi código) ... – FallenAngel

+6

uname = instance .username requiere que en su modelo tenga un campo llamado username? .... Que no es mi caso ... – Djanux

1

Puede utilizar un middleware para almacenar el usuario actual: http://djangosnippets.org/snippets/2179/

, entonces sería capaz de llegar al usuario con get_current_user()

+1

Esa solución se basa en Thread Locals. Esos son considerados 'malos' por varias razones. http://stackoverflow.com/questions/3227180/why-is-using-thread-locals-in-django-bad –

+0

Solo un comentario sobre lo que @ AaronC.deBruyn dijo - el enlace que publicó en realidad no está de acuerdo con su declaración, y la opinión popular concluye que el uso de locales de hilo para este propósito ** es aceptable **. – Ben

0

Podemos resolver este problema utilizando las clases de middleware. Crea una clase singleton en donde se almacenará la variable de usuario.

class Singleton(type): 
    ''' 
     Singleton pattern requires for GetUser class 
    ''' 
    def __init__(cls, name, bases, dicts): 
     cls.instance = None 

    def __call__(cls, *args, **kwargs): 
     if cls.instance is None: 
      cls.instance = super(Singleton, cls).__call__(*args, **kwargs) 
     return cls.instance 


class NotLoggedInUserException(Exception): 
    ''' 
    ''' 
    def __init__(self, val='No users have been logged in'): 
     self.val = val 
     super(NotLoggedInUser, self).__init__() 

    def __str__(self): 
     return self.val 

class LoggedInUser(object): 
    __metaclass__ = Singleton 

    user = None 

    def set_user(self, request): 
     if request.user.is_authenticated(): 
      self.user = request.user 

    @property 
    def current_user(self): 
     ''' 
      Return current user or raise Exception 
     ''' 
     if self.user is None: 
      raise NotLoggedInUserException() 
     return self.user 

    @property 
    def have_user(self): 
    return not user is None 

Crear propia clase middleware que se ajuste de usuario, por ejemplo, loggedInUser, e insertar a cabo después de middleware 'django.contrib.auth.middleware.AuthenticationMiddleware' en settings.py

from useranytimeaccess import LoggedInUser 
class LoggedInUserMiddleware(object): 
    ''' 
     Insert this middleware after django.contrib.auth.middleware.AuthenticationMiddleware 
    ''' 
    def process_request(self, request): 
     ''' 
      Returned None for continue request 
     ''' 
     logged_in_user = LoggedInUser() 
     logged_in_user.set_user(request) 
     return None 

En las señales de importación loggedInUser clase y obtener usuario actual

logged_in = LoggedInUser() 
user = logged_in.user 
16

Al ser reacio a perder el tiempo con el estado local de subprocesos, decidí probar un enfoque diferente. Por lo que puedo decir, los controladores de señal post_save y pre_save se llaman de forma síncrona en el hilo que llama a save(). Si estamos en el bucle de manejo de solicitudes normal, podemos simplemente subir la pila para encontrar el objeto de solicitud como una variable local en alguna parte. p.ej.

from django.db.models.signals import pre_save 
from django.dispatch import receiver 

@receiver(pre_save) 
def my_callback(sender, **kwargs): 
    import inspect 
    for frame_record in inspect.stack(): 
     if frame_record[3]=='get_response': 
      request = frame_record[0].f_locals['request'] 
      break 
    else: 
     request = None 
    ... 

Si hay una solicitud actual, se puede agarrar el atributo user de ella.

Nota: como se dice en los documentos inspect módulo,

Esta función depende del apoyo de marco de pila de Python en el intérprete, que no está garantizada de existir en todas las implementaciones de Python.

+0

Usted señor, es una leyenda. Solución brillante, funciona tal como se anuncia. – Bosco

+0

pros y contras de este enfoque? –

+2

** Pros ** - Funciona, es más sencillo de implementar y evita locales de subprocesos (puede o no ser algo malo) ** Contras ** - Rendimiento alcanzado (tiene que escanear la pila de llamadas potencialmente para cada señal), no funcionará a través de pruebas: deberá agregar lógica adicional para extraer objetos de solicitud de sus pruebas – Ben

Cuestiones relacionadas