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', { })
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 am using Django 3.2. I want to compute the value of variable and make that value available to all views in my project.
I have decided to use middleware - but it is not clear (yet), how I can make the value I computed in MyCustomMiddleware available in a view.
Here is my custom middleware class:
class MyCustomMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
mysecret_value = 4269
return response
After making the requisite modifications to the MIDDLEWARE section in settings.py, how do I acesss mysecret_value in my views?
myapp/views.py
def foobar(request):
the_value = mysecret_value # <- how do I access the RHS variable?
Middlewares run before views, so you can actually modify the request object:
class MyCustomMiddleware:
...
def __call__(self, request):
request.mysecret_value = 4269
return self.get_response(request)
Then you can access this value from any view:
def foobar(request):
the_value = request.mysecret_value
I'm writing a middleware with process_view method and I want it to apply only to the views in my app (not django.contrib.auth and other imported ones). How can this be done?
you can use request url like this
def process_request(self, request):
if request.path.startswith('any_prefix'):
# do something
else:
# do other thing
also you can get view function file like this:
process_view(request, view_func, view_args, view_kwargs):
if view_func.__module__ in ['module list']:
# do something
else:
# do other thing
in this solution if your view wrapped checking module may not work.
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've developed my own, but it seems like it's a great enough thing that someone else probably thought of it first and did a better job ;)
The goal is to be able to write, in your myapp/views.py
router = Router(prefix=r'^myapp/')
#router.route(url=r'my/url/here', name="my-great-view")
def myview(request):
return render_to_response("mytemplate.html", {})
And then in urls.py
urlpatterns += myapp.views.router.get_patterns()
And there are several other cool decorator things I've built in (#jsonview for taking a returned dictionary and making a json response, #use_template for taking a returned dictionary and passing it to the defined template as the context...)
It seems like this way makes everything a lot more localized an readable. When looking at a view function you don't have to search around to find what url it belongs to, or what it's "named".
I saw this one djangosnippet, but it's a lot more magic than I'd like, and it doesn't look like it's been generally adopted.
In the event that no one has put together a standard solution, should I clean mine up and submit a pull request to contrib?
here is what I currently have implemented: magic.py
Edit:
if you want multiple urls for the same view:
#router.route(url="my-first-url", kwargs={'foo':'bar'})
#router.route(url="my-second=url", kwargs={'foo':'baz'})
def my_view(...): ...
And of course this doesn't have to be the only way to do it -- the normal urlpatterns way has some nice things about it two, but these two methods are not mutually exclusive.
The regular configuration we have a main website urls.py . And the urls.py contains variable named urlpatterns.
so wo can push some url_pattern into it.
app/views.py
from django.urls import path as djpath
URLS = []
URLS_d = {}
def route(path=''):
def wrapper(func):
path_name = path or func.__name__
URLS.append(
djpath(path_name, func)
)
### below is not important ####
path_func = URLS_d.get(path_name, None)
if path_func:
print(path_func, '<>', func)
raise Exception('THE same path')
URLS_d[path_name] = func
### above is not important ####
return wrapper
#route()
def index(req):
return HttpResponse('hello')
website/urls.py
from app.views import URLS
urlpatterns.extend(URLS)
If you use class base view , you can use django-request-mapping,
A Simple Example
view.py
from django_request_mapping import request_mapping
#request_mapping("/user")
class UserView(View):
#request_mapping("/login/", method="post")
def login(self, request, *args, **kwargs):
return HttpResponse("ok")
#request_mapping("/signup/", method="post")
def register(self, request, *args, **kwargs):
return HttpResponse("ok")
#request_mapping("/<int:user_id>/role/")
def get_role(self, request, user_id):
return HttpResponse("ok")
#request_mapping("/<int:pk/", method='delete')
def delete(self, request, pk):
User.objects.filter(pk=pk).delete()
return HttpResponse("ok")
#request_mapping("/role")
class RoleView(View):
# ...
urls.py
from django_request_mapping import UrlPattern
urlpatterns = UrlPattern()
urlpatterns.register(UserView)
urlpatterns.register(RoleView)
and request urls are:
post: http://localhost:8000/user/login/
post: http://localhost:8000/user/signup/
get: http://localhost:8000/user/1/role/
delete: http://localhost:8000/user/1/
# ...