Flask: render_template with path - flask

I have several different templates that I'm trying to use for my flask app.
I have tried the following but it seems to only look directly inside /templates and not /templates/folder1, templates/folder2 etc.
return render_template('index.html', template_folder='folder1')
return render_template('folder1/index.html')
both do not work as expected, how can I specify the sub folder of different templates.

The template folder can be specified when creating the Flask app (or Blueprint):
from flask import Flask
app = Flask(__name__, template_folder='folder1')
Source: http://flask.pocoo.org/docs/0.12/api/#application-object
from flask import Blueprint
auth_blueprint = Blueprint('auth', __name__, template_folder='folder1')
Source: http://flask.pocoo.org/docs/0.12/blueprints/#templates
The template_folder is relative to where the app/blueprint is located.
Use the os library to create paths to template folders outside of the app/blueprint directory.
eg.
import os
APP_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TEMPLATE_PATH = os.path.join(APP_PATH, 'templates/')
run from subdirectory of app root
APP_PATH retrieves parent directory path (app root)

Be sure the Python file and Template folder are all under the same working directory folder.

I think Sean is right, but also: have you tried double quotes? I'm using Blueprint, so it may be different, but this is what mine looks like:
return render_template("users/register.html")
so yours may be:
return render_template("folder1/index.html")

Related

Static method returning empty list in Django

I am trying to run a Django app on local server. It works fine on mu Ubuntu machine but in mac, I can't get the CSS for
localhost:8000/admin
and
localhost:8000/docs to load.
On digging further, I found out that the static URL in main "urls.py" file
return an empty list instead of a URL pattern.
Does anyone have an idea why it is like that on the new mac system?
I have had the same issue. I was able to fix it manually by adding the following snippet to my urls.py file in the project (the urls.py file next to settings.py)...
from django.conf import settings
from django.conf.urls.static import static
from django.views.static import serve
. . .
if settings.TESTING_PRODUCTION:
urlpatterns += [
re_path(r'^static/(?P<path>.*)$', serve, {
'document_root': settings.STATIC_ROOT,
}),
]
I pulled this together from the Django Docs here.
I needed to do this so that I could test the "production" environment with manage.py runserver by manually setting DEBUG = False in settings.py which changes a few other URLs and also turns off trace printing in my code.
In my settings.py file, I have some code to set TESTING_PRODUCTION to True as well. But in actual production with a real web server, the code should set TESTING_PRODUCTION to False so that the static files can be served by the webserver directly and not through Django.

flask routes not working on gae application

I have the following directory structure for my GAE project:
endpoints/
insights/
insights.py
init.py
lib/
__init__.py
insights.yaml
so in order to get access to the third libraries within lib folder I add the next code in __init__.py file.
import os
import sys
def add_lib_path():
lib_directory = os.path.dirname(os.path.abspath(__file__))
if lib_directory not in sys.path:
sys.path.insert(0, lib_directory)
and I added this code in the insights.py file before the import statements:
from lib import add_lib_path
add_lib_path()
the problem is that now I can import third libraries correctly but my #app.route('/something', methods=['POST']) are not working.
I send a post request and it returns status 200 but it doesn't go inside my #app.route code, I can actually send any route and it just pass out returning 200 but not data and not error.
My imports look like this:
from lib import add_lib_path
add_lib_path()
from flask import Flask, request
And my code inside #app.route('/something', methods=['POST']) looks like this:
def someDef():
some code ...
return response
my yaml file looks like this:
runtime: python27
api_version: 1
threadsafe: false
service: insights
handlers:
- url: /.*
script: endpoints/insights/insights.py
libraries:
- name: ssl
version: latest
Any suggestions about this? Thanks in advance!
The wildcard URL handler in your app.yaml is intercepting the /something post:
handlers:
- url: /.*
script: application.app
You will need to either map out individual urls in your app, or make unique url set for your insights, like /insights/.* in app.yaml. Either way, you can't have a catch-all url handler in your app.yaml if there are other urls you want to give special treatment to, like sending to a separate service.
ok I just changed in the app.yaml file the threadsafe to true, then I added the script like this endpoints.insights.insights.app, using Python module path (with dots not slash) and in my insights.py file I changed #app.route('/something', methods=['POST']) to #app.route('/insights/something', methods=['POST']) .. I added the complete URL that I defined in the app.yaml file and now it's working.
Thanks to #GAEfan for the help, I'll accept the GAEfan answer because it helped me a lot

Django Static Files: Any Way to Get More Fine-Grained Control?

