How to change the template path dynamically in Django 1.8 - django

I want to add the mobile version to my site, so I need to change the template path dynamically to get the mobile template dir or the desktop dir.
As the TEMPLATE_DIRS is deprecated since version 1.8, I tried to change the DIRS option of a DjangoTemplates backend, but it didn't work.

As I had the same challenge I post my code here. I am using the get_flavor from django_mobile to detect what to show and I wanted to use the standard admin directories (as installed) for django_admin_bootstrapped and the regular django admin. In addition I wanted to only interfere the loader with admin pages and not with regular pages - in the latter case the loader does nothing.
So this works for me:
import os.path
from django.template.loaders.base import Loader
from django.template.loader import LoaderOrigin
from django.template import TemplateDoesNotExist
from django_mobile import get_flavour
class Myloader(Loader):
is_usable = True #this line is necessary to make it work
def load_template_source(self, template_name, template_dirs=None):
path_split = template_name.split("/")
if not u'admin' in path_split:
raise TemplateDoesNotExist
template_short_name = path_split[-1]
if get_flavour() == "mobile":
basepath = r'/path/to/django_admin_bootstrapped/templates/admin'
path = os.path.join(basepath,template_short_name)
else:
basepath = r'/path/to/django/contrib/admin/templates/admin'
path = os.path.join(basepath,template_short_name)
try:
with open(path,"r") as f1:
template_string = f1.read()
except IOError:
raise TemplateDoesNotExist
template_origin = LoaderOrigin(template_short_name, self.load_template_source, template_name, template_dirs)
return (template_string, template_origin)
If you want to distinguish the template path by something different e.g. by the name in the url you need to replace the "if get_flavour()=="mobile" " by looking for something inntemplate_name.

Related

ValueError: server must be a Flask app or a boolean

I'm trying to work through the simplest dashboard example in the django-plotly-dash documentation, but I'm consistently getting the ValueError above.
For the code below, assume the django project name is django_project and the django app name is dashboard.
My ROOT_URLCONF at django_project/urls.py has the following relevant code:
import dashboard.dash_app
from dashboard.views import test_view
urlpatterns = [
...
path('dashboard/', test_view, name='test_view'),
path('django_plotly_dash/', include('django_plotly_dash.urls')),
]
My dashboard app view, located at dashboard/views.py is as follows:
from django.shortcuts import render
def test_view(request):
return render(request, 'dashboard/main.html')
The main.html template is as follows:
from django.shortcuts import render
def test_view(request):
return render(request, 'dashboard/main.html')
{% load plotly_dash %}
{% plotly_app name="SimpleExample" %}
Finally, the DjangoDash app instance is created in a file called dashboard/dash_app.py. As shown earlier, this module is imported in django_project/urls.py, as above. Code is as follows:
import dash
import dash_core_components as dcc
import dash_html_components as html
from django_plotly_dash import DjangoDash
app = DjangoDash('SimpleExample')
app.layout = ...
#app.callback(...)
def callback_color(...):
...
During the debugging process, the only other seemingly relevant information that I have is that the base_pathname is '/django_plotly_dash/app/SimpleExample/'
Any other ideas?
This is caused by the recent update to Dash version 1.0 on 2019-06-20. The Dash class now checks to ensure that server is either boolean or an instance of Flask. Since django plotly_dash uses it's own PseudoFlask object, it fails this check and it is incompatible with Dash 1.0 and will need to be updated for use with the current Dash version.
I experienced the same problem today. Using an older version of Dash (0.43.0) solved it for me.

Separate domain for separate apps on django

