Django: How can i get the logged user outside of view request? - django

I have a class method (outside of a view) who needs the logged user's information. How can i retrieve the logged in user without passing the request to the method? Unfortunately i can't find nowhere a nice solution and it's not logical just not exist such a way to do it, because Django should store somewhere the logged in user. Otherwise (i believe) it would be impossible to use #login_required decorator from django.contrib.auth.decorators. Right?
So if it's not possible why it's not? Why Django works like this if the only think i want is the logged in user and not all the informations inside request?
Thanks in advance.

About decorators, it is wrong. Actually decorators called with request argument.
I believe better way is that passing user or request object to class's method. But there are other ways to access request.
Here is the code that we use. You need to add this middleware to MIDDLEWARES. And import & calling get_request function.
Update July 2017: Tested with Python 3.6.1 and Django 1.10.7, based in the original code from this answer and in the Writing your own middleware documentation.
First create a new app, ie. startapp request_middleware.
Then add "request_middleware" to your INSTALLED_APPS in settings.py.
After that paste the code bellow in, ie. request_middleware.middleware.py.
Finally add "request_middleware.middleware.RequestMiddleware" to your MIDDLEWARE in settings.py (In my case I've placed it in between 'debug_toolbar.middleware.DebugToolbarMiddleware' and 'django.middleware.security.SecurityMiddleware' as far above the list as I could).
# request_middleware.middleware.py
from threading import current_thread
_REQUESTS = {}
class RequestNotFound(Exception):
def __init__(self, message):
self.message = message
def get_request():
thread = current_thread()
if thread not in _REQUESTS:
raise RequestNotFound('global request error')
else:
return _REQUESTS[thread]
class RequestMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def process_request(self, request):
_REQUESTS[current_thread()] = request
def __call__(self, request):
self.process_request(request)
response = self.get_response(request)
return response
After that simply do from request_middleware.middleware import get_request in order to use get_request in your code.

Related

Get username in a middleware from Django Rest Framework SIMPLE JWT token (3rd party)

I am using Django Rest Framework and I've included a 3rd party package called REST framework simple JWT Auth which is the new framework referenced,
and this one, REST framework JWT Auth, which is the old one (I Imagine), since there was no update on github since a long time and maybe not supported for newer versions.
And I'm looking for a way, like this link on stackoverflow-3rd answer, via middlewares, to get the user information for each request in order to apply/save it, in needed, the user object in my models by using django signals.
I checked in documentation and on internet, but I didn't find anything. So, if you already had that case, I will appreciate your help.
Thank you
To get username from the user model you should use request.user. This will give you the authenticated user's info with the request. However if you use simple_jwt, it's not possible to directly use request.user in a middleware because authentication mechanism works in view function.
So you should manually authenticate inside the middleware and then you can use request.user to get whatever data from the user model.
from rest_framework_simplejwt import authentication
class MyMiddleware():
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_view(self, request, view_func, view_args, view_kwargs):
request.user = authentication.JWTAuthentication().authenticate(request)[0] # Manually authenticate the token
print(request.user) # Use the request.user as you want
With simple_jwt the user information will not be set on the request initially but after the request is processed it will be. So if you're just logging the information and you can do it after the request is processed, do it there.
So for example in a simple middleware:
def simple_middleware(get_response):
def middleware(request):
# request.user is an anonymous user at this point
response = get_response(request)
# request.uesr is the logged-in user here
return response
return middleware

Parsing POST request - object() takes no parameters

I try to access post request and check if user with email that is in post request already exist. When I try to send data to endpoint, I get error
TypeError: object() takes no parameters
my views.py
#csrf_exempt
class CheckIfEmailAvailable():
#csrf_exempt
def check(request):
email = request.POST.get("email")
if User.objects.filter(email=email).exists():
return Response({'status': 'not available'})
my url.py
url(r'^api/checkmail/', CheckIfEmailAvailable, name='check'),
What am I doing wrong ?
In this case better to use function in the url
from your_app.views import check
url(r'^api/checkmail/', check, name='check')
and your view will be like this (only function)
#csrf_exempt
def check(request):
email = request.POST.get("email")
if User.objects.filter(email=email).exists():
return JsonResponse({'status': 'not available'})
Also FYI if you want to use #csrf_exempt with classes you should use dispatch, you can get more info here
Example of JsonResponse
from django.http import JsonResponse
def your_view(request):
return JsonResponse({'foo':'bar'})
You need to inherit your class your generic django views for this to work. Url patterns expect a callable that can accept request (with args and kwargs) and return response. View.as_view class method returns a callable for you.
# In views file
from django.views.generic import View
...
class CheckIfEmailAvailable(View):
def get(self, request, *args, **kwargs):
return self.check(request) # or something else depends on your use case
# In urls file
from django.views.decorators.csrf import csrf_exempt
...
url(r'^api/checkmail/', csrf_exempt(CheckIfEmailAvailable.as_view()), name='check'),
Moreover csrf_exempt decorator won't work (out of box) on classes, neither on bound methods for that matter. Better way should be to use them in urls.
If you had posted the full traceback, you would see that the error does not come from parsing the request; Django does not even get nearly that far.
You can't use a class as a view like that. You should have check as a standalone function, without a class, and refer to it directly in your urls.
Django does support class-based views, but they have a very specific structure, need to inherit from the proper base class, and be referenced in the urls.py in a specific way.

Having multiple decorators in Django and using only one

I am using django-tokenapi to allow for authentication of an Android project that is using Django as a backend. The project also has a web interface.
django-tokenapi uses the #token_required decorator to protect certain views. Django uses the #login_required decorator to protect certain views.
What I would like is to have only one view that is protected by #login_required OR #token_required so it can be used with either the webapp or Android app.
So, ideally, it would look like this:
#token_required
#login_required
def names_update(request):
....
....
However that does not work. Is there a better method of doing this? Or is the correct thing to have two views, one the webapp and one for Android, that are protected by the proper decorator, and then lead to the same method.
No there's no easy way, if possible at all, to write an generalized OR decorator according to you described.
However, you can write a new decorator that does exactly what you want:
from functools import wraps
from django.contrib.auth import authenticate, login
from django.views.decorators.csrf import csrf_exempt
def token_or_login_required(view_func):
"""
Decorator which ensures the user is either logged in or
has provided a correct user and token pair.
"""
#csrf_exempt
#wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
user = request.REQUEST.get('user')
if user and user.is_authenticated:
return view_func(request, *args, **kwargs)
token = request.REQUEST.get('token')
if user and token:
user = authenticate(pk=user, token=token)
if user:
login(request, user)
return view_func(request, *args, **kwargs)
return HttpResponseForbidden()
return _wrapped_view
this decorate combines both token_required and login_required decorators, and will allow access to the view either if the user is logged in, or the token is valid.
You could try assigning a new view variable to the old view:
#token_required
def names_update_token(request):
...
#login_required
names_update_login = names_update_token
This should have the effect of creating a second view named names_update_login which is just a pointer to the first so that the code remains the same.
EDIT:
Another thought, and one I have used, is to create a more "generic" view and call it from each of the other views:
def update_token(request):
...
#token_required
def names_update_token(request):
update_token(request)
#login_required
def names_update_login(request):
update_token(request)
This gets around the issue you mentioned while still maintaining only a single copy of the actual code that implements the view.
It is possible to use more than one decorator on a single view. But in this case I think you should seperate the view and apply the decorators seperately. Otherwise token_required decorator will try to authenticate using token which won't be available on a request made using browser.

Persisting session variables across login

I want to hold information about a users preferences in a session variable. If the user chooses a preference while logged out and then later logs in, I want the preference to be maintained without needing to reselect it.
Django sessions maintain a session key in a cookie to track a users session. The way I understand it, this key is changed when a user logs in.
a) Does this mean all session variables are deleted on login or is there any sort of passover
b) In the case of not being able to save preferences across a login, is manually setting cookies the best way to proceed? I imagine a scenario like:
while logged out, maintain preferences in cookie
on login, copy preferences to session variable and write to db (via signal?)
on logout, update cookies with preferences (via signal?)
Update
I managed to get this functionality by saving preferences in the User's profile object, as well as in a cookie (these preferences are not sensitive in any way). When the user is logged in, their profile setting takes preference. When not logged in, the cookie preference is chosen
Upon login, Django calls session.flush() or session.cycle_key(), which makes sure nothing from the old session is kept. This is a security measure that protects you against session fixation vulnerabilities. So, when applying this solution, be aware what kind of variables you want to persist.
If you want to keep some state, you'll have to restore that after the login was issued.
The solution by Chase Seibert was a great start, it was very insecure due to threadsafety issues in that code. You can find an improved version here, which is safe to use:
from functools import wraps
class persist_session_vars(object):
"""
Some views, such as login and logout, will reset all session state.
(via a call to ``request.session.cycle_key()`` or ``session.flush()``).
That is a security measure to mitigate session fixation vulnerabilities.
By applying this decorator, some values are retained.
Be very aware what kind of variables you want to persist.
"""
def __init__(self, vars):
self.vars = vars
def __call__(self, view_func):
#wraps(view_func)
def inner(request, *args, **kwargs):
# Backup first
session_backup = {}
for var in self.vars:
try:
session_backup[var] = request.session[var]
except KeyError:
pass
# Call the original view
response = view_func(request, *args, **kwargs)
# Restore variables in the new session
for var, value in session_backup.items():
request.session[var] = value
return response
return inner
and now you can write:
from django.contrib.auth import views
#persist_session_vars(['some_field'])
def login(request, *args, **kwargs):
return views.login(request, *args, **kwargs)
And for class based views (django-allauth):
import allauth.account.views as auth_views
from django.utils.decorators import method_decorator
#method_decorator(persist_session_vars(['some_field']), name='dispatch')
class LoginView(auth_views.LoginView):
pass
and use that view in the url patterns:
import allauth.urls
from django.conf.urls import include, url
from . import views
urlpatterns = [
# Views that overlap the default:
url(r'^login/$', views.LoginView.as_view(), name='account_login'),
# default allauth urls
url(r'', include(allauth.urls)),
]
When you login/logout Django will flush all sessions if another user logs in (request.session.flush() in auth/init.py).
You're better of storing user settings in the database and add some middleware to get that data and store it in your request.
user data that persists sounds like it should live in something like a UserProfile model
I actually think your initial design made sense. If you want to save some session variables across the login/logout boundary, you can do something like this.
from functools import wraps
class persist_session_vars(object):
""" Some views, such as login and logout, will reset all session state.
However, we occasionally want to persist some of those session variables.
"""
session_backup = {}
def __init__(self, vars):
self.vars = vars
def __enter__(self):
for var in self.vars:
self.session_backup[var] = self.request.session.get(var)
def __exit__(self, exc_type, exc_value, traceback):
for var in self.vars:
self.request.session[var] = self.session_backup.get(var)
def __call__(self, test_func, *args, **kwargs):
#wraps(test_func)
def inner(*args, **kwargs):
if not args:
raise Exception('Must decorate a view, ie a function taking request as the first parameter')
self.request = args[0]
with self:
return test_func(*args, **kwargs)
return inner
You would throw this decorator on whatever view you're calling auth.login/logout from. If you're delegating to the built-in view for those, you can easily wrap them.
from django.contrib.auth import views
#persist_session_vars(['HTTP_REFERER'])
def login(request, *args, **kwargs):
return views.login(request, *args, **kwargs)
You can create a form Mixin that allows you to persist form values to the user's session (which doesn't require them being logged in). This is useful for things such as filter/sorting options on a public table-view report, where you want to keep their filter options persistent across refreshes.
View:
def list_states(request):
if request.method == 'GET':
form = StateListFilterForm().load_from_session(request.session)
elif request.method == 'POST':
form = StateListFilterForm(request.POST)
form.persist_to_session()
return render('forms/state_list.html', RequestContext(request, {'state_form': form})
Form:
class PersistableMixin:
def persist_to_session(form, session):
for key in form.fields.keys():
val = getattr(form, 'cleaned_data', form.data).get(key, None)
if val: # will not store empty str values
session[key] = val
return True
def load_from_session(form, session):
for key in form.fields.keys():
saved_val = session.get(key, '')
if saved_val: # will not load empty str values
form.fields[key].initial = saved_val
return form
class StateListFilterForm(forms.Form, PersistableMixin):
states = forms.MultipleChoiceField(required=False, choices=US_STATES)

django management commands and user

i use the django custom management/commands quite a bit. i would like to provide access to my views via some of these scripts; however i'm in a quandary about how to do this for authenticated users. i'm not really using the middleware libraries, so all i need is access to the request.META['REMOTE_USER']; is there a recommended why i can fake this? eg,
def poll_view( request ):
user = None
if 'META' in request and 'REMOTE_USER' in request.META:
user = request.META['REMOTE_USER']
if not user == None:
do_something()
and in my management/command script i have:
class Command(BaseCommand):
def handle(self, *args, **kwargs):
req = ???
poll_view( req )
Perhaps you can refactor your view to extract just the functionality you need and put it in a helper module. That way, you can call this function in both the view and the management command.
Example:
-----------in a file named somewhere.py----------
def do_this():
#do these
pass
------------in your view-------------------------
from somewhere import do_this
def my_view(request):
user = request.META.get('REMOTE_USER', None)
if user:
do_this()
-----------in your management command----------
from somewhere import do_this
class my_command(...):
def handle(self, args*, kwargs**):
do_this()
Well the management commands are just functions, so instead of calling the view from the command, call the command from the view and pass the request as a parameter
views.py
from managment import Command
def some_view(request):
Command._private_function(request=request)
commands.py
class Command(...):
def _private_function(request=None):
if request:
// do action with request details (i.e. coming from a view)
else:
// do action without (i.e. coming from command line)
def handle(self,...):
_private_function()
This allows you to perform the main logic of your admin-command from either the command line, or from a view (i.e. the web). You need to be careful though and be sure that you actually want the person with access to the view (authenticated user) to perform whatever action it is