commit
4bd4c7971a
@ -0,0 +1,45 @@ |
|||||||
|
*.py[cod] |
||||||
|
|
||||||
|
# C extensions |
||||||
|
*.so |
||||||
|
|
||||||
|
# Packages |
||||||
|
*.egg |
||||||
|
*.egg-info |
||||||
|
dist |
||||||
|
build |
||||||
|
eggs |
||||||
|
parts |
||||||
|
bin |
||||||
|
var |
||||||
|
sdist |
||||||
|
develop-eggs |
||||||
|
.installed.cfg |
||||||
|
lib |
||||||
|
lib64 |
||||||
|
|
||||||
|
# Installer logs |
||||||
|
pip-log.txt |
||||||
|
|
||||||
|
# Unit test / coverage reports |
||||||
|
.coverage |
||||||
|
.tox |
||||||
|
nosetests.xml |
||||||
|
|
||||||
|
# Translations |
||||||
|
*.mo |
||||||
|
|
||||||
|
# Mr Developer |
||||||
|
.mr.developer.cfg |
||||||
|
.project |
||||||
|
.pydevproject |
||||||
|
|
||||||
|
# Complexity |
||||||
|
output/*.html |
||||||
|
output/*/index.html |
||||||
|
|
||||||
|
# Sphinx |
||||||
|
docs/_build |
||||||
|
|
||||||
|
# Cookiecutter |
||||||
|
output/ |
@ -0,0 +1,41 @@ |
|||||||
|
cookiecutter-flask |
||||||
|
================== |
||||||
|
|
||||||
|
A Flask template for cookiecutter_. |
||||||
|
|
||||||
|
.. _cookiecutter: https://github.com/audreyr/cookiecutter |
||||||
|
|
||||||
|
Features |
||||||
|
-------- |
||||||
|
|
||||||
|
- Twitter Bootstrap 3 and starter templates |
||||||
|
- Flask-SQLAlchemy with basic User model |
||||||
|
- Flask-WTForms with login and registration forms |
||||||
|
- Procfile for deploying to a PaaS (e.g. Heroku) |
||||||
|
- nose for testing |
||||||
|
|
||||||
|
Screenshots |
||||||
|
----------- |
||||||
|
|
||||||
|
.. image:: https://dl.dropboxusercontent.com/u/1693233/github/cookiecutter-flask-01.png |
||||||
|
:target: https://dl.dropboxusercontent.com/u/1693233/github/cookiecutter-flask-01.png |
||||||
|
:alt: Home page |
||||||
|
|
||||||
|
.. image:: https://dl.dropboxusercontent.com/u/1693233/github/cookiecutter-flask-02.png.png |
||||||
|
:target: https://dl.dropboxusercontent.com/u/1693233/github/cookiecutter-flask-02.png.png |
||||||
|
:alt: Registration form |
||||||
|
|
||||||
|
Using this template |
||||||
|
------------------- |
||||||
|
:: |
||||||
|
|
||||||
|
$ pip install cookiecutter |
||||||
|
$ cookiecutter https://github.com/sloria/cookiecutter-flask.git |
||||||
|
|
||||||
|
You will be asked about your basic info (name, project name, etc.). This info will be used in your new project. |
||||||
|
|
||||||
|
|
||||||
|
License |
||||||
|
------- |
||||||
|
BSD licensed. |
||||||
|
|
@ -0,0 +1,11 @@ |
|||||||
|
{ |
||||||
|
"full_name": "Steven Loria", |
||||||
|
"email": "sloria1@gmail.com", |
||||||
|
"github_username": "sloria", |
||||||
|
"project_name": "My Flask App", |
||||||
|
"repo_name": "myflaskapp", |
||||||
|
"project_short_description": "A flasky app.", |
||||||
|
"release_date": "2013-08-15", |
||||||
|
"year": "2013", |
||||||
|
"version": "0.1.0" |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
*.py[cod] |
||||||
|
|
||||||
|
# C extensions |
||||||
|
*.so |
||||||
|
|
||||||
|
# Packages |
||||||
|
*.egg |
||||||
|
*.egg-info |
||||||
|
dist |
||||||
|
build |
||||||
|
eggs |
||||||
|
parts |
||||||
|
bin |
||||||
|
var |
||||||
|
sdist |
||||||
|
develop-eggs |
||||||
|
.installed.cfg |
||||||
|
lib |
||||||
|
lib64 |
||||||
|
|
||||||
|
# Installer logs |
||||||
|
pip-log.txt |
||||||
|
|
||||||
|
# Unit test / coverage reports |
||||||
|
.coverage |
||||||
|
.tox |
||||||
|
nosetests.xml |
||||||
|
|
||||||
|
# Translations |
||||||
|
*.mo |
||||||
|
|
||||||
|
# Mr Developer |
||||||
|
.mr.developer.cfg |
||||||
|
.project |
||||||
|
.pydevproject |
||||||
|
|
||||||
|
# Complexity |
||||||
|
output/*.html |
||||||
|
output/*/index.html |
||||||
|
|
||||||
|
# Sphinx |
||||||
|
docs/_build |
@ -0,0 +1,13 @@ |
|||||||
|
# Config file for automatic testing at travis-ci.org |
||||||
|
|
||||||
|
language: python |
||||||
|
|
||||||
|
python: |
||||||
|
- "3.3" |
||||||
|
- "2.7" |
||||||
|
|
||||||
|
# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors |
||||||
|
install: pip install -r requirements.txt |
||||||
|
|
||||||
|
# command to run tests, e.g. python setup.py test |
||||||
|
script: nosetests |
@ -0,0 +1,12 @@ |
|||||||
|
Copyright (c) {{ cookiecutter.year }}, {{ cookiecutter.full_name }} |
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
* Neither the name of {{ cookiecutter.project_name }} nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1 @@ |
|||||||
|
web: gunicorn {{cookiecutter.project_name}}.main:app -b 0.0.0.0:$PORT -w 3 |
@ -0,0 +1,15 @@ |
|||||||
|
============================= |
||||||
|
{{ cookiecutter.project_name }} |
||||||
|
============================= |
||||||
|
|
||||||
|
{{ cookiecutter.project_short_description}} |
||||||
|
|
||||||
|
|
||||||
|
Quickstart |
||||||
|
---------- |
||||||
|
|
||||||
|
:: |
||||||
|
git clone https://github.com/{{cookiecutter.github_username}}/{{ cookiecutter.repo_name }} |
||||||
|
cd {{cookiecutter.repo_name}} |
||||||
|
pip install -r requirements/dev.txt |
||||||
|
python run.py |
@ -0,0 +1,3 @@ |
|||||||
|
# Included because many Paas's require a requirements.txt file in the project root |
||||||
|
# Just installs the production requirements. |
||||||
|
-r requirements/prod.txt |
@ -0,0 +1,5 @@ |
|||||||
|
# Everything the developer needs in addition to the production requirements |
||||||
|
-r prod.txt |
||||||
|
|
||||||
|
# Testing |
||||||
|
nose |
@ -0,0 +1,12 @@ |
|||||||
|
# Everything that needed in production |
||||||
|
Flask==0.10.1 |
||||||
|
Flask-SQLAlchemy==1.0 |
||||||
|
Flask-WTF==0.9.0 |
||||||
|
Jinja2==2.7 |
||||||
|
MarkupSafe==0.18 |
||||||
|
SQLAlchemy==0.8.2 |
||||||
|
WTForms==1.0.4 |
||||||
|
Werkzeug==0.9.3 |
||||||
|
gunicorn==17.5 |
||||||
|
itsdangerous==0.23 |
||||||
|
wsgiref==0.1.2 |
@ -0,0 +1,8 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
import os |
||||||
|
from {{cookiecutter.repo_name}} import main |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
port = int(os.environ.get('PORT', 5000)) |
||||||
|
main.app.run() |
@ -0,0 +1,7 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from flask import Flask |
||||||
|
from flask.ext.sqlalchemy import SQLAlchemy |
||||||
|
|
||||||
|
app = Flask(__name__) |
||||||
|
app.config.from_pyfile('settings.py') |
||||||
|
db = SQLAlchemy(app) |
@ -0,0 +1,17 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from flask_wtf import Form |
||||||
|
from wtforms import TextField, PasswordField |
||||||
|
from wtforms.validators import DataRequired, Email, EqualTo, Length |
||||||
|
|
||||||
|
class RegisterForm(Form): |
||||||
|
username = TextField('Username', validators=[DataRequired(), Length(min=3, max=25)]) |
||||||
|
email = TextField('Email', validators=[DataRequired(), Email(), Length(min=6, max=40)]) |
||||||
|
password = PasswordField('Password', |
||||||
|
validators=[DataRequired(), Length(min=6, max=40)]) |
||||||
|
confirm = PasswordField( |
||||||
|
'Verify password', |
||||||
|
[DataRequired(), EqualTo('password', message='Passwords must match')]) |
||||||
|
|
||||||
|
class LoginForm(Form): |
||||||
|
username = TextField('Username', validators=[DataRequired()]) |
||||||
|
password = PasswordField('Password', validators=[DataRequired()]) |
@ -0,0 +1,14 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
""" |
||||||
|
Entry point for all things, to avoid circular imports. |
||||||
|
""" |
||||||
|
import os |
||||||
|
from .app import app, db |
||||||
|
from .models import * |
||||||
|
from .views import * |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
port = int(os.environ.get('PORT', 5000)) |
||||||
|
app.run(host='0.0.0.0', port=port) |
@ -0,0 +1,26 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
|
||||||
|
""" |
||||||
|
{{cookiecutter.project_name}} models. |
||||||
|
""" |
||||||
|
from .app import db |
||||||
|
|
||||||
|
class User(db.Model): |
||||||
|
|
||||||
|
__tablename__ = 'users' |
||||||
|
|
||||||
|
id = db.Column(db.Integer, primary_key=True) |
||||||
|
username = db.Column(db.String, unique=True, nullable=False) |
||||||
|
email = db.Column(db.String, unique=True, nullable=False) |
||||||
|
password = db.Column(db.String, nullable=False) |
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, username=None, email=None, password=None): |
||||||
|
self.username = username |
||||||
|
self.email = email |
||||||
|
self.password = password |
||||||
|
|
||||||
|
def __repr__(self): |
||||||
|
return '<User %r>' % (self.username) |
||||||
|
|
||||||
|
db.create_all() |
@ -0,0 +1,10 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
import os |
||||||
|
|
||||||
|
APP_DIR = os.path.abspath(os.path.dirname(__file__)) |
||||||
|
PROJECT_ROOT = os.path.abspath(os.path.join(APP_DIR, os.pardir)) |
||||||
|
DB_NAME = "test.db" |
||||||
|
DB_PATH = os.path.join(PROJECT_ROOT, DB_NAME) |
||||||
|
SQLALCHEMY_DATABASE_URI = "sqlite:///{0}".format(DB_PATH) |
||||||
|
DEBUG = True |
||||||
|
SECRET_KEY = 'shhhh' |
@ -0,0 +1,86 @@ |
|||||||
|
/* ============================================================================= |
||||||
|
App specific CSS file. |
||||||
|
========================================================================== */ |
||||||
|
|
||||||
|
/* universal */ |
||||||
|
|
||||||
|
html { |
||||||
|
overflow-y: scroll; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
padding-top: 60px; |
||||||
|
} |
||||||
|
|
||||||
|
section { |
||||||
|
overflow: auto; |
||||||
|
} |
||||||
|
|
||||||
|
textarea { |
||||||
|
resize: vertical; |
||||||
|
} |
||||||
|
|
||||||
|
.container-narrow{ |
||||||
|
margin: 0 auto; |
||||||
|
max-width: 700px; |
||||||
|
} |
||||||
|
|
||||||
|
/* Forms */ |
||||||
|
|
||||||
|
.navbar-form input[type="text"], |
||||||
|
.navbar-form input[type="password"] { |
||||||
|
width: 180px; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.form-register { |
||||||
|
width: 50%; |
||||||
|
} |
||||||
|
|
||||||
|
.form-register .form-control { |
||||||
|
position: relative; |
||||||
|
font-size: 16px; |
||||||
|
height: auto; |
||||||
|
padding: 10px; |
||||||
|
-webkit-box-sizing: border-box; |
||||||
|
-moz-box-sizing: border-box; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
/* footer */ |
||||||
|
|
||||||
|
footer { |
||||||
|
margin-top: 45px; |
||||||
|
padding-top: 5px; |
||||||
|
border-top: 1px solid #eaeaea; |
||||||
|
color: #999999; |
||||||
|
} |
||||||
|
|
||||||
|
footer a { |
||||||
|
color: #999999; |
||||||
|
} |
||||||
|
|
||||||
|
footer p { |
||||||
|
float: right; |
||||||
|
margin-right: 25px; |
||||||
|
} |
||||||
|
|
||||||
|
footer ul { |
||||||
|
list-style: none; |
||||||
|
} |
||||||
|
|
||||||
|
footer ul li { |
||||||
|
float: left; |
||||||
|
margin-left: 10px; |
||||||
|
} |
||||||
|
|
||||||
|
footer .company { |
||||||
|
float: left; |
||||||
|
margin-left: 25px; |
||||||
|
} |
||||||
|
|
||||||
|
footer .footer-nav { |
||||||
|
float: right; |
||||||
|
margin-right: 25px; |
||||||
|
list-style: none; |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
// place any jQuery/helper plugins in here, instead of separate, slower script files.
|
@ -0,0 +1,5 @@ |
|||||||
|
(function() { |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}).call(this); |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,30 @@ |
|||||||
|
{% raw %} |
||||||
|
{% extends "_layouts/base.html" %} |
||||||
|
|
||||||
|
{% block page_title %}Page Not Found{% endblock %} |
||||||
|
|
||||||
|
{% block css %} |
||||||
|
<style type="text/css"> |
||||||
|
body { |
||||||
|
padding-top: 40px; |
||||||
|
} |
||||||
|
h1, p { |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
h1 { |
||||||
|
font-size: 64px; |
||||||
|
margin: 75px 0 50px; |
||||||
|
} |
||||||
|
p { |
||||||
|
font-size: 24px; |
||||||
|
margin: 15px 0; |
||||||
|
} |
||||||
|
</style> |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<h1>404</h1> |
||||||
|
<p>Sorry, that page doesn't exist.</p> |
||||||
|
<p>Want to <a href="{{ url_for('home') }}">go home</a> instead?</p> |
||||||
|
{% endblock %} |
||||||
|
{% endraw %} |
@ -0,0 +1,74 @@ |
|||||||
|
{% raw %} |
||||||
|
<!doctype html> |
||||||
|
<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ --> |
||||||
|
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]--> |
||||||
|
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]--> |
||||||
|
<!--[if IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]--> |
||||||
|
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]--> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
|
||||||
|
<title>{% block page_title %} |
||||||
|
{% endraw %} |
||||||
|
{{ cookiecutter.project_name }} |
||||||
|
{% raw %} |
||||||
|
{% endblock %} |
||||||
|
</title> |
||||||
|
<meta name="description" content="{% block meta_description %}{% endblock %}"> |
||||||
|
<meta name="author" content="{% block meta_author %}{% endblock %}"> |
||||||
|
|
||||||
|
<!-- Mobile viewport optimized: h5bp.com/viewport --> |
||||||
|
<meta name="viewport" content="width=device-width"> |
||||||
|
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='libs/bootstrap3/css/bootstrap.min.css') }}"> |
||||||
|
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> |
||||||
|
{% block css %}{% endblock %} |
||||||
|
|
||||||
|
</head> |
||||||
|
<body class="{% block body_class %}{% endblock %}"> |
||||||
|
{% block body %} |
||||||
|
{% with form=form %} |
||||||
|
{% include "_layouts/nav.html" %} |
||||||
|
{% endwith %} |
||||||
|
|
||||||
|
<header>{% block header %}{% endblock %}</header> |
||||||
|
<div class="{% block content_class %}container{% endblock content_class %}"> |
||||||
|
|
||||||
|
<div role="main"> |
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %} |
||||||
|
{% if messages %} |
||||||
|
<div class="row"> |
||||||
|
<div class="span12"> |
||||||
|
{% for category, message in messages %} |
||||||
|
<div class="alert alert-{{ category }}"> |
||||||
|
<a class="close" title="Close" href="#" data-dismiss="alert">×</a> |
||||||
|
{{message}} |
||||||
|
</div><!-- end .alert --> |
||||||
|
{% endfor %} |
||||||
|
</div><!-- end span --> |
||||||
|
</div><!-- end row --> |
||||||
|
{% endif %} |
||||||
|
{% endwith %} |
||||||
|
|
||||||
|
{% block content %}{% endblock %} |
||||||
|
</div> |
||||||
|
|
||||||
|
</div><!-- end container --> |
||||||
|
|
||||||
|
{% include "_layouts/footer.html" %} |
||||||
|
|
||||||
|
|
||||||
|
<!-- JavaScript at the bottom for fast page loading --> |
||||||
|
<script src="{{ url_for('static', filename='libs/jquery2/jquery-2.0.3.min.js') }}"></script> |
||||||
|
<script src="{{ url_for('static', filename='libs/bootstrap3/js/bootstrap.min.js') }}"></script> |
||||||
|
|
||||||
|
|
||||||
|
<script src="{{ url_for('static', filename='js/plugins.js') }}"></script> |
||||||
|
<script src="{{ url_for('static', filename='js/script.js') }}"></script> |
||||||
|
|
||||||
|
<!-- end scripts --> |
||||||
|
{% endblock %} |
||||||
|
</body> |
||||||
|
</html> |
||||||
|
{% endraw %} |
@ -0,0 +1,14 @@ |
|||||||
|
<footer> |
||||||
|
<small> |
||||||
|
<ul class="company"> |
||||||
|
<li>© {{ cookiecutter.full_name }}</li> |
||||||
|
</ul> |
||||||
|
|
||||||
|
<ul class="footer-nav"> |
||||||
|
{% raw %} |
||||||
|
<li><a href="{{ url_for('about') }}">About</a></li> |
||||||
|
{% endraw %} |
||||||
|
<li><a href="mailto:{{ cookiecutter.email }}">Contact</a></li> |
||||||
|
</ul> |
||||||
|
</small> |
||||||
|
</footer> |
@ -0,0 +1,42 @@ |
|||||||
|
{% raw %} |
||||||
|
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation"> |
||||||
|
<!-- Brand and toggle get grouped for better mobile display --> |
||||||
|
<div class="navbar-header"> |
||||||
|
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse"> |
||||||
|
<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="{{ url_for('home') }}"> |
||||||
|
{% endraw %} |
||||||
|
{{cookiecutter.project_name}} |
||||||
|
{% raw %} |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
<!-- Collect the nav links, forms, and other content for toggling --> |
||||||
|
<div class="collapse navbar-collapse navbar-ex1-collapse"> |
||||||
|
<ul class="nav navbar-nav"> |
||||||
|
<li class="active"><a href="{{ url_for('home') }}">Home</a></li> |
||||||
|
<li><a href="{{ url_for('about') }}">About</a></li> |
||||||
|
</ul> |
||||||
|
{% if session.logged_in %} |
||||||
|
<a class="btn btn-default btn-sm navbar-btn navbar-right" href="{{ url_for('logout') }}">Log out</a> |
||||||
|
<ul class="nav navbar-nav navbar-right"> |
||||||
|
<li><a href="{{ url_for('members') }}">Logged in as {{ session.username }}</a></li> |
||||||
|
</ul> |
||||||
|
{% elif form %} |
||||||
|
<ul class="nav navbar-nav navbar-right"> |
||||||
|
<li><a href="{{ url_for('register') }}">Create account</a></li> |
||||||
|
</ul> |
||||||
|
<form method="POST" class="navbar-form form-inline navbar-right" action="" role="login"> |
||||||
|
<div class="form-group"> |
||||||
|
{{ form.username(placeholder="Username", class_="form-control") }} |
||||||
|
{{ form.password(placeholder="Password", class_="form-control") }} |
||||||
|
</div> |
||||||
|
<button type="submit" class="btn btn-default">Log in</button> |
||||||
|
</form> |
||||||
|
{% endif %} |
||||||
|
</div><!-- /.navbar-collapse --> |
||||||
|
</nav> |
||||||
|
{% endraw %} |
@ -0,0 +1,12 @@ |
|||||||
|
{% raw %} |
||||||
|
{% extends "_layouts/base.html" %} |
||||||
|
|
||||||
|
{% block content %} |
||||||
|
<div class="body-content"> |
||||||
|
<div class="row"> |
||||||
|
<h1>About</h1> |
||||||
|
<p>This template was created by <a href="http://github.com/sloria/">Steven Loria</a> for use with the <a href="http://github.com/audreyr/cookiecutter/">cookiecutter</a> package by <a href="http://github.com/audreyr/">Audrey Roy</a>.</p> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{% endblock %} |
||||||
|
{% endraw %} |
@ -0,0 +1,95 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||||
|
<meta name="description" content=""> |
||||||
|
<meta name="author" content=""> |
||||||
|
|
||||||
|
<title>Jumbotron Template for Bootstrap</title> |
||||||
|
|
||||||
|
<!-- Bootstrap core CSS --> |
||||||
|
<link href="../static/libs/bootstrap3/css/bootstrap.css" rel="stylesheet"> |
||||||
|
|
||||||
|
<!-- Custom styles for this template --> |
||||||
|
<link href="jumbotron.css" rel="stylesheet"> |
||||||
|
</head> |
||||||
|
|
||||||
|
<body> |
||||||
|
|
||||||
|
<div class="navbar navbar-inverse navbar-fixed-top"> |
||||||
|
<div class="container"> |
||||||
|
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".nav-collapse"> |
||||||
|
<span class="icon-bar"></span> |
||||||
|
<span class="icon-bar"></span> |
||||||
|
<span class="icon-bar"></span> |
||||||
|
</button> |
||||||
|
<a class="navbar-brand" href="#">Project name</a> |
||||||
|
<div class="nav-collapse collapse"> |
||||||
|
<ul class="nav navbar-nav"> |
||||||
|
<li class="active"><a href="#">Home</a></li> |
||||||
|
<li><a href="#about">About</a></li> |
||||||
|
<li><a href="#contact">Contact</a></li> |
||||||
|
<li class="dropdown"> |
||||||
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a> |
||||||
|
<ul class="dropdown-menu"> |
||||||
|
<li><a href="#">Action</a></li> |
||||||
|
<li><a href="#">Another action</a></li> |
||||||
|
<li><a href="#">Something else here</a></li> |
||||||
|
<li class="divider"></li> |
||||||
|
<li class="nav-header">Nav header</li> |
||||||
|
<li><a href="#">Separated link</a></li> |
||||||
|
<li><a href="#">One more separated link</a></li> |
||||||
|
</ul> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
<form class="navbar-form form-inline pull-right"> |
||||||
|
<input type="text" placeholder="Email"> |
||||||
|
<input type="password" placeholder="Password"> |
||||||
|
<button type="submit" class="btn">Sign in</button> |
||||||
|
</form> |
||||||
|
</div><!--/.nav-collapse --> |
||||||
|
</div> |
||||||
|
</div><!-- end navbar navbar-inverse --> |
||||||
|
|
||||||
|
<div class="container"> |
||||||
|
|
||||||
|
<!-- Main jumbotron for a primary marketing message or call to action --> |
||||||
|
<div class="jumbotron"> |
||||||
|
<h1>Hello, world!</h1> |
||||||
|
<p>This is a template for a simple marketing or informational website. It includes a large callout called the hero unit and three supporting pieces of content. Use it as a starting point to create something more unique.</p> |
||||||
|
<p><a class="btn btn-primary btn-large">Learn more »</a></p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="body-content"> |
||||||
|
|
||||||
|
<!-- Example row of columns --> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-lg-4"> |
||||||
|
<h2>Heading</h2> |
||||||
|
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p> |
||||||
|
<p><a class="btn btn-default" href="#">View details »</a></p> |
||||||
|
</div> |
||||||
|
<div class="col-lg-4"> |
||||||
|
<h2>Heading</h2> |
||||||
|
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p> |
||||||
|
<p><a class="btn btn-default" href="#">View details »</a></p> |
||||||
|
</div> |
||||||
|
<div class="col-lg-4"> |
||||||
|
<h2>Heading</h2> |
||||||
|
<p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p> |
||||||
|
<p><a class="btn btn-default" href="#">View details »</a></p> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<hr> |
||||||
|
|
||||||
|
<footer> |
||||||
|
<p>© Company 2013</p> |
||||||
|
</footer> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> <!-- /container --> |
||||||
|
|
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,34 @@ |
|||||||
|
{% raw %} |
||||||
|
{% extends "_layouts/base.html" %} |
||||||
|
{% block content %} |
||||||
|
<!-- Main jumbotron for a primary marketing message or call to action --> |
||||||
|
<div class="jumbotron"> |
||||||
|
{% endraw %} |
||||||
|
<h1>Welcome to {{ cookiecutter.project_name }}</h1> |
||||||
|
{% raw %} |
||||||
|
<p>This is a starter Flask template. It includes the Twitter Bootstrap 3, jQuery 2, Flask-SQLAlchemy, WTForms, and nose out of the box.</p> |
||||||
|
<p><a href="https://github.com/sloria/cookiecutter-flask" class="btn btn-primary btn-large">Learn more »</a></p> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="body-content"> |
||||||
|
|
||||||
|
<!-- Example row of columns --> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-lg-4"> |
||||||
|
<h2>Bootstrap 3</h2> |
||||||
|
<p>Sleek, intuitive, and powerful mobile-first front-end framework for faster and easier web development.</p> |
||||||
|
<p><a class="btn btn-default" href="http://getbootstrap.com/">View details »</a></p> |
||||||
|
</div> |
||||||
|
<div class="col-lg-4"> |
||||||
|
<h2>SQLAlchemy</h2> |
||||||
|
<p>SQLAlchemy is the Python SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL.</p> |
||||||
|
<p><a href="http://www.sqlalchemy.org/" class="btn btn-default" href="#">View details »</a></p> |
||||||
|
</div> |
||||||
|
<div class="col-lg-4"> |
||||||
|
<h2>WTForms</h2> |
||||||
|
<p>WTForms is a flexible forms validation and rendering library for python web development.</p> |
||||||
|
<p><a href="http://wtforms.simplecodes.com/" class="btn btn-default" href="#">View details »</a></p> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{% endblock %} |
||||||
|
{% endraw %} |
@ -0,0 +1,7 @@ |
|||||||
|
{% raw %} |
||||||
|
{% extends "_layouts/base.html" %} |
||||||
|
{% block content %} |
||||||
|
<h1>Welcome {{ session.username }}</h1> |
||||||
|
<h3>This is the members-only page.</h3> |
||||||
|
{% endblock %} |
||||||
|
{% endraw%} |
@ -0,0 +1,29 @@ |
|||||||
|
{% raw %} |
||||||
|
{% extends "_layouts/base.html" %} |
||||||
|
{% block content %} |
||||||
|
<div class="container-narrow"> |
||||||
|
<h1>Register</h1> |
||||||
|
<br/> |
||||||
|
<form class="form form-register" method="POST" action="" role="form"> |
||||||
|
<div class="form-group"> |
||||||
|
{{form.username.label}} |
||||||
|
{{form.username(placeholder="Username", class_="form-control")}} |
||||||
|
</div> |
||||||
|
<div class="form-group"> |
||||||
|
{{form.email.label}} |
||||||
|
{{form.email(placeholder="Email", class_="form-control")}} |
||||||
|
</div> |
||||||
|
<div class="form-group"> |
||||||
|
{{form.password.label}} |
||||||
|
{{form.password(placeholder="Password", class_="form-control")}} |
||||||
|
</div> |
||||||
|
<div class="form-group"> |
||||||
|
{{form.confirm.label}} |
||||||
|
{{form.confirm(placeholder="Password (again)", class_="form-control")}} |
||||||
|
</div> |
||||||
|
<p><input class="btn btn-default btn-submit" type="submit" value="Register"></p> |
||||||
|
</form> |
||||||
|
<p><em>Already registered?</em> Click <a href="/">here</a> to login.</p> |
||||||
|
</div> |
||||||
|
{% endblock %} |
||||||
|
{% endraw %} |
@ -0,0 +1,28 @@ |
|||||||
|
'''Unit testing''' |
||||||
|
|
||||||
|
import unittest |
||||||
|
try: |
||||||
|
from nose.tools import * # PEP8 asserts |
||||||
|
except ImportError: |
||||||
|
import sys |
||||||
|
print('nose required. Run "pip install nose".') |
||||||
|
|
||||||
|
from main import app |
||||||
|
|
||||||
|
class Test{{cookiecutter.project_name}}(unittest.TestCase): |
||||||
|
|
||||||
|
def setUp(self): |
||||||
|
app.config['TESTING'] = True |
||||||
|
self.app = app.test_client() |
||||||
|
|
||||||
|
def test_add(self): |
||||||
|
'''An example test.''' |
||||||
|
assert_equal(1 + 1, 2) |
||||||
|
|
||||||
|
def json_response(response, code=200): |
||||||
|
'''Checks that the status code is OK and returns the json as a dict.''' |
||||||
|
assert_equal(response.status_code, code) |
||||||
|
return json.loads(response.data) |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
unittest.main() |
@ -0,0 +1,82 @@ |
|||||||
|
# -*- coding: utf-8 -*- |
||||||
|
from __future__ import unicode_literals |
||||||
|
from flask import render_template, session, request, flash, redirect, url_for |
||||||
|
from functools import wraps |
||||||
|
from .app import app, db |
||||||
|
from .models import User |
||||||
|
from .forms import RegisterForm, LoginForm |
||||||
|
from sqlalchemy.exc import IntegrityError |
||||||
|
|
||||||
|
def flash_errors(form): |
||||||
|
for field, errors in form.errors.items(): |
||||||
|
for error in errors: |
||||||
|
flash("Error in the {0} field - {1}" |
||||||
|
.format(getattr(form, field).label.text, error), 'error') |
||||||
|
|
||||||
|
def login_required(test): |
||||||
|
@wraps(test) |
||||||
|
def wrap(*args, **kwargs): |
||||||
|
if 'logged_in' in session: |
||||||
|
return test(*args, **kwargs) |
||||||
|
else: |
||||||
|
flash('You need to log in first.') |
||||||
|
return redirect(url_for('home')) |
||||||
|
return wrap |
||||||
|
|
||||||
|
@app.route("/", methods=["GET", "POST"]) |
||||||
|
def home(): |
||||||
|
form = LoginForm(request.form) |
||||||
|
if request.method == 'POST': |
||||||
|
u = User.query.filter_by(username=request.form['username'], |
||||||
|
password=request.form['password']).first() |
||||||
|
if u is None: |
||||||
|
error = 'Invalid username or password.' |
||||||
|
flash(error, 'error') |
||||||
|
else: |
||||||
|
session['logged_in'] = True |
||||||
|
session['username'] = u.username |
||||||
|
flash("You are logged in.", 'success') |
||||||
|
return redirect(url_for("members")) |
||||||
|
return render_template("home.html", form=form) |
||||||
|
|
||||||
|
|
||||||
|
@app.route("/members/") |
||||||
|
@login_required |
||||||
|
def members(): |
||||||
|
return render_template("members.html") |
||||||
|
|
||||||
|
|
||||||
|
@app.route('/logout/') |
||||||
|
def logout(): |
||||||
|
session.pop('logged_in', None) |
||||||
|
session.pop('username', None) |
||||||
|
flash('You are logged out.', 'info') |
||||||
|
return redirect(url_for('home')) |
||||||
|
|
||||||
|
@app.route("/register/", methods=['GET', 'POST']) |
||||||
|
def register(): |
||||||
|
form = RegisterForm(request.form, csrf_enabled=False) |
||||||
|
if form.validate_on_submit(): |
||||||
|
new_user = User(form.username.data, form.email.data, form.password.data) |
||||||
|
try: |
||||||
|
db.session.add(new_user) |
||||||
|
db.session.commit() |
||||||
|
flash("Thank you for registering. You can now log in.", 'success') |
||||||
|
return redirect(url_for('home')) |
||||||
|
except IntegrityError as err: |
||||||
|
print(err) |
||||||
|
flash("That username and/or email already exists. Try again.", 'error') |
||||||
|
else: |
||||||
|
flash_errors(form) |
||||||
|
return render_template('register.html', form=form) |
||||||
|
|
||||||
|
|
||||||
|
@app.route("/about/") |
||||||
|
def about(): |
||||||
|
form = LoginForm(request.form) |
||||||
|
return render_template("about.html", form=form) |
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(404) |
||||||
|
def page_not_found(e): |
||||||
|
return render_template("404.html"), 404 |
Loading…
Reference in new issue