I want to add a X-Frame-Options header to Django CreateView. I need this because I'll be serving a form that will be loaded in iframe tags.
The problem is, there are several methods in django class-based views that return HttpResponse objects.
Is there a way to add the header to the responses without overwriting all those methods?
class MyView(CreateView):
def get(self, request, *args, **kwargs):
resp = super(MyView, self).get(request, *args, **kwargs)
resp['X-Frame-Options'] = ''
return resp
# Same would go for form_invalid, post, put, etc...
Okay, I fixed it. If you've encountered similar problem, here's how to do it.
You have to overwrite render_to_response method in same way I did with get in the example code above.
I tried the overwrite render to response method, but I wanted a solution that I could use for a whole chunk of urls mapped to several views, and not have to deal with overwriting the same method on several views.
I made a middleware class based off of django-cors-headers so I could allow iframe-ing of part of my django app. I keep a middleware.py in my main project directory and save a couple random middleware classes I have made there, like this one here and a ForceResponse Exception for example.
import re
from django import http
from django.conf import settings
class XFrameAllowMiddleware(object):
def process_request(self, request):
"""
If CORS preflight header, then create an
empty body response (200 OK) and return it
Django won't bother calling any other request
view/exception middleware along with the requested view;
it will call any response middlewares
"""
if (self.is_enabled(request) and
request.method == 'OPTIONS' and
"HTTP_ACCESS_CONTROL_REQUEST_METHOD" in request.META):
response = http.HttpResponse()
return response
return None
def process_response(self, request, response):
if self.is_enabled(request):
response['X-Frame-Options'] = 'ALLOWALL'
return response
def is_enabled(self, request):
return re.match(settings.XFRAME_URLS_REGEX, request.path)
Add it to your MIDDLEWARE_CLASSES and configure the regex in your settings:
MIDDLEWARE_CLASSES = (
...
'your_django_app.middleware.XFrameAllowMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
)
XFRAME_URLS_REGEX = r'^/iframe_this_url/.*$'
from the django-cors-headers read.me:
CORS_URLS_REGEX: specify a URL regex for which to enable the sending of CORS headers; Useful when you only want to enable CORS for specific URLs, e. g. for a REST API under /api/.
Example:
CORS_URLS_REGEX = r'^/api/.*$'
Default:
CORS_URLS_REGEX = '^.*$'
Related
Django is centered towards allow access to all the majority of the time, and restricting acces on the exception.
I am making a site whereby I would like access restricted the most of the time, and the rare case is open to all. Ensuring that one has added a #login_required decorate to all views is error prone.
I acheived this by creating a custom middleware like this. To keep track of open URLs, I defined a list, and then whenever I wished to open up a URL, I added it to the list, and the middleware checked the request path against this list, and allowed exceptions accordingly.
This method above works, but I often mess it up with changing urls, and other issues to do with code reuse on different sites.
Ideally I would like to create my own #login_not_requied decorator. How to get the class based view or function the request is going to call within the middleware, so I can check whether the view does not require login?
First create the decorator:
from functools import wraps
def login_not_required(obj):
"""Adds the attrbiute login_not_required = True to the object (func/class).
Use it as follows:
#login_not_required
class FooView(generic.View):
...
#login_not_required
def bar_view(request):
...
"""
#wraps(obj)
def decorator():
obj.login_not_required = True
return obj
return decorator()
Then create your own custom middleware:
from contextlib import suppress
from django.conf import settings
from django.shortcuts import redirect
NONE_AUTH_ACCOUNT_PATHS = [
settings.STATIC_URL,
'/accounts/login/',
'/accounts/password_reset/',
'/accounts/reset/',
'/favicon.ico',
'/terminal/login/',
'/terminal/login_failed/',
]
class RequireLoginCheck:
"""Middleware to require authentication on all views by default, except when allowed.
URLS can be opened by adding them to NONE_AUTH_ACCOUNT_PATHS, or by adding
the #login_not_required decorator.
Must appear below the sessions middleware because the sessions middleware
adds the user to the request, which is used by this middleware.
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def _is_none_auth_path(self, path):
for none_auth_path in NONE_AUTH_ACCOUNT_PATHS:
if path.startswith(none_auth_path):
return True
return False
def _is_login_not_required(self, view_func):
with suppress(AttributeError):
# If a class with the #login_not_required decorator, will return True
return view_func.view_class.login_not_required
with suppress(AttributeError):
# If a function with the #login_not_required decorator, will return True
return view_func.login_not_required
return False
def process_view(self, request, view_func, view_args, view_kwargs):
"""https://docs.djangoproject.com/en/stable/topics/http/middleware/#other-middleware-hooks"""
if not (
request.user.is_authenticated
or self._is_login_not_required(view_func)
or self._is_none_auth_path(request.path)
):
if settings.LOGIN_URL != request.path:
# if next URL after login is the same login URL, then cyclic loop
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
else:
return redirect('%s?next=%s' % (settings.LOGIN_URL, '/'))
return None
Ensure you add your middleware to MIDDLEWARE bellow the sessions middleware, in settings.py!
If you have ways it can be improved, please comment below and we make it better for all.
I have some Internet Explorer problems with my Angular 5 app, because in the
IE 11 my get-requests to a django-REST backend are cached by the browser.
I found a question suggesting to add special cache-control-headers to the response, but I didn't find a working answer how to do it with Django REST. All other browsers I have tested seem to work without problems.
Perhaps you could add the Cache-Control header to all responses using a middleware class.
class CacheControlMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response['Cache-Control'] = 'no-cache'
return response
and then wire that into your settings.py
MIDDLEWARE = [
...
'your.app.CacheControlMiddleware',
...
]
Bear in mind that will apply to all views (all your ModelViewSets), which you may or may not want.
If you want to apply it only to specific view sets, you may be better to override the retrieve() and list() methods in your view sets where you can set the Cache-Control header on the Response. For more info on that see http://www.django-rest-framework.org/api-guide/generic-views/#mixins
I had the same problem.
Adding the 'Cache-Control' header to the response worked for me.
response = Response()
response['Cache-Control'] = 'no-cache'
An example views.py:
from rest_framework import status
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
def snippet_list(request):
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
response = Response(serializer.data)
response['Cache-Control'] = 'no-cache'
return response
The other answers have the right idea, but here is a slightly more refined solution.
def disable_cache_middleware(get_response):
def middleware(request):
response = get_response(request)
response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
return response
return middleware
The main difference here is that no-store and must-revalidate are included in the header as well. The Mozilla documentation on the header suggests this combination to prevent browser caching. You can read more about the extra header values there.
Secondly, I just find that the function-based middleware results in a bit less boilerplate than the class-based solution in this case.
I'm Django starter. So far I learned pass variable from view to template. But now I need pass variable to my main layout. I can pass it in each page's def in view. But its too much duplication. So I started learn about middleware.
I created middlewares.py and included it in settings. In middlewares.py file, how to pass variable to my main layout?
Below is my current middlewares.py content, I tried many ways and commented them out, because not working.
from django.db import connections
from django.shortcuts import render, redirect
class NotificationsMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
request.context_data['notification_count'] = 2
response = view_func(request, *view_args, **view_kwargs)
return response
# def process_request(self, request):
# request.notification_count = 2
# return
# def process_template_response(self, request, response):
# # response.context['notification_count'] = 2
# response.context_data['notification_count'] = 2
# # return response
# return render(request, 'main/locations.html')
You may create a template context processor, first create a python file with a method that returns a dictionary with the info you need then add the route to this file in your project settings, using your example:
create a file context_processors.py on your app:
from myapp.models import MyModel
def my_context(request):
context_data = dict()
context_data['notification_count'] = MyModel.objects.all().count()
return context_data
The add the context processor in your settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
# ...
'myapp.context_proccessors.my_context',
)
The you can use the 'variable' in any template:
<p>Count: {{ notification_count }}</p>
You can define a context processor function and add it to TEMPLATE_CONTEXT_PROCESSORS list defined in settings.py.
Let's assume you have a file named project/context_processors.py, you can create a function that basically passes your value to all templates.
def notification_count(request):
return {'notification_count': 2}
settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
...
'context_proccessors.notification_count',
)
Calling {{notification_count}} in your template will return 2.
You can take a look at the official documentation for more info about context processors.
Second approach would be, as you said, using middleware. However, I think that would be an overkill since you can do much more complicated calculations via middlewares.
middlewares.py
class NotificationsMiddleware(object):
def process_template_response(self, request, response):
if not ('notification_count' in response.context_data):
response.context_data['notification_count'] = 2
return response
Please note that, you should add the middleware into MIDDLEWARE_CLASSES list defined in your settings file.
MIDDLEWARE_CLASSES += ('middlewares.NotificationsMiddleware',)
Please see the middleware documentation for more information on how to alter request-response cycle in Django framework.
Hope this helps.
Middleware allows you to bind data to the request, meaning if you use Middleware and not a Context Processor, you can access this universal variable data in both templates and views, or actually anywhere where a request object is used throughout your codebase.
In my mind, this makes Middleware superior to Context Processors if you actually need a universal variable that is available everywhere. In short, and according to the docs: https://docs.djangoproject.com/en/2.2/topics/http/middleware/ Middleware is called on every view call.
Here is a simple example using your code and Middleware
class NotificationsMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# I am showing this as a standard var not a dict as you originally had it,
# for simplicity, this is now a request attribute called notification_count
request.notification_count = 2
response = self.get_response(request)
return response
def process_view(self, request, view_func, view_args, view_kwargs):
# You don't need this method for the middleware to work anymore
# with Django as the __call__ method covers it, so you can delete it.
pass
Now pass the Custom Middleware to settings like so:
MIDDLEWARE = [
...
# custom middleware
'[YOUR-PATH].NotificationsMiddleware'
]
Now you can call the variable in any template like so:
{{ request.notification_count }}
Or in any view, like so:
def index(request):
print(' Notification Count =', request.notification_count)
...
return render(request, 'index.html', { })
I'm trying to do it this way, but it doesn't work.
class MyView(View):
def options(self, request, *args, **kwargs):
"""
Handles responding to requests for the OPTIONS HTTP verb.
"""
response = http.HttpResponse()
if self.kwargs.has_key('xml'):
response['Content-Type'] = 'text/xml; charset=utf-8'
return response
You don't need to write additional code. Use TemplateResponseMixin and set content_type attribute to whatever you need:
class MyView(TemplateResponseMixin):
content_type='application/xml'
...
I think the key point is render_to_response in django.views.generic.base , whose code is this:
def render_to_response(self, context, **response_kwargs):
"""
Returns a response, using the `response_class` for this
view, with a template rendered with the given context.
If any keyword arguments are provided, they will be
passed to the constructor of the response class.
"""
response_kwargs.setdefault('content_type', self.content_type) # key
return self.response_class(
request=self.request,
template=self.get_template_names(),
context=context,
**response_kwargs
)
As for your case, May be you need this code:
class MyView(ListView):
def get(self, request, *args, **kwargs):
context = self.get_context_data()
if self.kwargs.has_key('xml'):
return self.render_to_response(context, content_type="text/xml; charset=utf-8")
return self.render_to_response(context)
I made a middleware class based off of django-cors-headers so I could allow iframe-ing of part of my django app. I keep a middleware.py in my main project directory and save a couple random middleware classes I have made there, like this one here and a ForceResponse Exception for example.
import re
from django import http
from django.conf import settings
class XFrameAllowMiddleware(object):
def process_request(self, request):
"""
If CORS preflight header, then create an
empty body response (200 OK) and return it
Django won't bother calling any other request
view/exception middleware along with the requested view;
it will call any response middlewares
"""
if (self.is_enabled(request) and
request.method == 'OPTIONS' and
"HTTP_ACCESS_CONTROL_REQUEST_METHOD" in request.META):
response = http.HttpResponse()
return response
return None
def process_response(self, request, response):
if self.is_enabled(request):
response['X-Frame-Options'] = 'ALLOWALL'
return response
def is_enabled(self, request):
return re.match(settings.XFRAME_URLS_REGEX, request.path)
Add it to your MIDDLEWARE_CLASSES and configure the regex in your settings:
MIDDLEWARE_CLASSES = (
...
'your_django_app.middleware.XFrameAllowMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
)
XFRAME_URLS_REGEX = r'^/iframe_this_url/.*$'
from the django-cors-headers read.me:
CORS_URLS_REGEX: specify a URL regex for which to enable the sending of CORS headers; Useful when you only want to enable CORS for specific URLs, e. g. for a REST API under /api/.
Example:
CORS_URLS_REGEX = r'^/api/.*$'
Default:
CORS_URLS_REGEX = '^.*$'
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.