Django: How to extend SessionBase to have access to the Request Object? - python

I have a custom session class that I've built to extend the Django SessionBase. I did this in order to reuse a legacy Session table, so that sessions can pass between our Django pages and our PHP pages without having the user to log in and back out.
Everything's working perfectly so, far with one huge BUT.
I wrote some custom middleware in order to let the SessionStore.start() function have access to the Request Object. Unfortunately, in order to do that I used this answer: Access request.session from backend.get_user in order to remedy my problem.
I have learned that using the above answer (Essentially binding the request object to the settings, so you can access using import settings* and then settings.request) is totally horrible and the absolutely worst way to do this.
My core problem, is I don't understand how I can access the request from within the custom session backend I've written.

Maybe in middleware you could pass request to your custom SessionStore like this:
request.session = engine.SessionStore(session_key,request)
and in SessionStore:
class SessionStore(SessionBase):
def __init__(self, session_key=None, request):
self.request = request
super(SessionStore, self).__init__(session_key)
Later you can access request as self.request.

Django's SessionMiddleware does this:
class SessionMiddleware(object):
def process_request(self, request):
engine = import_module(settings.SESSION_ENGINE)
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
request.session = engine.SessionStore(session_key)
can't you do this?
import mycustomsessionbackend as myengine
class MyCustomSessionMiddleware(object):
def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
request.session = myengine.SessionStore(session_key, request)
...
# mycustomsessionbackend.py
class SessionStore(SessionBase):
def __init__(self, session_key=None, request=None):
super(SessionStore, self).__init__(session_key)
self.request = request

Related

Add metadata to WSGIRequest object

I have Django1.9 middleware class:
class MyMiddleware(object):
def process_request(self, request):
token = self._get_or_create_token(request)
#request.context['token'] = token
The issue is:
- I would like to put token to some sort of context to pass it through the application flow.
- I avoid putting it into request session, because it result in extra database reading/writing.
Could you suggest me some solution?
You can add any attribute to the HttpRequest, so you can implement this with:
class MyMiddleware(object):
def process_request(self, request):
token = self._get_or_create_token(request)
request.token = token
or if you really want some sort of context dictionary:
class MyMiddleware(object):
def process_request(self, request):
token = self._get_or_create_token(request)
if not hasattr(request, 'context'):
request.context = {}
request.context['token'] = token
I have Django1.9 middleware class.
As is documented, django-1.9 is not supported anymore since April 2017, therefore I strongly advice to update.

Is it possible for Django to use the same link and a database for each client?

Is it possible for Django to use the same link and a database for each client?
Hello, I'm new to Django and I have a question.
I'm bringing a PHP system to Django where all clients use the same link to access the system, but each with its database with users, access groups, etc.
In the custom login screen they inform their client id, user and password to authenticate, where always the client id is the name of the database.
I made the following code to change the database connection that is probably wrong:
def change_db (self):
connections ['default']. settings_dict ['NAME'] = self.db
return True
The code even exchanges the database I want to access, but testing on my local machine using the runserver, if another client is accessing simultaneously for example in another browser, it displaces the other client.
Has anyone had it or does it have an idea to solve? I searched the routers, I do not know if I would solve my problem, or if there was the same problem of switching the connection and removing the other client.
Grateful.
I think I solved my problem, I found a link that was very useful to me:
dynamically set database based on request in django
It uses a middleware to get the connection and router as mentioned by the staff that responded, which in the case it takes the subdomain, I made some changes in it to get the prefix that already solves my case, with the help of this link:
Django URL with dynamic prefix
Follow the code:
from django.utils.deprecation import MiddlewareMixin
from django.contrib.sessions.middleware import SessionMiddleware
import re
import threading
request_cfg = threading.local()
#Middleware
class RouterMiddleware(MiddlewareMixin):
def process_request(self, request):
words = request.get_full_path()
db_name = words.split('/')[1]
request_cfg.cfg = db_name
return None
def process_view(self, request, view_func, view_args, view_kwargs):
cliente = view_kwargs.get('cliente')
if cliente:
request.cliente = cliente
view_kwargs.pop('cliente')
return view_func(request, *view_args, **view_kwargs)
def process_response(self, request, response):
if hasattr(request_cfg, 'cfg'):
del request_cfg.cfg
return response
#Router
class DatabaseRouter (object):
def _default_db(self):
if hasattr(request_cfg, 'cfg'):
return request_cfg.cfg
else:
return 'default'
def db_for_read(self, model, **hints):
return self._default_db()
def db_for_write(self, model, **hints):
return self._default_db()
Thank you to anyone who tried to help me :)
According to docs, you should not edit settings.py at runtime, but
did you tried this?

How to test that a view is only accessed by staff users in Django

