Django: Rellenar ID de usuario al guardar un modelo

Tengo un modelo con un campo created_by que está vinculado al modelo estándar de usuario de Django. Necesito rellenar esto automáticamente con el ID del usuario actual cuando se guarda el modelo. No puedo hacer esto en la capa Admin, ya que la mayoría de las partes del sitio no usarán el administrador incorporado. ¿Alguien puede aconsejarme sobre cómo debo hacer esto?

Si desea algo que funcione tanto en el administrador como en cualquier otro lugar, debe usar un formulario modelo personalizado. La idea básica es anular el método __init__ para tomar un parámetro adicional – solicitud – y almacenarlo como un atributo del formulario, luego también anular el método guardar para establecer la identificación del usuario antes de guardarla en la base de datos.

 class MyModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.request = kwargs.pop('request', None) return super(MyModelForm, self).__init__(*args, **kwargs) def save(self, *args, **kwargs): kwargs['commit']=False obj = super(MyModelForm, self).save(*args, **kwargs) if self.request: obj.user = self.request.user obj.save() return obj 

La respuesta de Daniel no funcionará directamente para el administrador porque necesita pasar el objeto de solicitud. Es posible que pueda hacer esto anulando el método get_form en su clase ModelAdmin , pero probablemente sea más fácil mantenerse alejado de la personalización del formulario y simplemente anular save_model en su ModelAdmin .

 def save_model(self, request, obj, form, change): """When creating a new object, set the creator field. """ if not change: obj.creator = request.user obj.save() 

La forma menos obstructiva es usar un CurrentUserMiddleware para almacenar el usuario actual en un objeto local thread:

current_user.py

 from threading import local _user = local() class CurrentUserMiddleware(object): def process_request(self, request): _user.value = request.user def get_current_user(): return _user.value 

Ahora solo necesita agregar este middleware a MIDDLEWARE_CLASSES después del middleware de autenticación.

settings.py

 MIDDLEWARE_CLASSES = ( ... 'django.contrib.auth.middleware.AuthenticationMiddleware', ... 'current_user.CurrentUserMiddleware', ... ) 

Su modelo ahora puede usar la función get_current_user para acceder al usuario sin tener que pasar el objeto de solicitud.

models.py

 from django.db import models from current_user import get_current_user class MyModel(models.Model): created_by = models.ForeignKey('auth.User', default=get_current_user) 

Insinuación:

Si está utilizando Django CMS, ni siquiera necesita definir su propio CurrentUserMiddleware, pero puede usar cms.middleware.user.CurrentUserMiddleware y la función cms.utils.permissions.get_current_user para recuperar el usuario actual.

Todo este enfoque me molestó mucho. Quería decirlo exactamente una vez, así que lo implementé en middleware. Simplemente agregue WhodidMiddleware después de su middleware de autenticación.

Si sus campos created_by & modified_by están configurados como editable = False entonces no tendrá que cambiar ninguno de sus formularios.

 """Add user created_by and modified_by foreign key refs to any model automatically. Almost entirely taken from https://github.com/Atomidata/django-audit-log/blob/master/audit_log/middleware.py""" from django.db.models import signals from django.utils.functional import curry class WhodidMiddleware(object): def process_request(self, request): if not request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): if hasattr(request, 'user') and request.user.is_authenticated(): user = request.user else: user = None mark_whodid = curry(self.mark_whodid, user) signals.pre_save.connect(mark_whodid, dispatch_uid = (self.__class__, request,), weak = False) def process_response(self, request, response): signals.pre_save.disconnect(dispatch_uid = (self.__class__, request,)) return response def mark_whodid(self, user, sender, instance, **kwargs): if 'created_by' in instance._meta.fields and not instance.created_by: instance.created_by = user if 'modified_by' in instance._meta.fields: instance.modified_by = user 

Si usa vistas basadas en clases, la respuesta de Daniel necesita más. Agregue lo siguiente para asegurarse de que el objeto de solicitud esté disponible para nosotros en su objeto ModelForm

 class BaseCreateView(CreateView): def get_form_kwargs(self): """ Returns the keyword arguments for instanciating the form. """ kwargs = {'initial': self.get_initial()} if self.request.method in ('POST', 'PUT'): kwargs.update({ 'data': self.request.POST, 'files': self.request.FILES, 'request': self.request}) return kwargs 

Además, como ya se mencionó, debe devolver el obj al final de ModelForm.save ()

así es como lo hago con vistas genéricas:

 class MyView(CreateView): model = MyModel def form_valid(self, form): object = form.save(commit=False) object.owner = self.request.user object.save() return super(MyView, self).form_valid(form) 

Basado en la respuesta de Bikeshedder , encontré una solución ya que en realidad no funcionaba para mí.

  1. aplicación / middleware / current_user.py

     from threading import local _user = local() class CurrentUserMiddleware(object): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): _user.value = request.user return self.get_response(request) def get_current_user(): return _user.value 
  2. settings.py

     MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'common.middleware.current_user.CurrentUserMiddleware', ] 
  3. model.py

     from common.middleware import current_user created_by = models.ForeignKey(User, blank=False, related_name='created_by', editable=False, default=current_user.get_current_user) 

Estoy usando python 3.5 y django 1.11.3

Para hacer esto en el sitio de administración, consulte Autocompletar el campo created_by con el sitio de administración de Django

El método ‘guardar’ de forms.ModelForm devuelve el instanciado guardado.

Debería agregar una última línea a MyModelForm:

obj regreso

Este cambio es necesario si está utilizando vistas genéricas create_object o update_object.
Usan el objeto guardado para hacer la redirección.

No creo que la respuesta de Daniel sea la mejor, ya que cambia el comportamiento predeterminado de un formulario modelo al guardar siempre el objeto.

El código que usaría:

forms.py

 from django import forms class MyModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.user = kwargs.pop('user', None) super(MyModelForm, self).__init__(*args, **kwargs) def save(self, commit=True): obj = super(MyModelForm, self).save(commit=False) if obj.created_by_id is None: obj.created_by = self.user if commit: obj.save() return obj 

Para futuras referencias, la mejor solución que encontré sobre este tema:

https://pypi.python.org/pypi/django-crum/0.6.1

Esta biblioteca consiste en algún middleware. Después de configurar esta biblioteca, simplemente anule el método de guardar del modelo y haga lo siguiente,

 from crum import get_current_user def save(self, *args, **kwargs): user = get_current_user() if not self.pk: self.created_by = user else: self.changed_by = user super(Foomodel, self).save(*args, **kwargs) 

si crea y abstrae el modelo y lo hereda para todo su modelo, obtendrá sus campos auto poblados create_by y changed_by.

¿cuál es el problema con el uso de algo así como:

 class MyModelForm(forms.ModelForm): class Meta: model = MyModel exclude = ['created_by'] def save(self, user): obj = super().save(commit = False) obj.created_by = user obj.save() return obj 

Ahora llámalo como myform.save(request.user) en las vistas .

aquí está la función de guardar de ModelForm , que tiene solo un parámetro de commit .

Tenga en cuenta que si estaba buscando esto, pero agregando lo siguiente

 user = models.ForeignKey('auth.User') 

a un modelo trabajará para agregar la identificación del usuario al modelo.

A continuación, cada jerarquía pertenece a un usuario.

 class Hierarchy(models.Model): user = models.ForeignKey('auth.User') name = models.CharField(max_length=200) desc = models.CharField(max_length=1500)