Custom ContextProcessor with ListView - not showing in template - python

I am back to django after a few years and I have adopted Class Based Views - definitely a fan, BUT for some reason, I cannot get my custom content processor to show its data. I am using mostly Generic Views on my application, and from what I understand they should automatically serve the context to the view.
This is my context processor (context_processors.py)
from models import Alert
def context_alerts(request):
alert_list = {}
alerts = Alert.objects.filter(to_user=request.user)
alert_list['alerts'] = alerts
alert_list['unread_count'] = len([a for a in alerts if a.unread == True])
# printing to console - this works
print alert_list
return alert_list
Note that when I print the dictionary - it shows in my console, so I know it is firing.
It is setup in my settings as so
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'part_iq.context_processors.context_alerts'
],
},
},
]
Finally, this is an example view - I have about 20 or so, all standard class based views:
class BuyerRfqList(ListView):
context_object_name = "rfqs"
template_name = "dashboard/buyer/rfq-list.html"
def get_queryset(self):
user = self.request.user
rfqs = Rfq.objects.filter(from_user=user, hidden=False).order_by("created_at")
return rfqs
I am simply outputting the alerts in the template as so (trying both the name of the dict and the context processor function):
{{alert_list.unread_count}}
{{context_alerts.unread_count}}
with no luck.
I feel like it is something obvious, but I am too rusty w/ Django and new to ClassBasedViews to figure it out

Two problems:
There is no key called "count" in your "alert_list" dictionary. The template is actually translated to alert_list['count'] (because of #2) which doesn't exist so it just fails silently.
Dictionaries in Django templates are accessed as if they're objects.
so alert_list['alerts']
is equivalent to
{{ alert_list.alerts }}
Now you can just call count on the Queryset
{{ alert_list.alerts.count }}

You don't have anything called alerts_list in the template. That's just the local name you're using inside the function to hold the dict of variables to pass to the template. The contents of that dict are added directly to the context: so you can access {{ alerts }} and {{ unread_count }}.

Related

Is it possible to access the module or view class from inside a request

I have a Django project with a context processor to have the same object on every page.
It is working so far.
# /core/context_processor.py
# (is correctly linked in settings.py)
def default(request):
context = {'email': Email()}
return context
Now i want to implement a "TAG" constant in every Django module.
I want to have it accessible in every view context without passing it from every View class
It should be similar to accessing the "{{request.path}}" inside the template but explicite.
# /home/views.py
TAG = "TagOfHome"
class HomeView(TemplateView):
template_name = 'home/list.html'
# /addresses/views.py
TAG = "TagOfAddresses"
class AddressView(TemplateView):
template_name = 'address/list.html'
Now i want to access the TAG constant from the inside of every Template.
If it is a view of the addresses module it should be "TagOfAddresses", at home views it should be "TagOfHome" etc.
How can i achieve this?
Can i access the TAG somehow from within the context processor (request)?
Edit:
Thanks to #grrrrrr i could solved it using the 'request.resolver_match'.
But i didn't want to have 'config-stuff' inside of a module.
Therefore i created a dict in the settings.py:
TAGS = {
'core': 'home',
'addressbook': 'address',
'files': 'files'
}
# /core/context_processor.py
from config.settings import TAGS
def default(request):
context = {'email': Email()}
...
module = request.resolver_match.func.view_class.__module__.split('.')[0]
context['tag'] = TAGS.get(module, 'default')
return context
You can user ResolverMatch to get the app name as well as other metadata about the request's resolved url.
For example in your template you could use
{{ request.resolver_match.app_name }} or customize the behavior in your context processor to return the TAG:
def default(request):
context = {'email': Email()}
if request.resolver_match.app_name == 'home':
context['TAG'] = 'TagOfHome'
elif ...
return context

Python/Django universal method passed to template engine

I have a large Django Project that has hundreds of views already. I am creating a tasks feature where users are able to complete specific tasks associated with the specific application in the project they are using. I have multiple interfaces (aka Django apps in the project) : admin, management, onsite, etc... and each interface has its own navigation with a tasks link.
What I want is to be able to change the color of this link if a user is in an interface where a task has yet to be completed.
This is easy to check in each view and then I could universally render the correct color for the link based on a variable passed into the view, but that is extremely tedious with hundreds of views.
I suppose I could add a filter in each interface/Django App to simplify this a bit, but is that the most simple solution?
Here is an example of the method I want to be called in each interface's navigation:
from objects_client.task_models.task_models import Tasks
def does_interface_have_open_tasks(current_interface, db_alias):
if Tasks.objects.using(db_alias)\
.filter(interface=current_interface, completed=0).exists():
return True
return False
I ended up using a Context Processor to solve my needs like I show below:
import traceback
from objects_client.task_models.task_models import Tasks
def universally_used_data(request):
# I use multiple DBs
db_alias = request.session.get('db_alias')
# dictionary for global context values
dictionary_to_return = dict()
# interfaces and URL equivalents
interface_dictionary = {
'adm': 'admin',
'mgt': 'management',
'onsite': 'onsite',
'secu': 'security',
'maint': 'maintenance'
}
try:
# get interface url
short_url = request.path[1:-1].split('/')[1]
# get interface from dictionary above
interface = interface_dictionary.get(short_url)
dictionary_to_return['SHORT_URL'] = short_url
dictionary_to_return['INTERFACE'] = interface
# see if there is an open task...
if Tasks.objects.using(db_alias) \
.filter(interface=interface, completed=0).exists():
dictionary_to_return['OPEN_TASKS'] = True
else:
dictionary_to_return['OPEN_TASKS'] = False
except Exception as ex:
print(ex, traceback.format_exc())
return dictionary_to_return
Here is how I load the Context Processor:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
...
]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
...
# custom processors
'utils.context_processors.context_processors.universally_used_data'
],
},
},
]
Then I can just call the this variable in the template like so to change an HTML element's color, no {% load [whatever] %} or anything:
{% if OPEN_TASKS %}
style="color:red;"
{% endif %}
Thank you #Daniel Roseman for the suggestion/comment. This had me stumped for a bit :)