I'd like to separate some apps on my website, and move some on differents domains.
But I'd like to avoid rewriting the urls in all the templates.
Is there an easy way to do this ?
The idea was to rewrite the reverse function (and thus, all the url dispatching thing).
Then, you have the first website called 'foo.com' and the second named 'bar.com'
This works for more sites than just two
app/urls.py
#!/usr/bin/python
# coding: utf-8
from django.conf.urls import include, url
from foo import urls as foo_urls
from bar import urls as bar_urls
url_patterns = [
url(r'^foo/',
include(foo_urls),
namespace='foo'),
url(r'^bar/',
include(bar_urls),
namespace='bar'),
]
app/__init__.py
#!/usr/bin/python
# coding: utf-8
"""
Just to simplify the multi website thing. Everything is served from one instance
"""
from __future__ import absolute_import
from django.core import urlresolvers
from django.http import Http404
from .settings LANGUAGES
old_reverse = urlresolvers.reverse
def remove_prefix(s, prefix):
return s[len(prefix):] if s.startswith(prefix) else s
def new_reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None):
""" Return an url with the domain if the page is situated on another domain
Note that this works for urls templatetags and for
django.core.urlresolvers.reverse, that are used a lot in get_absolute_url()
methods.
"""
old_reverse_url = old_reverse(viewname, urlconf, args, kwargs, current_app)
splitted_url = old_reverse_url.split('/')
if splitted_url[1] in [L[0] for L in LANGUAGES]:
(trash, lang, app, *path) = splitted_url
else:
(trash, app, *path) = splitted_url
lang = ''
if app == 'admin' or current_app == 'admin':
# fix a case where sometime the reverse has or has not a trailing /
old_reverse_url = remove_prefix(old_reverse_url, '/')
# fix a case where sometime the reverse has a double admin at the
# begining
old_reverse_url = old_reverse_url.replace('adminadmin', 'admin')
return '/%s' % old_reverse_url
path = '/'.join(path)
if lang == '':
return '//%s.com/%s' % (app, path)
else:
return '//%s.com/%s/%s' % (app, lang, path)
urlresolvers.reverse = new_reverse
templates examples
<a href="{% url 'foo:index' %}*>foo index</a>

When using i18n_patterns, how to reverse url without language code

I am using i18n_patterns but I want to use reverse to create a link to the page without language in the url (such that the user will be redirected based on cookies and headers and such).
I have tried
from django.utils.translation import activate, deactivate, get_language
current_lang = get_language()
deactivate()
url = reverse(things)
activate(current_lang)
That works for getting other language versions using activate(target_lang), but if I deactivate I just get urls for the default language (/en/account/ but I want /account/).
I already thought getting alternate language versions is overly complicated, but this I cannot manage at all. Any hints? (Without manually stripping LANGUAGE_CODE from the url)
UPDATE: I also tried
from django.core.urlresolvers import get_resolver
get_resolver(None).reverse(*args, **kwargs)
but get NoReverseMatch
I think the easiest way is to let Django resolve the URL with the language prefix and then just remove the language prefix.
You can write the following function:
import re
from django.core.urlresolvers import reverse
def reverse_no_i18n(viewname, *args, **kwargs):
result = reverse(viewname, *args, **kwargs)
m = re.match(r'(/[^/]*)(/.*$)', result)
return m.groups()[1]
Now, anywhere in your code you can do something like this:
from myproject.utils import reverse_no_i18n
def my_view(request):
return HttpResponseRedirect(reverse_no_i18n('my_view_name'))
You might also want to create a custom {% url %} templatetag which calls your custom function.
I also spent time to find a nice solution and here is mine.
Next to main urls file ('my_project/urls.py'), create the file 'my_project/urls_without_lang.py' with the content below.
Then, you can use reverse('viewname', urlconf='my_project.urls_without_lang')
Django=<1.11
from copy import copy
from django.urls.resolvers import LocaleRegexURLResolver
from .urls import urlpatterns as urlpatterns_i18n
"""
Purpose of this file is to be able to reverse URL patterns without language prefix.
This is usefull to build URL meant to be communicated "outside" of the domain without any language duty.
To use it with 'reverse' method (from django.shortcuts module), simply give the additional parameter:
`urlconf='my_project.urls_without_lang'`
Example: `reverse('viewname', urlconf='my_project.urls_without_lang')`
"""
urlpatterns = copy(urlpatterns_i18n)
for el in urlpatterns_i18n:
if isinstance(el, LocaleRegexURLResolver):
urlpatterns.remove(el)
urlpatterns += el.url_patterns
Django>1.11
from copy import copy
from django.urls import URLResolver
from .urls import urlpatterns as urlpatterns_i18n
urlpatterns = copy(urlpatterns_i18n)
for el in urlpatterns_i18n:
if isinstance(el, URLResolver) and isinstance(el.urlconf_name, list):
urlpatterns.remove(el)
urlpatterns += el.url_patterns
Hope that will help some of you.

