Django - Global variable, Should I use Template context processor? - django

my question is:
Should I use Template context processor for global variable like category list ?
I have globs.py
from news.models import Category
def globs(request):
cats = Category.objects.all()
return {'cats': cats}
and in settings.py
TEMPLATE_CONTEXT_PROCESSORS = ("django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"globs.globs",)
And when I use in template 'cats' it works fine on developer server.
On my hosting I have a problem:
Error importing request processor module globs: "No module named globs"
Can I use something else for global variables?

globs.py needs to be in your importable path on your hosting server. You could move it to your news directory and use "news.globs.globs" in TEMPLATE_CONTEXT_PROCESSORS.

Related

How sites framework works?

I am trying to develop a ModelManager with an operation similar to the Sites Framework. Depending on a user's field, the ModelManager returns a queryset. I tried to imitate the operation of the Sites Framework but I do not understand how the SITE_ID is obtained dynamically with this function:
def get_queryset(self):
return super(CurrentSiteManager, self).get_queryset().filter(
**{self._get_field_name() + '__id': settings.SITE_ID})
It seems to be static :/.
I capture the user's field through a Middleware and assign it to request.field. How can I retrieve that field in the ModelManager and perform the query?
I think you are missing the dynamic way to get the current site instance.
From the documentation example:
from django.contrib.sites.shortcuts import get_current_site
def article_detail(request, article_id):
try:
a = Article.objects.get(id=article_id,
sites__id=get_current_site(request).id)
except Article.DoesNotExist:
raise Http404("Article does not exist on this site")
# ...
You should use the get_current_site method to get the current site.
It should be noted that it's not working if you actually define the current site in the settings like SITE_ID=1.
It looks up the current site based on request.get_host() if the SITE_ID setting is not defined.
You should read this part of the documentation actually explaining how django can get the current site in a dynamic fashion:
shortcuts.get_current_site(request)
A function that checks if django.contrib.sites is installed and returns either the current Site object or a RequestSite object based on the request. It looks up the current site based on request.get_host() if the SITE_ID setting is not defined.
Both a domain and a port may be returned by request.get_host() when the Host header has a port explicitly specified, e.g. example.com:80. In such cases, if the lookup fails because the host does not match a record in the database, the port is stripped and the lookup is retried with the domain part only. This does not apply to RequestSite which will always use the unmodified host.
And here is the code of the get_current actually called by get_current_site:
def get_current(self, request=None):
"""
Return the current Site based on the SITE_ID in the project's settings.
If SITE_ID isn't defined, return the site with domain matching
request.get_host(). The ``Site`` object is cached the first time it's
retrieved from the database.
"""
from django.conf import settings
if getattr(settings, 'SITE_ID', ''):
site_id = settings.SITE_ID
return self._get_site_by_id(site_id)
elif request:
return self._get_site_by_request(request)
raise ImproperlyConfigured(
"You're using the Django \"sites framework\" without having "
"set the SITE_ID setting. Create a site in your database and "
"set the SITE_ID setting or pass a request to "
"Site.objects.get_current() to fix this error."
)

Wildcard Subdomains with Django + Gunicorn + Nginx [duplicate]

Tl; dr: Is there a way to override the default behaviour of reverse?
In my django project I have alot of urls such as
url(r'^\w+/company/', include("company.urls", namespace="company")),
Which allows for urls such as
.../companyA/company/
.../companyB/company/
So that I can then use a custom middleware to modify the request to include some specific details based upon what company is using my site
This all works fine except for when django is trying to decipher the full path with reverse and {% url .. %}...
It seems to be returning /x/company/ as a default match for the regex. since the django.utils.regex_helper method next_char has an escape mapping for \w to map to x
The url tag I have been able to override to replace the /x/ with the correct company name and I am wondering if there is a similar thing I can do to override reverse in the same way, or anything else that I can do to resolve this problem?
Previously, I was using
url(r'^(?P<company_name>\w+)/company/', include("company.urls", namespace="company"))
But this meant I had to include a parameter in every view
def view(request, company_name):
...
As well as include it in all my other calls to the view (i.e with the {% url %}) which I am trying to avoid.
For ease of use, Django packages as compiled a page full of every possible existing django package that can accomplish this. However below is my own simple implementation
I modified my nginx proxy config to use the following
server_name ~(?<short_url>\w+)\.domainurl\.com$;
... stuff related to static files here
location / {
proxy_set_header X-CustomUrl $short_url;
.... other proxy settings
}
What this does is create a variable inside a request header that can then be used within Django. This variable I then used within a custom middleware to extend a request with a reference to the model which allows its use anywhere.
class CompanyMiddleware(object):
def process_request(self, request):
if settings.DEBUG:
request.company = CompanyClass.objects.get(id=1)
return None
short_url = request.META.get("HTTP_X_CUSTOMURL")
try:
company = CompanyClass.objects.get(short_url=short_url)
except Model.DoesNotExist:
return HttpResponseBadRequest('Company not found')
request.company = company
return None
Examples:
www.companya.domainurl.com # short_url is companya
test.domainurl.com # short_url is test
To use this within a template, context processors must be added to the settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
'django.core.context_processors.request' # This one in particular
)

