Django: remplir L'ID utilisateur lors de la sauvegarde d'un modèle
j'ai un modèle avec un created_by field qui est lié au Modèle standard de L'utilisateur Django. J'ai besoin de remplir automatiquement ce avec l'ID de l'Utilisateur courant lorsque le modèle est enregistré. Je ne peux pas faire cela au niveau de l'administrateur, car la plupart des parties du site n'utiliseront pas L'Administrateur intégré. Quelqu'un peut-il informer sur la façon dont je devrais aller à ce sujet?
13 réponses
si vous voulez quelque chose qui fonctionne à la fois dans l'administrateur et ailleurs, vous devez utiliser une forme de modèle personnalisée. L'idée de base est d'outrepasser la méthode __init__
pour prendre un paramètre supplémentaire - request - et le stocker comme un attribut du formulaire, puis aussi de outrepasser la méthode de sauvegarde pour définir l'ID utilisateur avant de sauvegarder dans la base de données.
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 manière la moins obstruante est d'utiliser un CurrentUserMiddleware
pour stocker l'utilisateur actuel dans un objet local de 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
Maintenant, vous n'avez plus qu'à ajouter cet middleware à vos MIDDLEWARE_CLASSES après l'middleware d'authentification.
settings.py
MIDDLEWARE_CLASSES = (
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
...
'current_user.CurrentUserMiddleware',
...
)
votre modèle peut maintenant utiliser la fonction get_current_user
pour accéder à l'utilisateur sans la réussite de la demande de l'objet autour de la.
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)
indice:
si vous utilisez Django CMS vous n'avez même pas besoin de définir votre propre CurrentUserMiddleware mais pouvez utiliser cms.middleware.user.CurrentUserMiddleware
et la fonction cms.utils.permissions.get_current_user
pour récupérer l'utilisateur courant.
la réponse de Daniel ne fonctionnera pas directement pour l'administrateur car vous devez passer dans l'objet request. Vous pourriez être en mesure de le faire en remplaçant la méthode get_form
dans votre classe ModelAdmin
, mais il est probablement plus facile de rester à l'écart de la personnalisation du formulaire et de simplement remplacer save_model
dans votre 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()
cette approche m'a mis sur écoute. Je voulais le dire exactement une fois, donc je l'ai implémenté dans middleware. Il suffit d'ajouter WhodidMiddleware après votre middleware d'authentification.
Si votre created_by & modified_by champs sont mis à editable = False
ensuite, vous n'aurez pas à modifier vos formes.
"""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 vous utilisez des vues de classe la réponse de Daniel a besoin de plus. Ajouter ce qui suit pour s'assurer que l'objet request est disponible pour nous dans votre objet 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
aussi, comme déjà mentionné, vous devez retourner l'obj à la fin de ModelForm.save ()
voici comment je le fais avec des génériques de vues:
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)
basé sur bikeshedder 'S réponse, j'ai trouvé une solution depuis son ne fonctionne pas réellement pour moi.
-
app/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
-
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', ]
-
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)
j'utilise python 3.5 et django 1.11.3
Pour le faire dans l'admin du site, voir Auto-remplissage created_by champ avec Django admin du site
Le "sauver" de la méthode de formes.ModelForm renvoie l'instancié sauvegardé.
vous devez ajouter une dernière ligne à MyModelForm:
...
retour obj
cette modification est nécessaire si vous utilisez create_object ou update_object generic views.
Ils utilisent l'objet sauvegardé pour faire la redirection.
Je ne crois pas que la réponse de Daniel soit la meilleure qui soit puisqu'elle change le comportement par défaut d'un modèle en sauvegardant toujours l'objet.
le code que j'utiliserais:
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
pour les références futures, la meilleure solution que j'ai trouvée à ce sujet:
https://pypi.python.org/pypi/django-crum/0.6.1
cette bibliothèque se compose d'un middleware. Après avoir configuré ce libary, il suffit de surcharger la méthode save du model et de faire ce qui suit,
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 vous créez et abstraites le model et en héritez pour tout votre model, vous obtenez votre created_by auto-peuplée et changed_by champs.
Quel est le problème avec quelque chose comme:
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
maintenant appelez-le comme myform.save(request.user)
dans le vues .
voici fonction de sauvegarde de ModelForm , qui n'a qu'un paramètre commit
.
notez bien si vous cherchiez ceci, mais en ajoutant la suivante
user = models.ForeignKey('auth.User')
à un modèle fonctionnera pour ajouter l'ID utilisateur au modèle.
dans ce qui suit, chaque hiérarchie appartient à un utilisateur.
class Hierarchy(models.Model):
user = models.ForeignKey('auth.User')
name = models.CharField(max_length=200)
desc = models.CharField(max_length=1500)