Initial commit

master
Steven Loria 11 years ago
commit 4bd4c7971a
  1. 45
      .gitignore
  2. 41
      README.rst
  3. 11
      cookiecutter.json
  4. 42
      {{cookiecutter.repo_name}}/.gitignore
  5. 13
      {{cookiecutter.repo_name}}/.travis.yml
  6. 12
      {{cookiecutter.repo_name}}/LICENSE
  7. 1
      {{cookiecutter.repo_name}}/Procfile
  8. 15
      {{cookiecutter.repo_name}}/README.rst
  9. 3
      {{cookiecutter.repo_name}}/requirements.txt
  10. 5
      {{cookiecutter.repo_name}}/requirements/dev.txt
  11. 12
      {{cookiecutter.repo_name}}/requirements/prod.txt
  12. 8
      {{cookiecutter.repo_name}}/run.py
  13. 0
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/__init__.py
  14. 7
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py
  15. 17
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/forms.py
  16. 14
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/main.py
  17. 26
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/models.py
  18. 10
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py
  19. 86
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/static/css/style.css
  20. 1
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/static/js/plugins.js
  21. 5
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/static/js/script.js
  22. 5584
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/static/libs/bootstrap3/css/bootstrap.css
  23. 11
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/static/libs/bootstrap3/css/bootstrap.min.css
  24. 1995
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/static/libs/bootstrap3/js/bootstrap.js
  25. 8
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/static/libs/bootstrap3/js/bootstrap.min.js
  26. 8
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/static/libs/jquery2/jquery-2.0.3.min.js
  27. 30
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/404.html
  28. 74
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/_layouts/base.html
  29. 14
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/_layouts/footer.html
  30. 42
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/_layouts/nav.html
  31. 12
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/about.html
  32. 95
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/example.html
  33. 34
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/home.html
  34. 7
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/members.html
  35. 29
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/register.html
  36. 0
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/__init__.py
  37. 28
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/unit_tests.py
  38. 82
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/views.py

45
.gitignore vendored

@ -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.

File diff suppressed because one or more lines are too long

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">&times;</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 &raquo;</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 &raquo;</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 &raquo;</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 &raquo;</a></p>
</div>
</div>
<hr>
<footer>
<p>&copy; 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 &raquo;</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 &raquo;</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 &raquo;</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 &raquo;</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…
Cancel
Save