Django's static files feature allows you to specify certain directories to have "collected" in to a public-facing folder. That's great, but is there any way to get more fine-grained control than just having certain folders? For instance, is there anyway to specify ...
Including specific files
Excluding specific files
Excluding specific sub-directories
For instance, I'd like to say "collect all the files in this one folder except for this one file and this one directory". Alternatively, I could accomplish the same thing if I could pick specific files, and then pick all of the sub-directories of that one directory (except the one I don't want).
Is any of that possible?
I wrote a custom django-admin command to enable a COLLECT_STATIC_IGNORE setting.
First create the following stucture in any app folder:
appname/
management/
__init__.py
commands/
__init__.py
_private.py
collectstatic.py
In collectstatic.py put:
from django.contrib.staticfiles.management.commands.collectstatic import Command
from django.conf import settings
class Command(Command):
def set_options(self, **options):
"""
Set instance variables based on an options dict
"""
self.interactive = options['interactive']
self.verbosity = int(options.get('verbosity', 1))
self.symlink = options['link']
self.clear = options['clear']
self.dry_run = options['dry_run']
ignore_patterns = options['ignore_patterns']
if options['use_default_ignore_patterns']:
ignore_patterns += ['CVS', '.*', '*~']
ignore_patterns += settings.COLLECT_STATIC_IGNORE # Added.
self.ignore_patterns = list(set(ignore_patterns))
self.post_process = options['post_process']
Or, even better, like #CantucciHQ suggested, use super:
class Command(Command):
def set_options(self, **options):
super(Command, self).set_options(**options)
self.ignore_patterns += settings.COLLECT_STATIC_IGNORE
self.ignore_patterns = list(set(self.ignore_patterns))
This overrides the set_options function from de build-in collectstatic command.
In settings.py add COLLECT_STATIC_IGNORE.
This example ignores scss files and all files in admin folders.
COLLECT_STATIC_IGNORE = ['*.scss', 'admin', ... ]
Then:
python manage.py collectstatic
Flags work so after adding something to COLLECT_STATIC_IGNORE you might want to use --clear to clear the existing files before trying to copy or link the original file.
python manage.py collectstatic --clear

Separate custom settings variable between development, staging and production

I'm following the project structure as laid out by Zachary Voase, but I'm struggling with one specific issue.
I'd very much like to have a custom settings boolean variable (let's call it SEND_LIVE_MAIL) that I would be using in the project. Basically, I'd like to use this settings variable in my code and if SEND_LIVE_MAIL is True actually send out a mail, whereas when it is set to False just print its contents out to the console. The latter would apply to the dev environment and when running unittests.
What would be a good way of implementing this? Currently, depending on the environment, the django server uses dev, staging or prd settings, but for custom settings variables I believe these need to be imported 'literally'. In other words, I'd be using in my views something like
from settings.development import SEND_LIVE_MAIL
which of course isn't what I want. I'd like to be able to do something like:
from settings import SEND_LIVE_MAIL
and depending on the environment, the correct value is assigned to the SEND_LIVE_MAIL variable.
Thanks in advance!
You shouldn't be importing directly from your settings files anyways. Use:
>>> from django.conf import settings
>>> settings.SEND_LIVE_MAIL
True
The simplest solution is to have this at the bottom of your settings file:
try:
from local_settings import *
except ImportError:
pass
And in local_settings.py specify all your environment-specific overrides. I generally don't commit this file to version control.
There are more advanced ways of doing it, where you end up with a default settings file and a per-environment override.
This article by David Cramer covers the various approaches, including both of the ones I've mentioned: http://justcramer.com/2011/01/13/settings-in-django/
import os
PROJECT_PATH = os.path.dirname(__file__)
try:
execfile(os.path.join(PROJECT_PATH, local_settings.py'))
except IOError:
pass
Then you can have your local_settings.py behave as if it was pasted directly into your settings.py:
$ cat local_settings.py
INSTALLED_APPS += ['foo']
You can do something like this for a wide variety of environment based settings, but here's an example for just SEND_LIVE_MAIL.
settings_config.py
import re
import socket
class Config:
def __init__(self):
fqdn = socket.getfqdn()
env = re.search(r'(devhost|stagehost|prodhost)', fqdn)
env = env and env.group(1)
env = env or 'devhost'
if env == 'devhost':
self.SEND_LIVE_MAIL = # whatever
elif env == 'stagehost':
self.SEND_LIVE_MAIL = # whatever
elif env == 'prodhost':
self.SEND_LIVE_MAIL = # whatever
config = Config()
settings.py
from settings_config import config
SEND_LIVE_MAIL = config.SEND_LIVE_MAIL

Django paths, developing in windows, deploying on linux

I'm developing Django apps on my local windows machine then deploying to a hosted linux server. The format for paths is different between the two and manually replacing before deployment is consuming more time than it should. I could code based on a variable in my settings file and if statements but I was wondering if anyone had best practices for this scenario.
The Django book suggests using os.path.join (and to use slashes instead of backslashes on Windows):
import os.path
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
)
I think this is the best solution as you can easily create relative paths like that. If you have multiple relative paths, a helper function will shorten the code:
def fromRelativePath(*relativeComponents):
return os.path.join(os.path.dirname(__file__), *relativeComponents).replace("\\","/")
If you need absolute paths, you should use an environment variable (with os.environ["MY_APP_PATH"]) in combination with os.path.join.
We have a situation very similar to yours, and we've been using different paths in settings, basing on sys.platform.
Something like this:
import os, sys
DEVELOPMENT_MODE = sys.platform == 'win32'
if DEVELOPMENT_MODE:
HOME_DIR = 'c:\\django-root\\'
else:
HOME_DIR = '/home/django-root/'
It works quite OK - assumed all development is being done on Windows.
Add
import os.path
BASE_PATH = os.path.dirname(__file__)
at the top of your settings file, and then use BASE_PATH everywhere you want to use a path relative to your Django project.
For example:
MEDIA_ROOT = os.path.join(BASE_PATH, 'media')
(You need to use os.path.join(), instead of simply writing something like MEDIA_ROOT = BASE_PATH+'/media', because Unix joins directories using '/', while windows prefers '\')
in your settings.py add the following lines
import os.path
SETTINGS_PATH = os.path.abspath(os.path.dirname(__file__))
head, tail = os.path.split(SETTINGS_PATH)
#add some directories to the path
import sys
sys.path.append(os.path.join(head, "apps"))
#do what you want with SETTINGS_PATH