Table of Contents
web mvc framework for python
- Python:
- CPython: 3.6 or newer
Package is uploaded on PyPI.
You can install it with pip:
$python3 pip install hallo
中文文档.
hallo --version
hallo create your-project
cd your-project
hallo install
file: module/hello.py
from app.module.base import BaseController
class HelloController(BaseController):
def world(self):
return 'hello world'
url: http://127.0.0.1/hello/world
file: module/json.py
from app.module.base import BaseController
class JsonController(BaseController):
def this_is_ok(self):
return self.ok('this is ok')
def this_is_error(self):
return self.error('this is error')
url: http://127.0.0.1/json/this-is-json
{
"code": 0,
"data": "this is ok",
}
url: http://127.0.0.1/json/this-is-error
{
"code": 1,
"data": "this is error",
}
file: module/html.py
from app.module.base import BaseController
class HtmlController(BaseController):
def index(self):
return self.render('html/index.html')
url: http://127.0.0.1/html/index
<p>hello world!</p>
file: models/user.py
from pymyorm.model import Model
class User(Model):
tablename = 'user'
run table/model to reflect all model from database
python console.py table/model
development
class DevelopmentConfig(Config):
ENV = 'development'
testing
class TestingConfig(Config):
ENV = 'testing'
production
class ProductionConfig(Config):
ENV = 'production'
host
class Config(object):
HOST = '127.0.0.1'
port
class Config(object):
PORT = 80
server name
class Config(object):
# domain
SERVER_NAME = 'hallo.com'
session
class Config(object):
# session
SECRET_KEY = ''
PERMANENT_SESSION_LIFETIME = timedelta(hours=1)
file upload
class Config(object):
# file upload
MAX_CONTENT_LENGTH = 8 * 1024 * 1024 # 8M
mysql
class Config(object):
# db: mysql / pgsql
DB_POOL_SIZE = 1
DB_CONF = dict(
source='mysql',
host='127.0.0.1',
port=3306,
user='root',
password='password',
database='hallo',
charset='utf8'
)
redis
class Config(object):
# redis
REDIS_URL = 'redis://127.0.0.1:6379/0'
memcache
class Config(object):
# cache
CACHE_CONF = [
'127.0.0.1:11211'
]
oss
class Config(object):
# oss
OSS_CONF = dict(
endpoint='',
bucket='',
access_key_id='',
access_key_secret=''
)
1、auto routing
http://127.0.0.1/<module>/<controller>/<action>
<module>: the directory or subdirectory under module
<controller>: the controller
<action>: the function of controller
2、user defined routing
file: app/router.py
router = Router(app=app)
router.add('/hello/<name>', 'hello/hi')
file: module/hello.py
from app.module.base import BaseController
class HelloController(BaseController):
def hi(self, name):
return f'hi, {name}'
url: http://127.0.0.1/hello/jack
hi, jack
3、subdomain
file: config.py
class Config(object):
SERVER_NAME = 'hallo.com'
file: app/router.py
admin = Router(app=app, subdomain='admin', module='admin')
1、get
file: module/http.py
from app.module.base import BaseController
class HttpController(BaseController):
def info(self):
name = self.get('name')
age = self.get('age')
return self.ok(dict(
name=name,
age=age
))
url: http://127.0.0.1/http/info?name=jack&age=18
{
"name": "jack",
"age": 18
}
2、post
file: module/http.py
from app.module.base import BaseController
class HttpController(BaseController):
def save(self):
name = self.post('name')
age = self.post('age')
return self.ok(dict(
name=name,
age=age
))
url: http://127.0.0.1/http/save
{
"name": "lucy",
"age": 18
}
3、header
file: module/header.py
from app.module.base import BaseController
class HeaderController(BaseController):
def token(self):
token = self.header('Token')
return self.ok(token)
url: http://127.0.0.1/header/token
{
"code": 0,
"data": {
"token": "123456"
}
}
4、file
file: module/file/upload.py
from app.module.base import BaseController
class FileController(BaseController):
def upload(self):
file = self.file('file')
url: http://127.0.0.1/file/upload
file: sql/user.sql
create table if not exists `user` (
`id` int unsigned not null auto_increment,
`username` varchar(32) not null default '',
`phone` varchar(16) not null default '',
`money` decimal(10,2) not null default 0,
`gender` tinyint unsigned not null default 0,
`password` varchar(128) not null default '',
`time` timestamp not null default current_timestamp,
primary key(`id`),
unique key `idx_username` (`username`),
key `idx_phone` (`phone`),
key `idx_time` (`time`)
) engine=InnoDB default charset=utf8mb4;
1、add user
file: module/user.py
from app.module.base import BaseController
from app.models.user import User
class UserController(BaseController):
def add(self):
username = self.post('username')
phone = self.post('phone')
money = self.post('money')
gender = self.post('gender')
model = User()
model.username = username
model.phone = phone
model.money = money
model.gender = gender
model.save()
return self.ok()
url: http://127.0.0.1/user/add
{
"code": 0,
"data": "ok"
}
2、edit user
file: module/user.py
from app.module.base import BaseController
from app.models.user import User
class UserController(BaseController):
def edit(self):
id = self.post('id')
username = self.post('username')
phone = self.post('phone')
money = self.post('money')
gender = self.post('gender')
model = User.find().where(id=id).one()
if not model:
return self.error('user not exists')
model.username = username
model.phone = phone
model.money = money
model.gender = gender
model.save()
return self.ok()
url: http://127.0.0.1/user/edit
{
"code": 0,
"data": "ok"
}
3、delete user
file: module/user.py
from app.module.base import BaseController
from app.models.user import User
class UserController(BaseController):
def delete(self):
id = self.post('id')
User.find().where(id=id).delete()
return self.ok()
url: http://127.0.0.1/user/delete
{
"code": 0,
"data": "ok"
}
4、list user
file: module/user.py
from app.module.base import BaseController
from app.models.user import User
class UserController(BaseController):
def list(self):
self.init_page()
model = User.find()
total = model.count()
all = model.offset(self.offset).limit(self.limit).all(raw=True)
return self.resp_page(all, total)
url: http://127.0.0.1/user/list
1、set
file: module/redis.py
from app.module.base import BaseController
class RedisController(BaseController):
def mem_set(self):
try:
key = self.get('key')
val = self.get('val')
self.redis.set(name=key, value=val, ex=3600)
return self.ok()
except Exception as e:
return self.error(str(e))
url: http://127.0.0.1/redis/mem-set?key=name&val=jack
2、get
file: module/redis.py
from app.module.base import BaseController
class RedisController(BaseController):
def mem_get(self):
try:
key = self.get('key')
val = self.redis.get(key)
if isinstance(val, bytes):
return self.ok(val.decode('UTF-8'))
else:
return self.error()
except Exception as e:
return self.error(str(e))
url: http://127.0.0.1/redis/mem-get?key=name
1、set
file: module/cache.py
from app.module.base import BaseController
class CacheController(BaseController):
def mem_set(self):
key = self.get('key')
val = self.get('val')
if self.cache.set(key, val):
return self.ok()
else:
return self.error()
url: http://127.0.0.1/cache/mem-set?key=name&val=lucy
2、get
file: module/cache.py
from app.module.base import BaseController
class CacheController(BaseController):
def mem_get(self):
key = self.get('key')
val = self.cache.get(key)
if val:
return self.ok(val)
else:
return self.error()
url: http://127.0.0.1/cache/mem-get?key=name
1、local
file: module/file.py
from app.module.base import BaseController
from app.helper.file import File
class FileController(BaseController):
def upload(self):
file = self.file('file')
f = File()
path = f.upload(file)
resp = dict()
resp['path'] = path
return self.ok(resp)
url: http://127.0.0.1/file/upload
{
"code": 0,
"data": {
"path": "static/upload/txt/9e/4e/9b/77/9e4e9b7754f8d4a26fc93663d2dae4d6.txt"
}
}
2、oss
file: module/oss.py
from app.module.base import BaseController
from app.helper.oss import Oss
class OssController(BaseController):
def upload(self):
file = self.file('file')
oss = Oss()
key = oss.upload(file)
resp = dict()
resp['key'] = key
resp['url'] = oss.url(key)
resp['sign_url'] = oss.sign_url(key=key, expires=3600)
return self.ok(resp)
url: http://127.0.0.1/oss/upload
file: module/html.py
from app.module.base import BaseController
class HtmlController(BaseController):
def index(self):
tv = dict()
tv['title'] = 'hello world!'
return self.render('html/index.html', tv)
url: http://127.0.0.1/html/index
1、builtin command: table
file: console/table.py
show all tables
python console.py table/show
create all tables
python console.py table/build
reflect all tables
python console.py table/model
2、builtin command: secret
file: console/secret.py
generate a random secret key
python console.py secret/key
3、user defined command: test
file: console/test.py
from app.console.base import BaseCommand
class TestCommand(BaseCommand):
def hello(self):
print('hello')
run test command
python console.py test/hello
1、app log
file: log/app.log
2022-07-04 22:12:13 INFO [base.py:28]: flask app init
2022-07-04 22:12:13 INFO [connection_pool.py:36]: put connection into pool
2022-07-04 22:12:13 INFO [_internal.py:224]: * Running on http://127.0.0.1:80 (Press CTRL+C to quit)
2、console log
file: log/console.log
2022-07-04 22:14:33 WARNING [table.py:27]: table user exists
log formatter
file: helper/functions.py
def create_log(log_file):
dictConfig({
'version': 1,
'formatters': {
'default': {
'format': '%(asctime)s %(levelname)s [%(filename)s:%(lineno)s]: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
}
},
'handlers': {
'wsgi': {
'class': 'logging.StreamHandler',
'stream': 'ext://flask.logging.wsgi_errors_stream',
'formatter': 'default'
},
'message': {
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'default',
'filename': log_file,
'maxBytes': 1024 * 1024,
'backupCount': 10,
'encoding': 'utf-8'
}
},
'root': {
'level': 'INFO',
'handlers': ['wsgi', 'message']
}
})
file: helper/decorator.py
1、time_cost
def time_cost(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
resp = func(*args, **kwargs)
end = time.time()
cost = round(end - start, 3)
logging.info(f'{request.path} time cost={cost}s')
return resp
return wrapper
2、retry
def retry(num=1, seconds=0):
def outer(func):
@functools.wraps(func)
def inner(*args, **kwargs):
for i in range(0, num):
try:
resp = func(*args, **kwargs)
if resp['code'] == 0:
return resp
if seconds > 0:
time.sleep(seconds)
except Exception as e:
logging.error(str(e))
return error(f'retry {num} times and failed')
return inner
return outer
3、check_login
def check_login(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
userid = session.get('userid')
if not userid:
return error('login is required', 401)
else:
return func(*args, **kwargs)
return wrapper
4、check_token
def check_token(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
token = request.headers.get('Token')
if not token:
return error('token is missing', 401)
if not app.config['redis'].get(token):
return error('token is expired', 401)
else:
return func(*args, **kwargs)
return wrapper
1、login by session
file: module/session/user.py
from app.module.base import BaseController
from flask import session
from app.models.user import User
from app.helper.decorator import check_login
class UserController(BaseController):
def login(self):
username = self.post('username')
password = self.post('password')
user = User.find().where(username=username).one()
if not user:
return self.error('user not exists')
if user.password != password:
return self.error('wrong password')
session['userid'] = user.id
return self.ok('login success')
2、logout by session
file: module/session/user.py
from app.module.base import BaseController
from flask import session
from app.models.user import User
from app.helper.decorator import check_login
class UserController(BaseController):
@check_login
def logout(self):
session.pop('userid')
return self.ok('logout success')
3、login by token
file: module/token/user.py
from app.module.base import BaseController
from app.module.user import User
from app.helper.decorator import check_token
class UserController(BaseController):
def login(self):
username = self.post('username')
password = self.post('password')
user = User.find().where(username=username).one()
if not user:
return self.error('user not exists')
if user.password != password:
return self.error('wrong password')
token = user.login()
resp = dict()
resp['token'] = token
return self.ok(resp)
4、logout by token
file: module/token/user.py
from app.module.base import BaseController
from app.module.user import User
from app.helper.decorator import check_token
class UserController(BaseController):
@check_token
def logout(self):
self.user.logout()
return self.ok('logout success')
cd your-project
hallo install
./install.sh
start project
./supervisor/start.sh
stop project
./supervisor/stop.sh
restart project
./supervisor/restart.sh
shutdown project
./supervisor/shutdown.sh
Hallo is released under the MIT License. See LICENSE for more information.