I am learning testing in Django, and have a view which I want to test. This view should only be accessed by staff users. Suppose the view is:
def staff_users(request):
....
# some logic
return HttpResponseRedirect('/repositories/')
if the request is coming from staff users, it should redirect to repositories otherwise I should get something like permission denied. I am starting with something like in tests.py.
def test_request_object(self):
self.user = User.objects.create_user(
username='abc', email='abc#gmail.com', password='1234')
request = HttpRequest()
# User send a request to access repositories
response = staff_users(request)
self.assertIsNone(response)
The problem is here I am not associating my request object with any users, and I also got to know about from django.contrib.admin.views.decorators import staff_member_required but not sure how to use them here. Could anyone tell me how should I test my view should only be accessed by staff users?
All you need to do is decorate your view which you want to protect as shown below:
#staff_member_required
def staff_users(request):
....
# some logic
return HttpResponseRedirect('/repositories/')
If you want a custom logic for testing instead of using django decorator then you can write your own decorator as well.
def staff_users_only(function):
def wrap(request, *args, **kwargs):
profile = request.session['user_profile']
if profile is True: #then its a staff member
return function(request, *args, **kwargs)
else:
return HttpResponseRedirect('/')
wrap.__doc__=function.__doc__
wrap.__name__=function.__name__
return wrap
and use it as:
#staff_users_only
def staff_users(request):
....
# some logic
return HttpResponseRedirect('/repositories/')
Edit
Association of sessions on request object for testing can be done as:
def test_request_object(self):
self.user = User.objects.create_user(
username='abc', email='abc#gmail.com', password='1234')
request = HttpRequest()
#create a session which will hold the user profile that will be used in by our custom decorator
request.session = {} #Session middleware is not available in UnitTest hence create a blank dictionary for testing purpose
request.session['user_profile'] = self.user.is_staff #assuming its django user.
# User send a request to access repositories
response = staff_users(request)
#Check the response type for appropriate action
self.assertIsNone(response)
Edit 2
Also it would be a far better idea to use django Client library for testing:
>>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'abc', 'password': '1234'})
>>> response.status_code
200
>>> response = c.get('/user/protected-page/')
>>> response.content
b'<!DOCTYPE html...

Using middleware to add arguments to view functions in django

I have a pretty small, basic site with a handful of views. Pretty much all of the views perform some interaction with an external API.
In each view I instantiate an object of my API class, which obviously is not very DRY. I also have a piece of middleware that adds one or two useful variables to the request object (my middleware runs last in the chain).
I was thinking of instantiating my api class in the middleware, and then passing it to each view as an argument, i.e.
def my_view(request, my_api_class_instance):
and then calling the view from the middleware, something like:
def process_view(self, request, view_func, view_args, view_kwargs):
my_api = api(some, args, here)
response = view_func(request, my_api, *view_args, **view_kwargs)
return response
It seems like a quick and easy way to tidy some code and reduce repetition. Is there anything inherently BAD about this?
If you look at the Django middleware documentation, you'll see;
If process_view returns an HttpResponse object, Django won’t bother calling any other view or exception middleware, or the appropriate view; it’ll apply response middleware to that HttpResponse, and return the result.
So returning an HttpResponse will skip the execution of other middleware classes below this one which in general should be avoided unless your middleware is the last one in settings.MIDDLEWARE_CLASSES list.
However, you still can bind your API object to HttpRequest instance passed on to middleware. It is the same approach to what AuhenticationMiddleware does in order to populate request.user.
def APIMiddleware(object):
def process_request(self, request):
request.api = API(host, port, user, password, extra={"url": request.get_full_path()})
you can just change view_kwargs in middleware
class SomeMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
view_kwargs['my_api'] = api(some, args, here)
return None
def my_view(request, my_api):
# you can use you api there
def my_view(request, args, my_api)
# you can use you api there
document it's there
middleware returns None, Django will continue processing this request, executing any other process_view() middleware.
but, this only applies to every view function can got the keyword argument 'myapi', otherwise will raise the TypeError.
so the best way isn't pass your api by func arguments, like #ozgur pass your api by request.
You can use a middleware but there are two other possibilities too, both more flexible. The first one is to use a decorator and wrap the view:
#my_api_init_decorator
def my_view(request, my_api):
...
This allows you to explicitly select views, check user authorization or permissions before you init your api...
The second solution is to use class based views and create your own view you inherit from.

Request variable not found in pluggable view

I have a pluggable view defined as follows
class ListView(View):
methods = ["GET", "POST"]
def __init__(self, model, template_name="list_view.html"):
self.model = model
self.template_name = template_name
def dispatch_request(self, *args, **kwargs):
if request.method == "GET":
objects = self.model.query.all()
return render_template(self.template_name,
objects=objects)
else:
#do post request
I'm trying to create a pluggable view that'll handle both get and post requests. When I try the above, I get the following error however
NameError: global name 'request' is not defined
According to the Flask docs, the request should be present in the dispatch_request method, but it isn't in my case. I'm using Flask 0.10.1
request is always a global context variable; you need to import it in your module:
from flask import request
See the Quickstart documentation on accessing request data; this is no different for pluggable views.

Categories

Resources