Previously we have been accessing kwargs in Django templates with {{view.kwargs.kwarh_name}}.
However, it has been to my surprise that this is not working in my django 3.2.4+
Has it been removed, is there any issue with the line above?
Note: Kwargs here I mean something like below:
Given a function as below:
def GetSomething(request, code):
return render(request, "some template here")
Now, the urlpatterns would be something like,
import GetSomething from .views
app_name = "some_app_name_here"
urlpattern = [
path("link/<int:code>/", GetSomething, name="some_cool_name") #http:localhost/link/12345/
]
Now in HTML/Django templates or Jinja2(whichever you use), accessing this code would be via {{ view.kwargs.code }}
But it so happens that this is not working for me in Django 3.2, any reasons??
Hope I was very clear!
The get_context_data method of class based views adds the view to the context as view. If you have a function based view, it will not be added to the context automatically. This is not related to Django versions 3.2.4+
So either use a class based view or use a function based view and pass the kwargs to the template through the context variable.
def GetSomething(request, code):
context = {
#some values here
'code':code,
}
return render(request, "some template here", context)
Then you can access your context variables in the template directly.
{{code}}
Related
This is a view for get all the records in the EducationalRecord model:
def all_education_resume(request):
RESUME_INFO['view'] = 'education'
educations_resume = EducationalRecord.objects.all().order_by('-created_date')
template = 'resumes/all_resume.html'
context = {'educations_resume': educations_resume, 'resume_info': RESUME_INFO}
return render(request, template, context)
Now, if I want to write exactly this view for other models (like job resumes, research resumes , etc.),
I must another view one separately.
My question is:
How can I get a view for all these requests, so first check the URL of
the request and then do the relevant query? How can I control URL
requests in my views?
My other question is exactly the same as my first question,with this difference:
control view that must render in specific template.In other words,in
second question the ratio between the template and the view is instead
of the ratio of the view to the url or how to create a template for
multiple views (for example, for a variety of database resume
resumes, I have a template) and then, depending on which view render,
the template output is different.
I have implemented these two issues as follows:
I wrote a view for each of request!
In each view, I set the value of RESUME_INFO['view'], and then I've checked it in a template page and specified the corresponding template.
What is the best solution to these two questions?
How can I get a view for all these requests, so first check the URL of the request and then do the relevant query? How can I control URL requests in my views?
You can access request.path, or you can let the url(..)s pass a parameter with kwargs that holds a reference to the model for example, but this is usually bad design. Typically if you use different models, you will likely have to order these different as well, filter these differently, render these differently, etc. If not, then this typically indicates that something is wrong with the modeling.
You can however make use of class-based views [Django-doc], to remove as much boilerplate as posssible. Your view looks like a ListView [Django-doc], by using such view, and patching where necessary, we can omit most of the "boilerplate" code:
# app/views.py
from django.views.generic.list import ListView
class MyBaseListView(ListView):
resume_info = None
template = 'resumes/all_resume.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['resume_info'] = {'view': self.resume_info}
return context
In the individual listviews, you then only need to specify the resume_info and the model or queryset to render it with the 'all_resume.html' template, for example:
# app/views.py
# ...
class EducationalResumeView(MyBaseListView):
queryset = EducationalRecord.objects.order_by('-created_date')
resume_info = 'education'
class OtherModelView(MyBaseListView):
model = OtherModel
resume_info = 'other_info'
So we can here use inheritance to define common things only once, and use it in multiple views. In case we need to change something in a specific view, we can override it at that level.
In the urls.py, you define such view with the .as_view() method [Django-doc]. For example:
# app/urls.py
from django.urls import path
from app.views import EducationalResumeView, OtherModelView
urlpatterns = [
path('education/', EducationalResumeView.as_view()),
path('other/', OtherModelView.as_view()),
]
According to django docs:
class MyModelAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(MyModelAdmin, self).get_urls()
my_urls = [
url(r'^my_view/$', self.my_view),
]
return my_urls + urls
def my_view(self, request):
# ...
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
# Anything else you want in the context...
key=value,
)
return TemplateResponse(request, "sometemplate.html", context)
If I am not wrong, we can do the same thing by adding url in urls.py and the views in views.py as it is normally done then, what is the use of introducing this way? I am a newbie to django and I may be missing something here.
Can you please provide an example where we cannot do it in views.py and we must use the above method?
Any guidance/help would be appreciated.
I think I figured out, both of them can be used to do the same thing but the key difference is that the views which you write using above method will belong to admin app while the general views in views.py belongs to the particular app in you have written.
Hence, the url in ModelAdmin need to be called using name admin:url_name since the url goes as admin/my_views/ in given example.
I would like to know how to pass a variable to all my templates, without repeating the same code on every method in my views.py file?
In the example below I would like to make categories (an array of category objects) available to all templates in the web app.
Eg: I would like to avoid writing 'categories':categories on every method. Is it possible?
One view method
def front_page(request):
categories = Category.objects.all()
if is_logged_in(request) is False:
return render_to_response('users/signup.html', {'is_logged_in': is_logged_in(request), 'categories':categories}, context_instance=RequestContext(request))
else:
return render_to_response('users/front_page.html', {'is_logged_in': is_logged_in(request), 'categories':categories},context_instance=RequestContext(request))
Another view method
def another_view_method(request):
categories = Category.objects.all()
return render_to_response('eg/front_page.html', {'is_logged_in': is_logged_in(request), 'categories':categories},context_instance=RequestContext(request))
What you want is a context processor, and it's very easy to create one. Assuming you have an app named custom_app, follow the next steps:
Add custom_app to INSTALLED_APPS in settings.py (you've done it already, right?);
Create a context_processors.py into custom_app folder;
Add the following code to that new file:
def categories_processor(request):
categories = Category.objects.all()
return {'categories': categories}
Add context_processors.py to TEMPLATE_CONTEXT_PROCESSORS in settings.py
TEMPLATE_CONTEXT_PROCESSORS += ("custom_app.context_processors.categories_processor", )
And now you can use {{categories}} in all the templates :D
As of Django 1.8
To add a TEMPLATE_CONTEXT_PROCESSORS, in the settings you must add the next code:
TEMPLATES[0]['OPTIONS']['context_processors'].append("custom_app.context_processors.categories_processor")
Or include that string directly in the OPTIONS.context_processors key in your TEMPLATES setting.
I'm trying to create a custom tag. Inside this custom tag, I want to be able to have some logic that checks if the user is logged in, and then have the tag rendered accordingly. This is what I have:
def user_actions(context):
request = template.Variable('request').resolve(context)
return { 'auth': request['user'].is_athenticated() }
register.inclusion_tag('layout_elements/user_actions.html', takes_context=True)(user_actions)
When I run this, I get this error:
Caught VariableDoesNotExist while rendering: Failed lookup for key [request] in u'[{}]'
The view that renders this ends like this:
return render_to_response('start/home.html', {}, context_instance=RequestContext(request))
Why doesn't the tag get a RequestContext object instead of the Context object? How can I get the tag to receive the RequestContext instead of the Context?
EDIT:
Whether or not it's possible to get a RequestContext inside a custom tag, I'd still be interested to know the "correct" or best way to determine a user's authentication state from within the custom tag. If that's not possible, then perhaps that kind of logic belongs elsewhere? Where?
By default, an instance of django.template.Context is used in rendering templates, but you can override this in your view, either by passing a subclass of django.template.Context as the context_instance keyword argument to render_to_response, or by instantiating the subclass, and passing it directly to Template.render.
For convenience, Django provides a useful, pre-defined Context subclass: django.template.RequestContext. RequestContext looks in your settings file for a setting called TEMPLATE_CONTEXT_PROCESSORS, which should be a tuple of callable objects, each of which should return a dictionary; RequestContext will loop over each callable, call it, and add the key/value pairs from its returned dictionary to the template context as variables. Check out these links for a more detailed explanation.
To add the current user to your template context, you need django.core.context_processors.auth:
Add the django.core.context_processors.auth context
processor to your list of context
processors in settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.auth',
... # Other context processors follow
)
Ensure the view than renders the template which calls
your custom tag uses RequestContext:
from django.template.context import RequestContext
from django.shortcuts import render_to_response
def my_view(request):
# Do some stuff
return render_to_response('templates/view.html',
context_instance=RequestContext(request))
Using RequestContext calls all context processors defined
in settings.TEMPLATE_CONTEXT_PROCESSORS, including the
auth context processor, which adds a context variable 'user'
to your template context.
In your template tag, you can access this variable
via context['user']
#register.inclusion_tag('templates/custom/tag.html', takes_context=True)
def custom_tag(context):
user = context['user']
if user.is_authenticated():
# Some stuff for the logged-in users
else:
# Stuff for anonymous users
i dont see how your view is linked to the template tag, because from what i know its django's template system that renders the tag, so the context is a dictionary, try this, hopefully it helps
user = context['user']
if user.is_authenticated():
do stuff
The "context" of a template node is a different thing to the RequestContext. A Context is also a dictionary, so if it has a user at all it would be accessed via context['user'].
The template nodes context contains information to help it render itself within the template, after doing some reading of the docs, I cannot find any way to access the RequestContext which would be associated with the request via the django.template.Context.
Moreover django.template.RequestContext extends from django.template.Context specifically in order to handle the added request object which would contain the user. So it is no surprise that Context would not have access to it.
See the source code of Context for proof.
How to get the user
After a bit of searching I found the django.template.resolve_variable method which can be used as follows:
from django.template import resolve_variable
user = resolve_variable('user', context)
Where context is a template context.
I ended up just passing the "user" parameter to the tag and using that to decide if the user was auth'd or not.
For debugging purposes, I would like to have a variable in all my templates holding the path of the template being rendered. For example, if a view renders templates/account/logout.html I would like {{ template_name }} to contain the string templates/account/logout.html.
I don't want to go and change any views (specially because I'm reusing a lot of apps), so the way to go seems to be a context processor that introspects something. The question is what to introspect.
Or maybe this is built in and I don't know about it?
The easy way:
Download and use the django debug toolbar. You'll get an approximation of what you're after and a bunch more.
The less easy way:
Replace Template.render with django.test.utils.instrumented_test_render, listen for the django.test.signals.template_rendered signal, and add the name of the template to the context. Note that TEMPLATE_DEBUG must be true in your settings file or there will be no origin from which to get the name.
if settings.DEBUG and settings.TEMPLATE_DEBUG
from django.test.utils import instrumented_test_render
from django.test.signals import template_rendered
def add_template_name_to_context(self, sender, **kwargs)
template = kwargs['template']
if template.origin and template.origin.name
kwargs['context']['template_name'] = template.origin.name
Template.render = instrumented_test_render
template_rendered.connect(add_template_name_to_context)
From Django 1.5 release notes:
New view variable in class-based views context
In all generic class-based views (or any class-based view inheriting from ContextMixin), the context dictionary contains a view variable that points to the View instance.
Therefore, if you're using class-based views, you can use
{{ view.template_name }}
This works if template_name is explicitly set as an attribute on the view.
Otherwise, you can use
{{ view.get_template_names }}
to get a list of templates, e.g. ['catalog/book_detail.html'].
Templates are just strings not file names. Probably your best option is to monkey patch render_to_response and/or direct_to_template and copy the filename arg into the context.