Version number in Django applications

I'm working on a Django application and I want to display the version of the application (such that people, who find bugs know the version of the application and can provide better bug reports).
Is there a universally accepted way to store version number in Django (I mean the version of my application, not Django) ?
I was looking for this exact same question, and found your question. The answer you accepted is not quite satisfactory to me.
I am working with django debugtoolbar, in there you can also show all versions of the apps used. I was wondering how to get the versions of my custom applications to show there as well.
Looking a bit further I found this question and answer:
How to check the version of a installed application in Django in running time?
This answer however does not tell me where to put this __version__
So I looked in to an open application, which does show up in django toolbar.
I looked in to the django restframework code, there I found out:
the version is put in the __init__.py file
(see https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/init.py)
and it is put here as:
__version__ = '2.2.7'
VERSION = __version__ # synonym
And after this, in his setup.py, he gets this version from this __init__.py :
see: https://github.com/tomchristie/django-rest-framework/blob/master/setup.py
like this:
import re
def get_version(package):
"""
Return package version as listed in `__version__` in `init.py`.
"""
init_py = open(os.path.join(package, '__init__.py')).read()
return re.match("__version__ = ['\"]([^'\"]+)['\"]", init_py).group(1)
version = get_version('rest_framework')
When using buildout and zestreleaser:
By the way, I am using buildout and zest.releaser for building and versioning.
In this case, above is a bit different (but basically the same idea):
see http://zestreleaser.readthedocs.org/en/latest/versions.html#using-the-version-number-in-setup-py-and-as-version
The version in setup.py is automatically numbered by setup.py, so in __init__.py you do:
import pkg_resources
__version__ = pkg_resources.get_distribution("fill in yourpackage name").version
VERSION = __version__ # synonym
There are many places where you can store your app version number and a few methods that allow you to show it in django templates. A lot depends on the release tool you're using and your own preferences.
Below is the approach I'm using in my current project.
Put the version number into version.txt
I'm storing the app version number in the version.txt file. It's one of the locations the zest.releaser release tool (that I'm using) takes into account while doing a release.
The whole content of version.txt is just the app version number, for example: 1.0.1.dev0
Read the number to a variable in settings.py
...
with open(version_file_path) as v_file:
APP_VERSION_NUMBER = v_file.read()
...
Create a custom context processor
This paragraph and the following ownes are based on the wonderful answer by bcchun to Can I access constants in settings.py from templates in Django?
A custom context processor will allow you to add the app version number to the context of every rendered template. You won't have to add it manually every time you render a template (and usually you'll want to have the version number somewhere in the footer of every page).
Create context_processors.py file in your app directory:
from django.conf import settings
def selected_settings(request):
# return the version value as a dictionary
# you may add other values here as well
return {'APP_VERSION_NUMBER': settings.APP_VERSION_NUMBER}
Add the context processor to settings.py
TEMPLATES = [{
...
'OPTIONS': {
'context_processors': [
...
'your_app.context_processors.selected_settings'
],
},
}]
Use RequestContext or render in views
RequestContext and render populate the context with variables supplied by context_processors you set in settings.py.
Example:
def some_view(request):
return render(request, 'content.html')
Use it in a template
...
<div>{% trans 'App version' %}:{{APP_VERSION_NUMBER}}</div>
....
For me the best result/approach is to use the __init__.py on the project folder, such as
.
├── project_name
│   ├── __init__.py
and later check using the standar way, as said in (PEP396)
>>> import project_name
>>> project_name.__version__
'1.0.0'
I solved this by adding a templatetag to my django project:
in proj/templatetags, added version.py:
from django import template
import time
import os
register = template.Library()
#register.simple_tag
def version_date():
return time.strftime('%m/%d/%Y', time.gmtime(os.path.getmtime('../.git')))
Then, in my base.html (or whichever template), adding:
{% load version %}
<span class='version'>Last Updated: {% version_date %}</span>
If using GIT for source versioning, you might want manual promotion of stable
releases, and automatic numbering for development commits.
One why to obtain this in a Django project is:
In "PROJECT/_ init _.py" define:
__version__ = '1.0.1'
__build__ = ''
Then in setting.py do:
import os
import subprocess
import PROJECT
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
try:
PROJECT.__build__ = subprocess.check_output(["git", "describe", "--tags", "--always"], cwd=BASE_DIR).decode('utf-8').strip()
except:
PROJECT.__build__ = PROJECT.__version__ + " ?"
Thus, PROJECT._ build _ will show:
v1.0.1 in stable releases
and
v1.0.1-N-g8d2ec45
when the most recent tag doesn't point to the last commit (where N counts the number of additional commits after tag, followed by commit signature)
It seems the settings file would be a reasonable location to store the version number. I don't believe there is any Django accepted way to store a version number of your personal application. It seems like an application specific variable that you should define.
For more information on getting the version number out of svn: Getting SVN revision number into a program automatically
Not for Django applications, per se, but for Python modules, yes. See PEP 396, PEP 386 and the verlib library (easy_install verlib).
(I’d elaborate, but I just now discovered this, myself.)
Version info is typically maintained in git commit tags. Else, even git commits and last updated time is a good indicator of which version is running and when it was deployed.
For those using django-rest-framework and only having an API, you can return both of these; "last updated" as well as "last git commit" using an /api/version endpoint:
In views.py:
import os
import time
import subprocess
import json
class VersionViewSet(ViewSet):
def list(self, request):
# ['git', 'describe', '--tags'] # use this for named tags (version etc)
# ['git', 'describe', '--all', '--long'] # use this for any commit
# git log -1 --pretty=format:"Last commit %h by %an, %ar ("%s")"
# {"commit_hash": "%h", "full_commit_hash": "%H", "author_name": "%an", "commit_date": "%aD", "comment": "%s"}
FILE_DIR = os.path.dirname(os.path.abspath(__file__))
git_command = ['git', 'log', '-1', '--pretty={"commit_hash": "%h", "full_commit_hash": "%H", "author_name": "%an", "commit_date": "%aD", "comment": "%s"}']
git_identifier = subprocess.check_output(git_command, cwd=FILE_DIR).decode('utf-8').strip()
git_identifier = json.loads(git_identifier)
last_updated = time.strftime('%a, %-e %b %Y, %I:%M:%S %p (%Z)', time.localtime(os.path.getmtime('.git'))).strip()
return Response({
"last_updated": last_updated,
"git_commit": git_identifier
}, status=200)
In urls.py:
from myapp.views import VersionViewSet
router = routers.DefaultRouter()
...
router.register(r'version', VersionViewSet, base_name='version')
This creates the endpoint in line with the other endpoints in your API.
Output will be seen like this at http://www.example.com/api/version/:
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"last_updated": "Mon, 6 May 2019, 11:19:58 PM (IST)",
"git_commit": {
"commit_hash": "e265270",
"full_commit_hash": "e265270dda196f4878f4fa194187a3748609dde0",
"author_name": "Authorname",
"commit_date": "Mon, 6 May 2019 23:19:51 +0530",
"comment": "The git commit message or subject or title here"
}
}
I use this option __import__('project').VERSION or __import__('project').__version__. The version is put in the __init__.py file as everybody said, for example:
proyect_name
| __init__.py
# __init__.py file
VERSION = '1.0.0' # or __version__ = '1.0.0'
Now from everywhere you can get it:
# Error tracking settings
sentry_sdk.init(
...
release=__import__('cjvc_project').VERSION
)
I used a context processor and it looks like this:
import sys
sys.path.append('..')
from content_services import __version__
def get_app_version(request):
"""
Get the app version
:param request:
:return:
"""
return {'APP_VERSION': __version__}
Since the project name is content_services I have to change the sys path up 1 level so I can import it.
In case you use Git and version tagging you can display the application version in admin site header.
Create a version.py file in the project or any app module:
import os
import subprocess
FILE_DIR = os.path.dirname(os.path.abspath(__file__))
def get_version_from_git():
try:
return subprocess.check_output(['git', 'describe', '--tags'],
cwd=FILE_DIR).decode('utf-8').strip()
except:
return '?'
VERSION = get_version_from_git()
Add the version to admin site header in urls.py:
from django.contrib import admin
from django.utils.safestring import mark_safe
from utils import version
...
admin.site.site_header = mark_safe('MyApp admin <span style="font-size: x-small">'
f'({version.VERSION})</span>')
If you need to provide the version to external tools like Django Debug Toolbar, you can expose the version in project __init__.py as suggested above:
from utils import version
__version__ = version.VERSION
VERSION = __version__ # synonym

