web-dev-qa-db-fra.com

Django: Obtenir l'utilisateur actuel dans la sauvegarde du modèle

Je souhaite obtenir l'utilisateur actuellement connecté (request.user) dans la méthode save de models.py. Je souhaite vérifier le rôle de l'utilisateur et voir s'il peut effectuer certaines opérations en fonction de son rôle.

models.py :

class TimeSheet(models.Model):
    check_in_time = models.TimeField()
    check_out_time = models.TimeField()

class Tasks(models.Model):
    time_sheet = models.ForeignKey(TimeSheet)
    project = models.ForeignKey(Project)
    start_time = models.TimeField()
    end_time = models.TimeField()

    def save(self, *args,**kwargs):
        project = SpentTime.objects.get(project__project__id = self.project.id)
        start = datetime.datetime.strptime(str(self.start_time), '%H:%M:%S')
        end = datetime.datetime.strptime(str(self.end_time), '%H:%M:%S')
        time = float("{0:.2f}".format((end - start).seconds/3600.0))

        if common.isDesigner(request.user):
            SpentTime.objects.filter(project__project__id = self.project.id).update(design = float(project.design) + time)

        if common.isDeveloper(request.user):
            SpentTime.objects.filter(project__project__id = self.project.id).update(develop = float(project.develop) + time)

        super(Tasks, self).save(*args, **kwargs)

Ici, le modèle Tasks est utilisé comme modèle en ligne dans le modèle Timesheet. Je souhaite vérifier le rôle de l'utilisateur actuellement connecté et mettre à jour un autre modèle en fonction du rôle de l'utilisateur. Ici, j'ai besoin de request.user pour vérifier le rôle de l'utilisateur actuel. Je n'utilise ni formulaires ni modèles, et utilise complètement l'administrateur Django. Existe-t-il une méthode permettant d’obtenir request.user dans la méthode save ou de vérifier et mettre à jour les valeurs d’un autre modèle dans admin.py?

40
arulmr

Vous pouvez aborder ce problème sous un autre angle. Au lieu de changer la méthode de sauvegarde des modèles, vous devez remplacer la méthode AdminSites save_model. Là, vous aurez l'objet requête et pourrez accéder aux données utilisateur enregistrées comme vous l'avez déjà indiqué.

Consultez ce chapitre de la documentation: Documentation Django ModelAdmin save_model

29
Jens

J'ai trouvé un moyen de le faire, cela implique toutefois de déclarer un MiddleWare. Créez un fichier appelé get_username.py dans votre application, avec le contenu suivant:

from threading import current_thread

_requests = {}

def get_username():
    t = current_thread()
    if t not in _requests:
         return None
    return _requests[t]

class RequestMiddleware(object):
    def process_request(self, request):
        _requests[current_thread()] = request

Éditez votre settings.py et ajoutez-le au MIDDLEWARE_CLASSES:

MIDDLEWARE_CLASSES = (
    ...
    'yourapp.get_username.RequestMiddleware',
)

Maintenant, dans votre méthode save(), vous pouvez obtenir le nom d'utilisateur actuel comme ceci:

from get_username import get_username

...

def save(self, *args, **kwargs):
    req = get_username()
    print "Your username is: %s" % (req.user)
15
nKn

Je ne pense pas que la méthode save_model soit prioritaire. Imaginez, par exemple, que vous souhaitiez enregistrer les informations utilisateur ou valider le modèle en fonction d'informations utilisateur et que save () ne provienne pas d'une vue, ni du site d'administration lui-même.

Ce que les gens demandent, ce sont des constructions comme celles-ci:

def save(..)
    self.user = current_user()

ou

def save(..)
    user = current_user()
    if user.group == 'XPTO':
        error('This user cannot edit this record')

La meilleure approche que j'ai trouvée jusqu'à présent est:

https://bitbucket.org/q/Django-current-user/overview

8
Josir

La solution proposée par @nKn est un bon point de départ, mais lorsque j'ai essayé de la mettre en œuvre aujourd'hui, j'ai rencontré deux problèmes:

  1. Dans la version actuelle de Django, le middleware créé en tant qu'objet brut ne fonctionne pas.
  2. Les unittests échouent (puisqu'ils s'exécutent généralement en un seul thread, votre requête peut donc être collée entre deux tests consécutifs si le premier test a une requête HTTP et le second n'a pas).

Voici le code de mon middleware mis à jour, qui fonctionne avec Django 1.10 et ne casse pas:

from threading import current_thread

from Django.utils.deprecation import MiddlewareMixin


_requests = {}


def current_request():
    return _requests.get(current_thread().ident, None)


class RequestMiddleware(MiddlewareMixin):

    def process_request(self, request):
        _requests[current_thread().ident] = request

    def process_response(self, request, response):
        # when response is ready, request should be flushed
        _requests.pop(current_thread().ident, None)
        return response

    def process_exception(self, request, exception):
        # if an exception has happened, request should be flushed too
         _requests.pop(current_thread().ident, None)
5
Igor Pomaranskiy

La méthode correcte consiste à utiliser threading.local au lieu d'utiliser un dictionnaire avec threading.current_thread, car cela entraînerait une fuite de mémoire, car les anciennes valeurs y resteront aussi longtemps que l'application fonctionnera:

import threading

request_local = threading.local()

def get_request():
    return getattr(request_local, 'request', None)

class RequestMiddleware():
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        request_local.request = request
        return self.get_response(request)

Si vous souhaitez accéder à l'utilisateur à la place de la demande, vous pouvez utiliser la fonction get_request().user ou enregistrer l'utilisateur à la place de la demande sur __call__.

0
Francisco Couzo