I would like to have a common variable available to all my templates. I've been working on this all morning and the only way I can find to do it is to have middleware like this:
class GetNameMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_template_response(self, request, response):
response.context_data['name'] = 'dave'
return response
and have a view like this:
from django.template.response import TemplateResponse
from django.shortcuts import render
def home(request):
return TemplateResponse(request, 'home.html', {})
and then I can access it in the template
Hello {{ name }}
It just seems a bit ugly that I can't use render any more (I know I can do from django.template.response import TemplateResponse as render but that's not really a solution)
(Django 1.10, python 3.5.2)
Django has a standard way for implementing this, which is context processors. This article provides an example implementation
https://www.webforefront.com/django/setupdjangocontextprocessors.html.
Actually a context processor is just a function that accepts a request object and returns a dict with the data you want.
Related
i have a django project with 50 url (in 1 application).
i would set a custom middleware for 20 urls.
when add middleware to MIDDLEWARE_CLASSES , this set for all urls .
How should this be done?
(i use last version of python and djnago)
You can utilize decorators, view's decorators behave similarly to middlewares, simple ex:
from functools import wraps
def custom_view_decorator(view_function):
#wraps(view_function)
def wrap(request, *args, **kwargs):
# Any preprocessing conditions..etc.
return view_function(request, *args, **kwargs)
return wrap
Usage in views.py:
#custom_view_decorator
def my_view(request, *args, **kwargs):
# view code, return response
you can either add a check condition to your middleware which is not recommended(since you cannot determine whether this middleware is working or not without reading it's code). or create a decorator for those views, decorator job is exactly this.
for starters you could do something like this:
class BlockUrlsMiddleware(MiddlewareMixin):
REDIRECT_URL = reverse("not_allowed")
WHITELISTED_URLS = [
"/your/custom/views/urls",
"/your/custom/apis/urls",
]
def process_request(self, request):
if request.path in self.WHITELISTED_URLS:
return None
else:
return HttpResponseRedirect(self.REDIRECT_URL)
few points to Note:
return None means everything is good and serve this request.
return HttpResponseRedirect means URLs are accessed which are not allowed and block this request by redirecting it to your default URLs (that you need to set) in this case REDIRECT_URL.
In views.py I use render() .
In app -> middleware.py I have this code:
from django.conf import settings
class NoTrackingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_template_response(self, request, response):
no_tracking = True if request.GET.get("NO_TRACKING", default=False) is not False else False
pub_id = "PUBLISHER_TEST" if no_tracking is True else settings.WFF_PUB_ID
response.context_data["no_tracking"] = no_tracking
response.context_data["pub_id"] = pub_id
return response
In settings.py I have:
MIDDLEWARE = [
...
'app.middleware.NoTrackingMiddleware',
]
Yet if I place a breakpoint at process_template_response it gets ignored and pub_id is always empty.
Why?
From the documentation (emphasis mine):
process_template_response() is called just after the view has finished executing, if the response instance has a render() method, indicating that it is a TemplateResponse or equivalent.
You state that you're using django.http.shortcuts.render, whose documentation reads:
Combines a given template with a given context dictionary and returns an HttpResponse object with that rendered text.
Django does not provide a shortcut function which returns a TemplateResponse because the constructor of TemplateResponse offers the same level of convenience as render().
Thus render returns a HttpResponse, not a TemplateResponse, and as noted above, process_template_response is only called for TemplateResponses.
You either need to change your view to return TemplateResponse, instead of using the render shortcut, or perform your logic elsewhere. I think your logic could be implemented in a context processor instead of middleware.
I'm building a function to send email and I need to use a context_processor variable inside the HTML template of the email, but this don't work.
Example:
def send_email(plain_body_template_name, html_body_template_name):
plain_body = loader.render_to_string(plain_body_template_name, context)
html_body = loader.render_to_string(html_body_template_name, context)
email_msg = EmailMultiAlternatives(body=plain_body)
email_msg.attach_alternative(html_body, 'text/html')
email_message.send()
In my custom context_processor.py I just have a function that receive a HttpRequest and return a dict like {'foo': 'bar'}, and in the template I try to render using {{foo}}.
I added the context_processor in the TEMPLATE['OPTIONS']['context_processors'] too.
Assuming you're using the django backend in your TEMPLATE with
'BACKEND': 'django.template.backends.django.DjangoTemplates',
django is seeing that you haven't passed in a request and opting for a basic Context to wrap your dict instead of a RequestContext which will handle the context_processors you've defined.
You can probably get away with doing
html_body = loader.render_to_string(html_body_template_name, context, request=request)
but you'd need to pass in the request object.
This might not make sense though. Are you emailing the person making the request? Does the context make sense to include?
If your context processor doesn't need the request then I'd either make it a simple utility function (if it's only called here) or make the request parameter optional, import it into this module, and add it directly into the context
context = {"my_var": 1}
context.update(your_extra_context())
loader.render_to_string(...)
There are some complicated ways of updating a Context() in layers, but I don't think that's necessary here.
I had a similar problem - I needed to render template to string with context processor values, but at the same time request object was None (running command from console). Then I found this approach:
from django.template.loader import render_to_string
from django.template import RequestContext
from django.shortcuts import render
def index(request):
if not request:
context = {'param1':'value1'}
return render_to_string('myapp/index.html', RequestContext(None, context))
else:
#render as usual
render(request, 'myapp/index.html', context)
pass
when you pass RequestContext instead of dictionary, it populates values of all context processors into the context. But request must be optional in all your context processors, otherwise this won't work.
I have a django website with many urls and views. Now I have asked to redirect all non-authenticated users to a certain landing page. So, all views must check if user.is_authenticated() and return to a new set of landing pages.
Can it be done in a pretty way, instead of messing with my views.py/urls.py that much?
There is a simpler way to do this, just add the "login_url" parameter to #login_required and if the user is not login he will be redirected to the login page. You can find it here
from django.contrib.auth.decorators import login_required
#login_required(login_url='/accounts/login/')
def my_view(request):
...
You can use Middleware.
Something like this will check user auth every request:
class AuthRequiredMiddleware(object):
def process_request(self, request):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('landing_page')) # or http response
return None
Docs: process_request
Also, don't forget to enable it in settings.py
MIDDLEWARE_CLASSES = (
...
'path.to.your.AuthRequiredMiddleware',
)
see the docs for login required decorator
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...
another option is to add it to your urls.py patterns, see this answer
urlpatterns = patterns('',
(r'^foo/$', login_required(direct_to_template), {'template': 'foo_index.html'}),
)
As of Django 1.10, the custom middleware classes must implement the new style syntax. You can use the following class to verify that the user is logged in while trying to access any views.
from django.shortcuts import HttpResponseRedirect
class AuthRequiredMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
if not request.user.is_authenticated: # in Django > 3 this is a boolean
return HttpResponseRedirect('login')
# Code to be executed for each request/response after
# the view is called.
return response
You can avoid specifying login_url by setting LOGIN_URL.
Therefore, in settings.py add:
LOGIN_URL = '<some_url>'
And in your views.py annotate relevant functions with only #login_required:
#login_required
def some_view_function(request):
If you need to redirect within a view function, you can do so with:
return redirect_to_login(request.get_full_path())
This can be done with middleware.
I've found a really nifty djangosnippet that does exactly what you are asking for. You can find it here, and it looks like:
from django.http import HttpResponseRedirect
from django.conf import settings
from re import compile
EXEMPT_URLS = [compile(settings.LOGIN_URL.lstrip('/'))]
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
EXEMPT_URLS += [compile(expr) for expr in settings.LOGIN_EXEMPT_URLS]
class LoginRequiredMiddleware:
"""
Middleware that requires a user to be authenticated to view any page other
than LOGIN_URL. Exemptions to this requirement can optionally be specified
in settings via a list of regular expressions in LOGIN_EXEMPT_URLS (which
you can copy from your urls.py).
Requires authentication middleware and template context processors to be
loaded. You'll get an error if they aren't.
"""
def process_request(self, request):
assert hasattr(request, 'user'), "The Login Required middleware\
requires authentication middleware to be installed. Edit your\
MIDDLEWARE_CLASSES setting to insert\
'django.contrib.auth.middlware.AuthenticationMiddleware'. If that doesn't\
work, ensure your TEMPLATE_CONTEXT_PROCESSORS setting includes\
'django.core.context_processors.auth'."
if not request.user.is_authenticated():
path = request.path_info.lstrip('/')
if not any(m.match(path) for m in EXEMPT_URLS):
return HttpResponseRedirect(settings.LOGIN_URL)
All you have to do is to save the file as middleware.py and include the class in you're settings.py, i.e.
MIDDLEWARE_CLASSES += ('projectname.common.middleware.RequireLoginMiddleware',)
You can also define a LOGIN_URL in settings.py, so that you'll be redirected to your custom login page. The default LOGIN_URL is '/accounts/login/'.
Maybe too late but in django 1.9+ it's too easy.
Django introduced Login Required mixin for generic classes and this a great example
here by William S. Vincent
simply in your view add LoginRequiredMixin as parent class
from django.contrib.auth.mixins import LoginRequiredMixin
class BlogUpdateView(LoginRequiredMixin, UpdateView):
model = Post
template_name = 'post_edit.html'
fields = ['title', 'body']
Also you can use login_required decorator for method request
from django.contrib.auth.decorators import login_required
#login_required(login_url='/login/')
def home(request):
return render(request, "home.html")
It's showing like this: http://127.0.0.1:1235/login/?next=/home/
I'm using class based views and I couldn't get any of these solutions to work for what I needed so I'll post my working solution here.
class ManagementPageView(TemplateView):
# Make a dispatch method to handle authentication
def dispatch(self, *args, **kwargs):
# Check if user is authenticated
if not self.request.user.is_authenticated:
# Redirect them to the home page if not
return redirect('home')
# Render the template if they are
return render(self.request, 'management.html')
I have a good many class based views that use reverse(name, args) to find urls and pass this to templates. However, the problem is class based views must be instantiated before urlpatterns can be defined. This means the class is instantiated while urlpatterns is empty leading to reverse throwing errors. I've been working around this by passing lambda: reverse(name, args) to my templates but surely there is a better solution.
As a simple example the following fails with exception:
ImproperlyConfigured at xxxx
The included urlconf mysite.urls doesn't have any patterns in it
mysite.urls
from mysite.views import MyClassView
urlpatterns = patterns('',
url(r'^$' MyClassView.as_view(), name='home')
)
views.py
class MyClassView(View):
def get(self, request):
home_url = reverse('home')
return render(request, 'home.html', {'home_url':home_url})
home.html
<p><a href={{ home_url }}>Home</a></p>
I'm currently working around the problem by forcing reverse to run on template rendering by changing views.py to
class MyClassView(View):
def get(self, request):
home_url = lambda: reverse('home')
return render(request, 'home.html', {'home_url':home_url})
and it works, but this is really ugly and surely there is a better way. So is there a way to use reverse in class based views but avoid the cyclic dependency of urlpatterns requiring view requiring reverse requiring urlpatterns...
EDIT:
I'm using this as so:
views.py
def sidebar_activeusers(cls):
sidebar_dict = {'items' = []}
qs = models.random.filter.on.users
for user in qs:
item = {
'value': user.name,
'href': reverse('user_profile', args=[hash_id(user.id)])}
sidebar = loader.get_template('sidebar.html')
cls.base_dict['sidebar_html'] = sidebar.render(Context(sidebar_dict))
return cls
#sidebar_activeusers
class MyView1(View):
base_dict = {}
...
#other_sidebar_that_uses_same_sidebar_template
class MyView2(View):
basically I'd like to use the same sidebar template for a few different content types. Although the models displayed in the sidebar will be arbitrary the format will always be the same.
For your example MyClassView, it's ok to use reverse
class MyClassView(View):
def get(self, request):
home_url = reverse_lazy('home')
return render(request, 'home.html', {'home_url': home_url},
The reverse method is not called when the class is defined -- It is only called when the request is processed and the get method is called, so there shouldn't be an error. I have tested the example above and it works fine.
However, When you use your sidebar_activeusers decorator, the reverse call occurs before the url conf is loaded. In cases like these, you can use reverse_lazy. Here's an example of reverse_lazy in action:
from django.core.urlresolvers import reverse_lazy
class MyOtherClassView(View):
home_url = reverse_lazy('home')
def get(self, request):
return render(request, 'home.html', {'home_url':self.home_url})
This time, home_url is set when the class is defined, so reverse_lazy is required instead of reverse, because the url conf hasn't loaded yet.
Your home url can be accessed directly in your template by using declaring {% url 'home' %}. It's a standard way to access your named urls in your urls.py file.
You do not have to send the home_url variable to your template explicitly in your class-based view function.
In other words, in your home.html file:-
<p>Home</p>
will do.