Django integration with Mako Jinja2 template comparison using : render_to_response. Problem?

I am using Django to build up my web project. As known that mako and Jinja2 template are faster that the one Django's given, I start finding way to integrate mako and Jinja2 into Django with using the Django's way of render_to_response method. After a lot research, I finally figure out the way to make this happen. However, in my integration, the jmeter's performance is something like Jinja2 (3ms) > Django's template (50ms) > mako (218ms).
If I went wrong with something?....Or pls help to advise some best practice to integrate jinja2 and mako.
Below is the coding ()
Mako2django.py
from django.http import HttpResponse
from django.template import Context
from mako.lookup import TemplateLookup
from mysite.settings import TEMPLATE_DIRS
def render_to_mako(t,c=None,context_instance=None):
path = TEMPLATE_DIRS
makolookup = TemplateLookup(directories=[path],output_encoding='utf- 8',input_encoding='utf-8')
mako_temp = makolookup.get_template(t)
if context_instance:
context_instance.update(c)
else:
context_instance = Context(c)
data = {}
for d in context_instance:data.update(d)
return HttpResponse(mako_temp.render(**data))
Jinja2django.py
from django.http import HttpResponse
from django.conf import settings
from jinja2 import Environment, ChoiceLoader, FileSystemLoader
# Setup environment
default_mimetype = getattr(settings, 'DEFAULT_CONTENT_TYPE')
# Create the Jinja2 Environment
env = Environment(
line_statement_prefix='%',
line_comment_prefix='##',
loader=ChoiceLoader([FileSystemLoader(path) for path in getattr(settings, 'TEMPLATE_DIRS', ())]))
def render_to_string(filename, context={}):
return env.get_template(filename).render(**context)
def render_to_jinja2(filename, context={},mimetype=default_mimetype, request = None):
if request: context['request'] = request
return HttpResponse(render_to_string(filename, context),mimetype=mimetype)
The view.py is similar as below
from draft.jinja2django import render_to_jinja2
def view1(request):
b = "helloworld"
return render_to_jinja2('testjinja.html', context={"test":b})
Starting from Django 1.2 you can create your custom template loader that returns your Template object. Doing that you can make django's render_to_response, render_to_string and counterparts render using your template system.
I am using this one:
https://gist.github.com/972162
It loads Jinja2 templates transparently and falls back to Django templates for admin, contrib and third-party apps.