J'ai rencontré un problème où j'ai attribué request.user
à une variable appelée prior_user
, puis essentiellement authentifié l'utilisateur, puis vérifié pour voir si request.user != prior_user
. Je m'attendais à ce qu'ils ne soient pas les mêmes et que prior_user
devrait contenir `AnonymousUser. À ma grande surprise, ils étaient les mêmes.
Exemple de code:
prior_user = request.user # request object, obtained froma view
authenticate_user(request) # some function that authenticates
print prior_user.username != request.user.username # returns False i.e.they are the same!
J'ai ensuite découvert que prior_user contient en fait une instance de Django.utils.functional.SimpleLazyObject, donc je suppose que c'est une sorte de type de recherche paresseuse, c'est-à-dire que la valeur de prior_user n'est pas recherchée jusqu'à ce qu'elle soit réellement utilisée. En regardant le code source, je ne peux pas le confirmer.
Toute personne ayant une expérience Django peut me dire ce qui se passe et pourquoi cela est nécessaire?
Cela me laisse un peu secoué, car l'instruction d'affectation habituelle ne fonctionne pas comme je m'y attend et quoi d'autre dans Django agit comme ça? Je n'ai pas non plus vu cela décrit dans le docs .
Donc, toute personne ayant une connaissance super humaine de Django peut fournir une certaine clarté?
L'intergiciel auth
ajoute un attribut user
à request
qui est une instance de SimpleLazyObject
. SimpleLazyObject
, elle-même est une sous-classe de LazyObject
. LazyObject
est, comme décrit par le code réel:
Un wrapper pour une autre classe qui peut être utilisé pour retarder l'instanciation de la classe encapsulée
SimpleLazyObject
définit simplement cette classe (le _wrapped
attribut sur LazyObject
) via une méthode passée, dans ce cas, get_user
. Voici le code de cette méthode:
def get_user(request):
if not hasattr(request, '_cached_user'):
request._cached_user = auth.get_user(request)
return request._cached_user
En soi, ce n'est qu'une enveloppe autour de auth.get_user
, qui permet une sorte de mécanisme de mise en cache. Voici donc ce qui est finalement exécuté:
def get_user(request):
from Django.contrib.auth.models import AnonymousUser
try:
user_id = request.session[SESSION_KEY]
backend_path = request.session[BACKEND_SESSION_KEY]
backend = load_backend(backend_path)
user = backend.get_user(user_id) or AnonymousUser()
except KeyError:
user = AnonymousUser()
return user
Donc, tout ce qui se passe vraiment ici, c'est que request.user
est ambigu jusqu'à ce qu'il soit réellement utilisé pour quelque chose. C'est important, car cela lui permet de s'adapter en fonction de l'état d'authentification actuel. Si vous accédez à une propriété dessus avant vous vous authentifiez, il retourne une instance AnonymousUser
, mais si vous vous authentifiez puis y accédez, il retourne une instance de User
.