Django admin without template context processor

Is it possible to make django-admin interface not to include some of the template context processors?
in settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
..some.django.context.processors,
'myapp.views.context_processor',
)
in my.views.py:
def context_processor(request):
return {'user': my_custom_string}
So, data returning in 'my' context_processor overrides 'user' variable in django-admin template context and i've got an error.
And the second problem is that 'my' context_processor returnes 'fat' dict. There is no need to have it in admin's interface
You have the request object available to your context processor; you can examine the request's path to see if it's under the root path of the admin.

How to get the name of current app within a template?

What's the simplest way to access the name of the current app within template code?
Alternatively, what's the simplest way to define a template variable to hold the name of the current app?
(The goal here is to minimize the number of places I need to edit if I rename an app.)
Since Django 1.5 there is a "resolver_match" attribute on the request object.
https://docs.djangoproject.com/en/dev/ref/request-response/
This contains the matched url configuration including "app_name", "namespace", etc.
https://docs.djangoproject.com/en/2.2/ref/urlresolvers/#django.urls.ResolverMatch
The only caveat is that it is not populated until after the first middleware passthrough, so is not available in process_request functions of middleware. However it is available in middleware process_view, views, and context processors. Also, seems like resolver_match is not populated in error handlers.
Example context processor to make available in all templates:
def resolver_context_processor(request):
return {
'app_name': request.resolver_match.app_name,
'namespace': request.resolver_match.namespace,
'url_name': request.resolver_match.url_name
}
There's a way to obtain an app name for a current request.
First, in your project's urls.py, considering your app is called 'main':
#urls.py
url(r'^', include('main.urls', app_name="main")),
Then, a context processsor:
#main/contexts.py
from django.core.urlresolvers import resolve
def appname(request):
return {'appname': resolve(request.path).app_name}
Don't forget to enable it in your settings:
#settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
"django.core.context_processors.request",
"main.contexts.appname",)
You can use it in your template like any other variable: {{ appname }}.

Understaning Django's template automatic search/file system

From the tutorial online https://docs.djangoproject.com/en/dev/intro/tutorial02/#customizing-your-project-s-templates it states to place user templates into a directory like so:
/project/app/templates/app/example.html
But when the automatic template loader searches it looks in:
/project/app/templates/
Likewise when you try to load a default template that is located at:
/Python27/Lib/site-packages/django/contrib/admin/templates/admin/base.html
It will search in:
/Python27/Lib/site-packages/django/contrib/admin/templates/
Am I missing something here? I have not defined any TEMPLATE_DIRS because I want the default system to operate like the tutorial says.
UPDATE/SOLUTION:
The reason I was confused is that you put templates that are native to the app in
/project/app/templates
and then put non-native templates in
/project/app/templates/non-native_app-name
And also when using {% extends "template.html" %} you must place app name before template like so {% extends "app-name/template.html" %}
Correct me if I am wrong here... Thanks for the help
If you have django.template.loaders.app_directories.Loader in your TEMPLATE_LOADERS settings, this actually should be in your settings by default:
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
Then, django will search through templates directory in each of your apps, quote from docs:
Loads templates from Django apps on the filesystem. For each app in
INSTALLED_APPS, the loader looks for a templates subdirectory. If the
directory exists, Django looks for templates in there.
This means you can store templates with your individual apps. This
also makes it easy to distribute Django apps with default templates.
if u read django's source, you can find code in django.template.loaders.py .
def find_template(name, dirs=None):
# Calculate template_source_loaders the first time the function is executed
# because putting this logic in the module-level namespace may cause
# circular import errors. See Django ticket #1292.
global template_source_loaders
if template_source_loaders is None:
loaders = []
for loader_name in settings.TEMPLATE_LOADERS:
loader = find_template_loader(loader_name)
if loader is not None:
loaders.append(loader)
template_source_loaders = tuple(loaders)
for loader in template_source_loaders:
try:
source, display_name = loader(name, dirs)
return (source, make_origin(display_name, loader, name, dirs))
except TemplateDoesNotExist:
pass
raise TemplateDoesNotExist(name)