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?

40
demandé sur Matt Ball 2009-05-14 14:02:42

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
32
répondu Daniel Roseman 2014-12-03 15:21:58

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.

23
répondu bikeshedder 2014-02-14 18:23:26

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()
22
répondu Tim Fletcher 2011-07-07 17:36:31

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
13
répondu mindlace 2012-10-29 09:42:47

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 ()

7
répondu Monster 2013-03-07 10:36:43

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)
6
répondu yretuta 2013-03-21 05:32:03

basé sur bikeshedder 'S réponse, j'ai trouvé une solution depuis son ne fonctionne pas réellement pour moi.

  1. 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
    
  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)
    

j'utilise python 3.5 et django 1.11.3

3
répondu leila 2017-08-04 01:19:28

Pour le faire dans l'admin du site, voir Auto-remplissage created_by champ avec Django admin du site

2
répondu Ryan Fugger 2017-05-23 10:29:24

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.

1
répondu Florentin 2010-08-04 17:56:28

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
1
répondu jgadelange 2014-12-20 14:46:47

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.

0
répondu durdenk 2015-07-11 23:53:51

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 .

0
répondu simple_human 2018-07-07 04:21:11

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)
-4
répondu Deven Kalra 2014-01-05 23:25:21