README_EN.md
# fpage
FPage is a tornado classic project generator(cli).
`classic` means the style before separation of front-end and backend became popular.
Quick start a project with tornado + mako/jinja2 + peewee/sqlalchemy。
## Use
```
pip install fpage
fpage new [project-name]
```
or
```bash
python fpage.py new [project-name]
```
Step by step:
1. Type your project name.
2. Choose template engine (**M**ako/**J**inja2/**T**ornado)
3. Choose the ORM you like best(**P**eewee/**S**QLChemy)
4. Enter Y to confirm.
Now here is a directory, it's protype of your project.
Run `python app.py`, and access http://127.0.0.1:9000 to check.
Example:
```bash
# fpage new test_project
Project Name (test_project):
Template Engine [M/J/T]:
Database ORM [P/S]:
Project Name: test_project
Template Engine: mako
Database ORM: peewee
Sure (Y/n)?
Complete.
To get started:
cd test_project
python app.py
Served at http://localhost:9000
```
## Features
* based on tornado
* MVT Pattern (Model, View, Template)
* compatible with python 3 & python 2
* secure support (secure cookie, xsrf)
* URL Route decorator like flask (@route)
* simple session support(based on secure cookie)
* choose your favourite template engine (mako/jinjia2/tornado)
* defined template variable: req static url_for csrf_token/xsrf_token config
* choose your favourite ORM sqlalchemy/peewee
* message flashing (like of messages django, and flash of flask)
* simple user system
* page title help tool
* a filter extension for peewee's model_to_dict
* paginator
## Directories
* model
* view
* templates
* lib - tools
## Feature example
* **URL Route decorator like flask (@route)**
```python
from view import route, url_for, View
@route('/')
class Index(View):
def get(self):
self.render()
def post(self):
pass
@route('/about', name='about')
class About(View):
def get(self):
self.render()
```
* **Simple session support(based on secure cookie)**
```python
@route('/')
class Index(View):
def get(self):
self.session['test'] = 'session test 1'
del self.session['test']
self.session['test'] = 'session test 2'
self.render(s=self.session['test'])
```
* **Choose your favourite template engine (mako/jinjia2/tornado)**
```mako
<body>
${self.body()}
<%block name="script"/>
</body>
```
```jinja
<body>
{% block body %}{% endblock %}
{% block script %}{% endblock %}
</body>
```
* **Defined template variable: req static url_for csrf_token/xsrf_token config**
req -> request object
```mako
${ req.current_user }
```
static -> static file
```mako
<script src="${ static('js/main.js') }"></script>
<link rel="stylesheet" href="${ static('css/style.css') }">
```
url_for -> url reverse
```mako
<p><a href="${ url_for('jump') }">Jump Page</a></p>
<p><a href="${ url_for('about') }">About Page</a></p>
```
csrf_token -> self.xsrf_form_html()
```mako
<form method="post" class="am-form">
${csrf_token}
</form>
```
* **Choose your favourite ORM sqlalchemy/peewee**
config
```python
DATABASE_URI = "sqlite:///database.db"
```
sqlalchemy
```python
from model import BaseModel
from sqlalchemy import Column, Integer, String, Float, ForeignKey, Boolean
class Test(BaseModel):
__tablename__ = 'test'
id = Column(Integer, primary_key=True, autoincrement=True)
test = Column(String)
```
peewee
```python
from peewee import *
from model import BaseModel
class Test(BaseModel):
test = TextField()
```
* **Message flashing (like of messages django, and flash of flask)**
view
```python
@route('/jump_test', name='jump')
class Jump(View):
def get(self):
self.messages.error('Message Test: Error!!')
self.redirect(url_for('about'))
```
template
```mako
% for msg in get_messages():
% if msg.tag == 'success':
<div class="ui-green">
${msg.txt}
</div>
% elif msg.tag == 'error':
<div class="ui-red">
${msg.txt}
</div>
% endif
% endfor
```
* a filter extension for peewee
see `lib/pvpatch.py`
* **Page title help tool**
config
```python
config.TITLE = 'FPage'
```
View
```python
self.render(page_title=page_title('Test Board', 'Forum')
```
The title of page: Test Board » Forum » FPage
* **paginator**
model.pagination_peewee / model.pagination_sqlalchemy
Definition:
```python
def pagination(count_all, query, page_size, cur_page=1, nearby=2):
pass
```
Return:
```python
{
'cur_page': cur_page,
'prev_page': prev_page,
'next_page': next_page,
'first_page': first_page,
'last_page': last_page,
'page_numbers': list(items),
'page_count': page_count,
'items': [...],
'info': {
'page_size': page_size,
'count_all': count_all,
}
}
```
## TODO-LIST
* nothing