I'm using App Engine's web-app templating system (similar if not identical to django)
Normally I render templates from my static directory /templates/ as
follows in my main handler:
dirname = os.path.dirname(__file__)
template_file = os.path.join(dirname, os.path.join('templates', template_name))
output = template.render(template_file,template_values)
self.response.out.write(output)
For my current app, I want to render a template contained in a
string. I'm using the following code:
t = template.Template(template_string)
c = template.Context(template_values)
output = t.render(c)
self.response.out.write(output)
It renders the template, but not the "include" tags contained within
the string. For example, the string template
"hello world {% include 'helloworld.html' %}"
renders "hello world" but does not render the contents of
'helloworld.html'.
I'm guessing that this has something to do with the string not being
in the same directory as 'helloworld.html', but I'm not sure how to
specify that the include tags should look in '/templates/*'
Any help would be appreciated,
Arjun
The webapp framework uses Django 0.9.6 templates. If you're loading templates from a string as you describe above, you need to configure the template loader so it can find dependencies loaded from files. Here's how webapp configures them.
{% include 'dirname/helloworld.html' %}
should work!
Related
help needed for general advice with the example on how to implement django apps.
I am trying to implement django app called django-simple-polls.
After installing the app with...
pip install ...
...adding the app in INSTALLED APPS
...the server still runs...
...migrations runs with no problem...
...being able to create basic poll from the admin...
questions starts here as I do not know how can I see the poll on the server:
1)
urlpatterns = [
...
url(r'^poll/', include('poll.urls')),
]
I am guessing polls app is installed somewhere within django so I do not have to create any additional folder/file in my project. Do I need to import a library to use the include() function? It also mean I need to run polls only in that certain url? That means 'poll.urls' already exist?
2)
Add this tags in your template file to show the poll:
{% load poll_tags %}
...
{% poll %}`
Again my guess is just to create any template folder and put as base the code above. What does "..." mean?. Where do I put the above code? How do I call that HTML file?
Is that pretty much the only way of building apps into a project?
Thanks
ps. At the moment when visiting http://127.0.0.1:8000/poll
^poll/
^admin/
^news/index/ [name='index']
^news/post/ [name='view_post']
^news/view/category [name='view_category']
The current path, poll, didn't match any of these.
But there is path called poll. :)
Following the guidance: https://github.com/applecat/django-simple-poll/blob/master/README.md
I have created the following basic template:
<!DOCTYPE html>
<html>
<head>
<title>Polls</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
{% load poll_tags %}
...
{% poll %}
</body>
</html>
I am unable to render the surveys created in the admin onto the website.
questions starts here as I do not know how can I see the poll on the server
Answer: Make sure you change your URL mapping for your poll's urls.py.
urlpatterns = [ ... url(r'^poll/', include('poll.urls')), ]
This should be added to your project's urls.py
Again my guess is just to create any template folder and put as base the code above. What does "..." mean?. Where do I put the above code? How do I call that HTML file?
If you've set your templates folder in your settings.py you can create another folder named poll then you put your templates there. Then on your views you do.
from django.shortcuts import render
def index(request):
return render(request, 'poll/yourtemplatename.html')
I tried to read file print.html that located in path: templates/print.html.
So, I used url_for() to refer to it as below:
{{ url_for('templates', filename='print.html') }}
However, I got an error as below Traceback:
BuildError: ('templates', {'filename': 'print.html'}, None)
What's wrong with this?
If I used {{ url_for('static', filename='print.html') }}, it just working find. However, file that I tried to read is not in static folder, it was in templates/print.html instead.
How can I use url_for() to read my file print.html in templates folder? Thanks.
I'll start by saying-- if you're trying to url_for a template, it's likely you're misunderstanding a concept. But, assuming you're know the difference between a rendered template and a static asset:
You could build a route for the print.html:
#app.route('/print')
def print():
return render_template('print.html')
Which would then enable you to url_for('print') to get the resolved url, and any dynamic components in your print.html template would get rendered to static html.
Otherwise, if the print.html is truly static-- just move it to the static folder, and use the url_for('static', filename='print.html') as you've indicated.
There's a handy Flask blueprint pattern that's useful if you're just rendering off a bunch of templates and don't need any view logic, see it here
I am trying to understand how to create a link to static files in jinja2.
Everything I look up relates to Flask whereas I am using just webapp2 at this stage.
My main.py file looks as follows:
import os
import urllib
from google.appengine.api import users
from google.appengine.ext import ndb
import jinja2
import webapp2
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'],
autoescape=True)
class MainPage(webapp2.RequestHandler):
def get(self):
template = JINJA_ENVIRONMENT.get_template('/templates/base.html')
self.response.out.write(template.render())
class ConsultsPage(webapp2.RequestHandler):
def get(self):
template = JINJA_ENVIRONMENT.get_template('/templates/consults.html')
self.response.out.write(template.render())
class CreateConsultPage(webapp2.RequestHandler):
def get(self):
template = JINJA_ENVIRONMENT.get_template('/templates/create-consult.html')
self.response.out.write(template.render())
app = webapp2.WSGIApplication([
('/', MainPage),
('/consults', ConsultsPage),
('/schedule/create-consult', CreateConsultPage)
], debug=True)
My base.html template contains the links to the static files in "/css", "/js" "/images" etc.
When I look at the localhost:8080/ and localhost:8080/consults all the static files are working. Page looks fine.
However the next level in the structure localhost:8080/consults/create-consult is not linking to static files.
When I view source I see that the css link has rendered as localhost:8080/consults/css/style.css , when the actual location is localhost:8080/css/style.css.
I understand I may need to make all links dynamic via some environment variable called uri_for, but I can't find the correct way to implement this.
I tried to replace my css link with
href="{{ uri_for('static', filename='css/screen.css') }}"
I was told by App Engine uri_for not set.
Basically would like to know the correct process for setting uri_for and then how to incorporate it in the paths for my links to static files.
Any help appreciated.
uri_for() is a Flask-specific function; it matches the name static to a route, which in turn then can be used to generate a path (like /static/css/screen.css if the static route is configured to handle /static/<path:filename> urls).
You just need to hardcode the path as /css/screen.css, no need for functions.
Note the leading /; that makes it an absolute path, relative to your current host. For a page at http://localhost:8080/foo/bar, such a path is then prefixed with http://localhost:8080 to form http://localhost:8080/css/screen.css. When you deploy to the app engine, the hostname will be different.
You could store a prefix URL or path in a global, so you can easily swap out the path for a CDN later:
JINJA_ENVIRONMENT.globals['STATIC_PREFIX'] = '/'
and use that in your templates:
<style src="{{ STATIC_PREFIX }}css/screen.css"></style>
You can now alter all such URLs in one place, by setting the STATIC_PREFIX to a different value, including http://somecdn.cdnprovider.tld/prefix/.
I need to make an app where i have the ready made standalone html templates avaialbe in my
app/ready_templates/template1
Now i have made an view where i display the name , thumbnail of that template to show to users
But there is demo link there which i want tthem to see the whole template.
basically if they click on demo then i want to redirect them to
template1/index.html so they can see all the templates images , js etc without linking anything to django views
So your requirement seems pretty straightforward. You can do this using the static files feature in Django. Basically whenever you have a bunch of static content like html, css, js images you put it in a folder in your app called static. For example the admin app that ships with Django has all it's static content in django/contrib/admin/static. Then you could place something simple like the below into your django template to link to the static content. You of course don't have to hard code each name like template1 you could have it dynamically generated in your django template.
{% load static from staticfiles %}
<a href="{% static "template1/index.html" %}" />template1</a>
I'm running the 1.4.2 appengine SDK locally on a windows machine. I have an application running Django 0.96. The template rendering is using the django wrapper from
google.appengine.ext.webapp.template.render
to render templates. I often use a relative path to link my templates e.g
{% extends "../templates/base.html" %}
After upgrading to Django 1.2 the find_template method from
django.template.loader in the appengine's Django 1.2 lib folder is now raising a TemplateDoesNotExist when the relative paths are used
for loader in template_source_loaders:
try:
#raises TemplateDoesNotExist name='../templates/home.html' dirs=None
source, display_name = loader(name, dirs)
return (source, make_origin(display_name, loader, name, dirs))
except TemplateDoesNotExist:
pass
raise TemplateDoesNotExist(name)
I've been stepping through the Django and AppEngine code for a while now but can't see any reason for this. Can anyone provide any more insight?
Thanks,
Richard
This problem bit me too when I converted from 0.96 to 1.2 Django templates. I was initially pushed to do so when SDK 1.4.2 started issuing the warning that I needed to pick a version, but when I looked into the much-needed improvements in the template language, I was eager to make the change.
And then everything broke. Like you, I used a lot of relative paths in my extends and include commands. It took a lot of debugging and digging, but I did figure out the cause of the problem and a pretty good solution.
The cause: in Django 1.2, the code that loads template files started using a command called safe_join to join path parts (you can see the code in google_appengine\lib\django_1_2\django\template\loaders\filesystem.py) . It won't allow relative paths to go above what it thinks of as the top-level directory. This is the same thing as a web server being configured to prevent you gaining access to the server's whole filesystem just by sticking some ..'s into your URL. The end result is that the
{% extends "../templates/base.html" %}
that used to be just fine breaks the rules and it isn't going to work.
The way that I fixed this in my application without completely restructuring how my templates are laid out is by implementing a custom TemplateLoader. Django's template rendering engine allows an application to have many different classes that know how to find templates in different ways. If you look in the directory that I gave above, you'll see that there are several provided, and they are all classes that inherit from BaseLoader. I provided my own that is custom-tailored to how my templates are laid out.
My project has a Rails-like lay-out:
app/
controllers/
home_controller.py
posts_controller.py
models/
...
views/
home/
index.html
about.html
posts/
show.html
new.html
shared/
base.html
post.html
Every template extends base.html and a couple include post.html, and they previously used relative paths to get to their location in base/. Ideally, I didn't even want to use the .. up-dir to get there, but it was required with 0.96. I created the following template loader to work with my scheme:
from django.conf import settings
from django.template import TemplateDoesNotExist
from django.template.loader import BaseLoader
from django.utils._os import safe_join
import os
class MvcTemplateLoader(BaseLoader):
"A custom template loader for the MVCEngine framework."
is_usable = True
__view_paths = None
def __init__(self, views_path):
self.views_path = views_path
# We only need to instantiate the view_paths class variable once.
if MvcTemplateLoader.__view_paths is None:
temp_paths = []
for each_path in os.listdir(views_path):
# We want to skip hidden directories, so avoid anything that starts with .
# This works on both Windows and *NIX, but could it fail for other OS's?
if not each_path.startswith('.'):
full_path = os.path.join(views_path, each_path)
if each_path == "shared":
# The shared directory is special. Since templates in many other directories will be
# inheriting from or including templates there, it should come second, right after the
# root views directory. For now, it will be first.
temp_paths.insert(0, full_path)
else:
temp_paths.append(full_path)
# The root views_path itself will always be first in order to give resolution precendence to templates
# that are specified with a parent directory. In other words, home/index.html will be immediately
# resolved with no ambiguity; whereas, index.html could resolve as bar/index.html rather than
# foo/index.html.
temp_paths.insert(0, views_path)
MvcTemplateLoader.__view_paths = temp_paths
def get_template_sources(self, template_name):
for template_dir in MvcTemplateLoader.__view_paths:
try:
yield safe_join(template_dir, template_name)
except UnicodeDecodeError:
# The template dir name was a bytestring that wasn't valid UTF-8.
raise
except ValueError:
# The joined path was located outside of this particular
# template_dir (it might be inside another one, so this isn't
# fatal).
pass
def load_template_source(self, template_name, template_dirs=None):
tried = []
for filepath in self.get_template_sources(template_name):
try:
file = open(filepath)
try:
return (file.read().decode(settings.FILE_CHARSET), filepath)
finally:
file.close()
except IOError:
tried.append(filepath)
error_msg = "Could not find %s in any of the views subdirectories." % template_name
raise TemplateDoesNotExist(error_msg)
load_template_source.is_usable = True
_loader = MvcTemplateLoader
And I caused my custom template loader to be included in the collection that Django tries by changing my application's main function to look like this:
def main():
from google.appengine.dist import use_library
use_library('django', '1.2')
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.conf import settings
views_path = os.path.join(os.path.dirname(__file__), 'app','views')
settings.TEMPLATE_LOADERS = (('gaemvclib.mvctemplateloader.MvcTemplateLoader', views_path), 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader')
(and then all the rest of the stuff that normally goes into your main function).
So, I think that you should be able to modify the TemplateLoader code above to match how you have your template directories laid out, and it will give you a greater control over not only how you layout you templates hierarcy but also how you write your extends and include statement. You no longer use .. but rather just give the path of the template relative to whatever in your project is the equivalent of my views directory.
One reason you get the error message TemplateDoesNotExist: templates\AdminListstr.html
Doublecheck the slash you use between "templates" and your template name. I wasted a few hours, then checked the value being returned by os.path.join.
In summary you need to insure you are using "/" not the Windows "\". If you use the wrong slash, your code will work on (Windows) development but fail when deployed.
My diagnostic code printed this: look at the last slash
And the value returned by os.path.join is:/base/data/home/apps/s~myapp/1.356037954289984934/templates\LabListstr.html
Aye yi yi!