I'm very new at Pyramid, I have used Django in the past, but I can't find a clean explanation of how to use base templating in Pyramid Chameleon templates.
I have a very simple .pt file which I want to be my base.pt template it's something like this:
<link href="static/bootstrap/css/bootstrap.css" rel="stylesheet">
<head>
</head>
<body>
<header class="navbar navbar-inverse navbar-fixed-top bs-docs-nav" role="banner">
<div class="container">
<div class="navbar-header">
<button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".bs-navbar-collapse">
<span class="sr-only">Toggle navigation</span>
</button>
My project
</div>
</div>
</header>
</body>
</html>
As you can see I try to have bootstrap header in all the following templates of my project, so what do I need to have so that all templates inherit or have base.pt as base template ? In Django I will just use {% include base.html %}
Chameleon and Mako are the two templating languages with support currently bundled within Pyramid. However, Jinja2 is officially supported by the pyramid_jinja2 addon and is easily activated. Jinja2 provides a syntax very similar to Django's if you do not wish to learn Chameleon.
config.include('pyramid_jinja2')
#view_config(..., renderer='myapp:templates/home.jinja2')
def view(request):
return {}
Related
I am ordering the query result using time but I want to give the user the ability to choose the way he wants to order data lets say he wants to use alphabetical order or he wants to order with ascending or descending order I want to know how to that in flask, sorry for my bad English
this is my code
#routes.route('/posts')
def show_posts():
page=request.args.get('page',1,type=int)
posts=Posts.query.order_by(Posts.timestamp.desc).paginate(page,per_page=10)
return render_template('routes/show_posts.html',posts=posts)
ok, so this is my first flask answer (I only yesterday finished tutorials):
EDIT/INFO: at the end I didn't use any forms to accomplish this, just simple routes, but forms could be used too, this just seemed a bit more simple (more because I couldn't figure out how to use those forms for this :))
EDIT2/INFO: also I don't think for this particular code there is any need for that methods argument in decorators so that could probably be removed for this specific code
the main file (it contains everything because that was easier than to show each module I would have used)
from flask import Flask, render_template, url_for
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
#app.route('/posts/filter_by/<string:key>')
#app.route('/posts', methods=['GET', 'POST'])
#app.route('/')
def all_posts(key=None):
posts = Post.query.order_by(Post.title.asc()).all()
if key == 'ZA':
posts = Post.query.order_by(Post.title.desc()).all()
return render_template('posts.html', posts=posts)
if __name__ == '__main__':
app.run(debug=True)
and here is the template (uses bootstrap stuff):
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle"
type="button" id="dropdownMenu1" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
Sort by
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu1">
<a class="dropdown-item" href="{{ url_for('all_posts', key='AZ') }}">A-Z</a>
<a class="dropdown-item" href="{{ url_for('all_posts', key='ZA') }}">Z-A</a>
</div>
</div>
{% for post in posts %}
<div class="card">
<div class="card-body">
<h4 class="card-title">{{ post.title }}</h4>
<p class="card-text">{{ post.content }}</p>
</div>
</div>
{% endfor %}
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
So the first thing (top-down):
import all that is needed
then initialize the Flask and SQLAlchemy classes and also config the app to the database
Create the post model for storing posts and stuff (that I don't exactly know)
So then comes the main part - the route.
So first of I create a route with a variable:
#app.route('/posts/filter_by/<string:key>')
this will allow to request this URL and be able to get additional information (from that variable) that will help with handling the response
then add more routes so that the url looks cleaner and that is pretty much the only reason (as far as I know)
then define the function:
name it what You feel like but You have to pass the variable as an argument and since multiple routes are used and probably for other reasons too set a default value for that argument so that it is not necessary to be provided
then using simple if statements and using that variable handle how posts are ordered
then return the template including those posts in that order
Now about the template
First of it uses bootstrap stuff and I got the most code from there (just copy pasted from their website) or here which is a nice cheatsheet.
So I copied the dropdown menu from that cheatsheet and just changed some values, most notably the href attribute:
Those I replaced with url_for functions and passed the variable too as You can see so that the request sent is with that variable that will allow to handle how posts are ordered
Hope all is clear, if You have any more questions, ask them.
first thing first Matiiss your answer is very good thanks for the help, now I have found another way it has some similarities to Matiiss answer i want to share it so if anyone found this question now you have two answers to choose from.
#routes.route('/posts')
def show_posts():
posts= Posts.query.order_by(Posts.name.desc()).all()
filter_rule = request.args.get('filter')
if filter_rule == 'A-Z':
posts= Posts.query.order_by(Posts.name).all()
return render_template('posts.html',posts=posts)
HTML part
<div class="row">
<div class="col-md-3">
<div class="nav nav-pills flex-column" role="tablist" aria-orientation="vertical">
<a class="nav-item nav-link"
href="{{ url_for('.show_posts', filter='A-Z') }}">
A-Z
</a>
<a class="nav-item nav-link"
href="{{ url_for('.show_posts', filter='Z-A') }}">
Z-A
</a>
</div>
</div>
So I've been working w/ Flask & Bootstrap on an Apache Server. I've gotten it to the point where I can access the app & render the "first" or "main" template w/ the following route:
from view.py:
#app.route('/')
def fn_home():
return render_template("main.html")
Unfortunately, every attempt to route to another webpage/function from main.html fails. I'm using the "url_for" function in the navbar list href, attempting to get flask to supply the xls-upload.html webpage to Apache.
from main.html:
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li>Upload Spreadsheets </li>
from view.py:
#app.route('/upload')
def upload():
return render_template("xls-upload.html")
It looks like the function is being engaged, since the URL changes to http://myapp/upload, but the html page is NOT rendered/returned by the function - instead I receive a 404 "Not Found". I can't seem to return anything from the function, even return "Hello World".
It "seems" like Apache is really trying to resolve the http://myapp/upload path, rather than having a socket opened up to the Flask application through which the html is then sent. I'm not sure if this is a FCGI problem, if I'm missing a relative/absolute path issue, misunderstanding how Flask works in general, or some combination of all, etc.
I'm new to Flask so I'm hoping that someone could help me along the way since I really feel I've come to a dead end.
Thanks in advance!
My flask app is structured as follows:
var/www/cgi-bin/myapp/ (root dir)
start.fcgi
view.py (the flask routing/app file)
static (dir)
bootstrap files
templates (dir)
main.html
xls-upload.html
Here are my applicable files:
1) /etc/httpd/conf.d/myapp:
<VirtualHost *:80>
ServerAdmin webmaster#localhost
DocumentRoot /var/www/cgi-bin/myapp/static/
ServerName myapp
Alias /static/ /var/www/cgi-bin/myapp/static/
ScriptAlias / /var/www/cgi-bin/myapp/start.fcgi
<Directory "var/www/cgi-bin/myapp">
AllowOverride None
Order allow,deny
Allow from all
AuthType Basic
AuthUserFile /etc/httpd/.htpasswd
AuthName 'Enter Password'
Require valid-user
</Directory>
</VirtualHost>
2) /var/www/cgi-bin/myapp/start.fcgi:
#!/usr/bin/python
# IMPORTS:
from flup.server.fcgi import WSGIServer
from view import app
if __name__ == '__main__':
WSGIServer(app).run()
3) /var/www/cgi-bin/myapp/view.py:
#!/usr/bin/python
# IMPORTS:
import os
from flask import Flask, render_template, url_for, request, session, redirect
from werkzeug import secure_filename
# STATIC VARIABLES
UPLOAD_FOLDER = 'var/www/cgi-bin/myapp/xls-dir'
ALLOWED_EXTENSIONS = set(['xls'])
## flask:
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
# FUNCTIONS
def fn_allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
#app.route('/')
def fn_home():
return render_template("main.html")
#app.route('/upload')
def upload():
return render_template("xls-upload.html")
#return "HI there"
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
4) /var/www/cgi-bin/myapp/templates/main.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>ALG Tools HOME</title>
<!-- Bootstrap -->
<link href="{{ url_for('static', filename = 'css/bootstrap.min.css') }}" rel="stylesheet">
</head>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">ALG Tool - HOME</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li>Upload Spreadsheets </li>
<li>Download Spreadsheets</li>
<li>Generate Configs</li>
</ul>
</div>
</div>
</nav>
<body>
<h2>ALG stuff</h2>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{{ url_for('static', filename = 'js/bootstrap.min.js') }}"></script>
</body>
</html>
5) /var/www/cgi-bin/myapp/templates/xls-upload.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>ALG XLS Upload</title>
<!-- Bootstrap -->
<link href="{{ url_for('static', filename = 'css/bootstrap.min.css') }}" rel="stylesheet">
</head>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">ALG Tool - HOME</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li>Upload Spreadsheets </li>
<li>Download Spreadsheets</li>
<li>Generate Configs</li>
</ul>
</div>
</div>
</nav>
<body>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{{ url_for('static', filename = 'js/bootstrap.min.js') }}"></script>
</body>
</html>
FCGI isn't really a recommended way to serve a Python web app. You should look into one of the many ways of running WSGI.
However, assuming you need to do this for some reason, you have a minor configuration issue which is the cause of your problem; you need a trailing slash on the ScriptAlias path.
ScriptAlias / /var/www/cgi-bin/myapp/start.fcgi/
With this, Apache will pass the full path to the start.fcgi script, instead of replacing it.
Note that even with FCGI, you should not put your app code in cgi-bin. It doesn't need to be there, as it isn't run by the web server like a CGI app. In fact, your code should not even be under /var/www at all.
I am experiencing a weird issue using Django on Google App Engine. I have a file upload form defined within a django-app like this:
class ConvertForm(forms.Form):
from = forms.ChoiceField(choices=choices_from,
label='from:')
to = forms.ChoiceField(choices=choices_to,
label='to:')
class Meta:
fields = ('from','to')
And then I have in my app.views file the following:
def convert(request):
if request.POST:
form = ConvertForm(request.POST,request.FILES)
if form.is_valid():
from = form.cleaned_data['from']
to = form.cleaned_data['to']
# Redirect to a result page after post to avoid duplicates
return HttpResponseRedirect('/convert/results')
else:
form = ConvertForm()
args = {}
args.update(csrf(request))
args['form']=form
return render_to_response('convert.html',args)
The form-part of my convert.html template looks like this:
<form action="/convert/convert/" method="post" enctype="multipart/form-data">{%\
csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input type="submit" name="submit" value="Convert">
</form>
It's supposed to be a file-upload form (hence the multipart), but I edited the form-contents for brevity.
Now, when I browse the proper url, nothing happens. I can clearly see that the correct functions are being called, since replacing the body of the convert() function with a simple
return render_to_response('convert_test.html',{'some_text':some_text})
displays the value of some_text in the browser window. Is there any additional quirks when dealing with forms within GAE that I am missing, or why isn't convert.html being rendered with the form? I should mention that all of this is on localhost, I haven't deployed it yet.
Edit: After some more fiddling around with this, it seems that maybe the source of the error is in the inheritance of templates. If I take out all django-tags {} in the convert.html, I can get the form to render correctly.
So, the question is now how do I properly set up template inheritance within GAE?
My full convert.html template looks like this:
{% extends "base.html" %}
{% block content %}
<h2>[ convert ]</h2>
<form action="/convert/convert/" method="post" enctype="multipart/form-data">{% \
csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input type="submit" name="submit" value="Convert">
</form>
{% endblock %}
So basically redefining the content block of the base.html template. This was working perfectly before I tested it on GAE, so I can't shake the feeling that's involved somehow.
If it is relevant, my django settings.py looks like this for the templates:
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__),'..','convert','templates'),
os.path.join(os.path.dirname(__file__),'..','templates'),
)
And as I said, taking out the {}-tags from convert.html gives me the form, but rendered on its own in an otherwise completely white and empty page.
This is the contents of my base.html template
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/\
DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="description" content="Your description goes here" />
<meta name="keywords" content="your,keywords,goes,here" />
<meta name="author" content="Your Name" />
<link rel="stylesheet" type="text/css" href="/static/css/basic-minimal.css" ti\
tle="Basic Minimal" media="all" />
<title>Basic Minimal v1.2</title>
</head>
<body class="light">
<div id="wrap">
<div id="header">
<h1>[ snazzy title ]</h1>
</div>
<div id="sidebar">
<ul>
<li>[ Convert ]</li>
<li>[ Transform ]</li>
<li>[ Help ]</li>
<li>[ References ]</li>
</ul>
</div>
<div id="content">
<h2>[ more title ]</h2>
<p>Text</p>
<p>More text</p>
</div>
</div>
</body>
</html>
This works beautifully for the "title" page (even the css gets loaded), but rendering other templates as convert.html above using this as a base does not work.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 6 years ago.
Improve this question
I am new to Python web frameworks and trying to learn Flask. I went through the tutorial and all was good. I am now trying to make my own little app to learn the framework. I had in my main flask.py file the following code
#app.route('/')
def index():
return render_template('main.html')
In main.html I have this html
{% extends "layout.html" %}
{% block content %}
<div> Foo bar test</div>
{% endblock %}
and then in layout.html I have a basic web layout that looks like this
<!DOCTYPE html>
<title>Flaskpad</title>
<link href="/static/css/bootstrap.css" rel="stylesheet">
<link href="/static/css/flaskpad.css" rel="stylesheet">
<style type="text/css">
.socials {
padding: 10px;
}
</style>
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<ul class="nav">
<li class="active">
<a class="brand" href="#">Flaskpad/a>
</li>
<li>Login</li>
<li>About</li>
<li>Contact</li>
</ul>
</div>
</div>
</div>
<div class="container">
<div class="row fcontent">
<div class="span3">
Empty Space
</div>
<div class="span6 maincontent">
{% block content %}{% endblock %}
</div>
<div class="span3">
Empty Space
</div>
</div>
<div class="row ffooter">
<div class="span12">
Made by Bar Foo
</div>
</div>
</div>
</body>
I know the css links are done incorrectly but I just have them there as placeholders. Now when I run python flask.py and go to localhost:5000 the page turns up blank and I cannot figure out why. If I put in normal text in the main.html before the extends that will show up so I know it is loading main.html but it seems to not be extending the layout. The page literally is blank as when you view source there is nothing. I cannot figure this out.
Works for me. Do you have your HTML files in a folder named "templates" relative to your python module?
Here's my full test code.
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('main.html')
if __name__ == '__main__':
app.run()
Edit: You said: "I had in my main flask.py file the following code" - You should probably not name it flask.py, this may be screwing things up.
Which web browser are you using for your testing?
I tried your code and it worked for me using chrome and firefox but I did notice that you have an incorrectly closed anchor tag in your layout.html at the line
<a class="brand" href="#">Flaskpad/a>
Maybe that is causing the browser to show the page as blank?
Aren't you missing the opening <head> tag in the layout.html ? I don't see it anywhere.
I gave up on it, I couldn't get it to work. I made a new directory, copied and pasted the text into new files and then ran it and it worked successfully.
I do not know why this was not working in this instance.
I am using latest Pyramid to build a web app. Somehow we have started using Chameleon as the template engine. I have used Mako before and it was extremely simple to create a base template. Is this possible with chameleon as well?
I have tried to look through the docs but I can not seem to find an easy solution.
With Chameleon >= 2.7.0 you can use the "load" TALES expression. Example:
main.pt:
<html>
<head>
<div metal:define-slot="head"></div>
</head>
<body>
<ul id="menu">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<div metal:define-slot="content"></div>
</body>
</html>
my_view.pt:
<html metal:use-macro="load: main.pt">
<div metal:fill-slot="content">
<p>Bonjour tout le monde.</p>
</div>
</html>
Another option, which was used prior Chameleon got an ability to load templates from the filesystem, is to pass the "base" template as a parameter.
To simplify things, I often wrap such stuff into a "theme" object:
class Theme(object):
def __init__(self, context, request):
self.context = context
self.request = request
layout_fn = 'templates/layout.pt'
#property
def layout(self):
macro_template = get_template(self.layout_fn)
return macro_template
#property
def logged_in_user_id(self):
"""
Returns the ID of the current user
"""
return authenticated_userid(self.request)
which can then be used like this:
def someview(context, request):
theme = Theme(context, request)
...
return { "theme": theme }
Which then can be used in the template:
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
metal:use-macro="theme.layout.macros['master']">
<body>
<metal:header fill-slot="header">
...
</metal:header>
<metal:main fill-slot="main">
...
</metal:main>
</body>
</html>
Make a template here:
<proj>/<proj>/templates/base.pt
with contents:
<html>
<body>
<div metal:define-slot="content"></div>
</body>
</html>
Use the template here:
<proj>/<proj>/templates/about_us.pt
by inserting the contents:
<div metal:use-macro="load: base.pt">
<div metal:fill-slot="content">
<p>Hello World.</p>
</div>
</div>