Context_processor does not define a " " class/attribute(error)

I am following a Django course from Antonio's Melle Book, and I need a context_processor to use a cart instance throught the webapp. I constantly get the error, that the context processor does not define a "cart" object attribute.
Note: I am using cached sessions, if it matters
I tried putting the cart in a try catch statement, I have read the docs, I doesn't sort things out for me
context_processors.py
from .cart import Cart
def cart(request):
return {'cart': Cart(request)}
settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
(...)
'cart.context_processors.cart,']}
cart.py
class Cart(object):
def __init__(self, request):
self.session = request.session
cart = self.session.get(settings.CART_SESSION_ID)
if not cart:
cart = self.session[settings.CART_SESSION_ID] = {}
self.cart = cart
You haven't shown the actual error message. But the problem is probably because you've put the comma inside the quotes instead of outside. Change it to:
'cart.context_processors.cart',]}

Access a variable in HTML from a .py file [duplicate]

base.html is used as the base template for all other pages. base.html has the navigation bar and in the navigation bar, I want to show the number of messages the user received. Thus, I need to have a variable like {{number_of_messages}} in the base.html.
However, how should I pass this variable to it? Every template extends base.html and is rendered by a function. I don't think returning number_of_messages in all functions is a good way. Is there better way to implement this? How can I pass this variable to all templates?
You can use tags.
#myproject/myproject/templatetags/tags.py
from django import template
register = template.Library()
#register.simple_tag
def number_of_messages(request):
return _number
In your Base.html
{% load tags %}
{% number_of_messages request %}
Have a look at:
https://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.RequestContext
As long as:
you use the render shortcut in your view (or otherwise take care to use a RequestContext to render your response)
have django.contrib.auth.context_processors.auth in your TEMPLATE_CONTEXT_PROCESSORS setting (as it is by default)
...then you have the current request's User (or AnonymousUser) instance available in your template as {{ user }} ...I am guessing from there you may be able to access the number of messages directly?
Or perhaps you are using Django's messages framework?
This comes with it's own context processor which (as long as you use render or RequestContext) will make a {{ messages }} var (containing the messages for current user) available in your templates. For 'number of messages' you can do {{ messages|length }}
If none of these built-in options provide what you need you can either:
make your own template context processor which will run for every request and make additional variables available to all templates (when rendered with a RequestContext)
make your own template tag which can be used only where needed... of course if this is used in your base.html and all templates inherit from base.html then it's still going to run for every page.
I find the simplest steps to passing variables to your base templates in django is to add a context_processor.py file like so:
In your app create context_processors.py and declare your variables e.g.:
# context_processors.py
def message_processor(request):
if request.user.is_authenticated:
no_msgs = request.user.profile.msgs
else:
no_msgs = 0
return {
'messages' : no_msgs
}
Then register your process or under TEMPLATES in your settings.py file:
TEMPLATES = [
{
...
'context_processors': [
...
# custom
'appname.context_processors.message_processor',
],
},
},
]
And then you will be able to get that variable anywhere in your app as:
{{ messages }}
If you want the variable in really all the views, then a custom template context processor is probably the best option.
https://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext
If you want the variable only in some of the views, then you can make those views call a common function that populates the common variables, something like this:
def some_view(request):
params = _common_params(request)
params.update({
# params specific to .some_view
})
return render_to_response('path/to/template, params)
or create a custom decorator like this:
from functools import wraps
def render_with_params():
def _inner(view_method):
def _decorator(request, *args, **kwargs):
params = _common_params(request)
(template_path, view_params) = view_method(request, *args, **kwargs)
params.update(view_params)
return render_to_response(template_path, params, context_instance=RequestContext(request))
return wraps(view_method)(_decorator)
return _inner
#render_with_params()
def some_view(request):
params = { ... }
return ('path/to/template', params)

Always including the user in the django template context

I am working on a small intranet site for a small company, where user should be able to post. I have imagined a very simple authentication mechanism where people just enter their email address, and gets sent a unique login url, that sets a cookie that will always identify them for future requests.
In my template setup, I have base.html, and the other pages extend this. I want to show logged in or register button in the base.html, but how can I ensure that the necessary variables are always a part of the context? It seems that each view just sets up the context as they like, and there is no global context population. Is there a way of doing this without including the user in each context creation?
Or will I have to make my own custom shortcuts to setup the context properly?
There is no need to write a context processor for the user object if you already have the "django.core.context_processors.auth" in TEMPLATE_CONTEXT_PROCESSORS and if you're using RequestContext in your views.
if you are using django 1.4 or latest the module has been moved to django.contrib.auth.context_processors.auth
#Ryan: Documentation about preprocessors is a bit small
#Staale: Adding user to the Context every time one is calling the template in view, DRY
Solution is to use a preprocessor
A: In your settings add
TEMPLATE_CONTEXT_PROCESSORS = (
'myapp.processor_file_name.user',
)
B: In myapp/processor_file_name.py insert
def user(request):
if hasattr(request, 'user'):
return {'user':request.user }
return {}
From now on you're able to use user object functionalities in your templates.
{{ user.get_full_name }}
In a more general sense of not having to explicitly set variables in each view, it sounds like you want to look at writing your own context processor.
From the docs:
A context processor has a very simple interface: It's just a Python function that takes one argument, an HttpRequest object, and returns a dictionary that gets added to the template context. Each context processor must return a dictionary.
The hints are in every answer, but once again, from "scratch", for newbies:
authentication data is in templates (almost) by default -- with a small trick:
in views.py:
from django.template import RequestContext
...
def index(request):
return render_to_response('index.html',
{'var': 'value'},
context_instance=RequestContext(request))
in index.html:
...
Hi, {{ user.username }}
var: {{ value }}
...
From here: https://docs.djangoproject.com/en/1.4/topics/auth/#authentication-data-in-templates
This template context variable is not available if a RequestContext is
not being used.
#Dave
To use {{user.username}} in my templates, I will then have to use
requestcontext rather than just a normal map/hash: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-requestcontext
So I guess there are no globals that the template engine checks.
But the RequestContext has some prepopulate classes that I can look into to solve my problems. Thanks.
If you can hook your authentication into the Django authentication scheme you'll be able to use request.user.
I think this should just be a case of calling authenticate() and login() based on the contents of your Cookie.
Edit: #Staale - I always use the locals() trick for my context so all my templates can see request and so request.user. If you're not then I guess it wouldn't be so straightforward.
its possible by default, by doing the following steps, ensure you have added the context 'django.contrib.auth.context_processors.auth' in your settings. By default its added in settings.py, so its looks like this
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.auth',)
And you can access user object like this,
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
For more information, refer here http://docs.djangoproject.com/en/1.2/topics/auth/#authentication-data-in-templates
Use context_processors. https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-TEMPLATES-OPTIONS
settings.py
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'myapp.functions.test'
],
},
myapp/functions.py
def test(request):
return {'misc': 'misc'}

Categories