Created
April 17, 2017 13:19
-
-
Save lueadi/9390fe8c2ab648f2c6cc59444bc771df to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="PublishConfigData"> | |
<serverData> | |
<paths name="nickyu"> | |
<serverdata> | |
<mappings> | |
<mapping local="$PROJECT_DIR$" web="/" /> | |
</mappings> | |
</serverdata> | |
</paths> | |
</serverData> | |
</component> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="Encoding"> | |
<file url="PROJECT" charset="UTF-8" /> | |
</component> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="ProjectLevelVcsManager" settingsEditedManually="false"> | |
<OptionsSetting value="true" id="Add" /> | |
<OptionsSetting value="true" id="Remove" /> | |
<OptionsSetting value="true" id="Checkout" /> | |
<OptionsSetting value="true" id="Update" /> | |
<OptionsSetting value="true" id="Status" /> | |
<OptionsSetting value="true" id="Edit" /> | |
<ConfirmationsSetting value="0" id="Add" /> | |
<ConfirmationsSetting value="0" id="Remove" /> | |
</component> | |
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.5.2 (C:\Users\Administrator\AppData\Local\Programs\Python\Python35-32\python.exe)" project-jdk-type="Python SDK" /> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="ProjectModuleManager"> | |
<modules> | |
<module fileurl="file://$PROJECT_DIR$/.idea/web-blog-awesome.iml" filepath="$PROJECT_DIR$/.idea/web-blog-awesome.iml" /> | |
</modules> | |
</component> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="VagrantProjectSettings"> | |
<option name="instanceFolder" value="" /> | |
<option name="provider" value="" /> | |
</component> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<module type="PYTHON_MODULE" version="4"> | |
<component name="NewModuleRootManager"> | |
<content url="file://$MODULE_DIR$" /> | |
<orderEntry type="inheritedJdk" /> | |
<orderEntry type="sourceFolder" forTests="false" /> | |
</component> | |
<component name="TestRunnerService"> | |
<option name="PROJECT_TEST_RUNNER" value="Unittests" /> | |
</component> | |
</module> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
__author__ = 'Michael Liao' | |
''' | |
async web application. | |
''' | |
import logging; logging.basicConfig(level=logging.INFO) | |
import asyncio, os, json, time | |
from datetime import datetime | |
from aiohttp import web | |
def index(request): | |
return web.Response(body='<h1>Awesome</h1>') | |
@asyncio.coroutine | |
def init(loop): | |
app = web.Application(loop=loop) | |
app.router.add_route('GET', '/', index) | |
srv = yield from loop.create_server(app.make_handler(), '127.0.0.1','9000') | |
logging.info('server started at http://127.0.0.1:9000...') | |
return srv | |
loop = asyncio.get_event_loop() | |
loop.run_until_complete(init(loop)) | |
loop.run_forever() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
__author__ = 'Michael Liao' | |
import asyncio, logging | |
import aiomysql | |
def log(sql, args=()): | |
logging.info('SQL: %s' % sql) | |
async def create_pool(loop, **kw): | |
logging.info('create database connection pool...') | |
global __pool | |
__pool = await aiomysql.create_pool( | |
host=kw.get('host', 'localhost'), | |
port=kw.get('port', 3306), | |
user=kw['user'], | |
password=kw['password'], | |
db=kw['db'], | |
charset=kw.get('charset', 'utf8'), | |
autocommit=kw.get('autocommit', True), | |
maxsize=kw.get('maxsize', 10), | |
minsize=kw.get('minsize', 1), | |
loop=loop | |
) | |
async def select(sql, args, size=None): | |
log(sql, args) | |
global __pool | |
async with __pool.get() as conn: | |
async with conn.cursor(aiomysql.DictCursor) as cur: | |
await cur.execute(sql.replace('?', '%s'), args or ()) | |
if size: | |
rs = await cur.fetchmany(size) | |
else: | |
rs = await cur.fetchall() | |
logging.info('rows returned: %s' % len(rs)) | |
return rs | |
async def execute(sql, args, autocommit=True): | |
log(sql) | |
async with __pool.get() as conn: | |
if not autocommit: | |
await conn.begin() | |
try: | |
async with conn.cursor(aiomysql.DictCursor) as cur: | |
await cur.execute(sql.replace('?', '%s'), args) | |
affected = cur.rowcount | |
if not autocommit: | |
await conn.commit() | |
except BaseException as e: | |
if not autocommit: | |
await conn.rollback() | |
raise | |
return affected | |
def create_args_string(num): | |
L = [] | |
for n in range(num): | |
L.append('?') | |
return ', '.join(L) | |
class Field(object): | |
def __init__(self, name, column_type, primary_key, default): | |
self.name = name | |
self.column_type = column_type | |
self.primary_key = primary_key | |
self.default = default | |
def __str__(self): | |
return '<%s, %s:%s>' % (self.__class__.__name__, self.column_type, self.name) | |
class StringField(Field): | |
def __init__(self, name=None, primary_key=False, default=None, ddl='varchar(100)'): | |
super().__init__(name, ddl, primary_key, default) | |
class BooleanField(Field): | |
def __init__(self, name=None, default=False): | |
super().__init__(name, 'boolean', False, default) | |
class IntegerField(Field): | |
def __init__(self, name=None, primary_key=False, default=0): | |
super().__init__(name, 'bigint', primary_key, default) | |
class FloatField(Field): | |
def __init__(self, name=None, primary_key=False, default=0.0): | |
super().__init__(name, 'real', primary_key, default) | |
class TextField(Field): | |
def __init__(self, name=None, default=None): | |
super().__init__(name, 'text', False, default) | |
class ModelMetaclass(type): | |
def __new__(cls, name, bases, attrs): | |
if name=='Model': | |
return type.__new__(cls, name, bases, attrs) | |
tableName = attrs.get('__table__', None) or name | |
logging.info('found model: %s (table: %s)' % (name, tableName)) | |
mappings = dict() | |
fields = [] | |
primaryKey = None | |
for k, v in attrs.items(): | |
if isinstance(v, Field): | |
logging.info(' found mapping: %s ==> %s' % (k, v)) | |
mappings[k] = v | |
if v.primary_key: | |
# 找到主键: | |
if primaryKey: | |
raise StandardError('Duplicate primary key for field: %s' % k) | |
primaryKey = k | |
else: | |
fields.append(k) | |
if not primaryKey: | |
raise StandardError('Primary key not found.') | |
for k in mappings.keys(): | |
attrs.pop(k) | |
escaped_fields = list(map(lambda f: '`%s`' % f, fields)) | |
attrs['__mappings__'] = mappings # 保存属性和列的映射关系 | |
attrs['__table__'] = tableName | |
attrs['__primary_key__'] = primaryKey # 主键属性名 | |
attrs['__fields__'] = fields # 除主键外的属性名 | |
attrs['__select__'] = 'select `%s`, %s from `%s`' % (primaryKey, ', '.join(escaped_fields), tableName) | |
attrs['__insert__'] = 'insert into `%s` (%s, `%s`) values (%s)' % (tableName, ', '.join(escaped_fields), primaryKey, create_args_string(len(escaped_fields) + 1)) | |
attrs['__update__'] = 'update `%s` set %s where `%s`=?' % (tableName, ', '.join(map(lambda f: '`%s`=?' % (mappings.get(f).name or f), fields)), primaryKey) | |
attrs['__delete__'] = 'delete from `%s` where `%s`=?' % (tableName, primaryKey) | |
return type.__new__(cls, name, bases, attrs) | |
class Model(dict, metaclass=ModelMetaclass): | |
def __init__(self, **kw): | |
super(Model, self).__init__(**kw) | |
def __getattr__(self, key): | |
try: | |
return self[key] | |
except KeyError: | |
raise AttributeError(r"'Model' object has no attribute '%s'" % key) | |
def __setattr__(self, key, value): | |
self[key] = value | |
def getValue(self, key): | |
return getattr(self, key, None) | |
def getValueOrDefault(self, key): | |
value = getattr(self, key, None) | |
if value is None: | |
field = self.__mappings__[key] | |
if field.default is not None: | |
value = field.default() if callable(field.default) else field.default | |
logging.debug('using default value for %s: %s' % (key, str(value))) | |
setattr(self, key, value) | |
return value | |
@classmethod | |
async def findAll(cls, where=None, args=None, **kw): | |
' find objects by where clause. ' | |
sql = [cls.__select__] | |
if where: | |
sql.append('where') | |
sql.append(where) | |
if args is None: | |
args = [] | |
orderBy = kw.get('orderBy', None) | |
if orderBy: | |
sql.append('order by') | |
sql.append(orderBy) | |
limit = kw.get('limit', None) | |
if limit is not None: | |
sql.append('limit') | |
if isinstance(limit, int): | |
sql.append('?') | |
args.append(limit) | |
elif isinstance(limit, tuple) and len(limit) == 2: | |
sql.append('?, ?') | |
args.extend(limit) | |
else: | |
raise ValueError('Invalid limit value: %s' % str(limit)) | |
rs = await select(' '.join(sql), args) | |
return [cls(**r) for r in rs] | |
@classmethod | |
async def findNumber(cls, selectField, where=None, args=None): | |
' find number by select and where. ' | |
sql = ['select %s _num_ from `%s`' % (selectField, cls.__table__)] | |
if where: | |
sql.append('where') | |
sql.append(where) | |
rs = await select(' '.join(sql), args, 1) | |
if len(rs) == 0: | |
return None | |
return rs[0]['_num_'] | |
@classmethod | |
async def find(cls, pk): | |
' find object by primary key. ' | |
rs = await select('%s where `%s`=?' % (cls.__select__, cls.__primary_key__), [pk], 1) | |
if len(rs) == 0: | |
return None | |
return cls(**rs[0]) | |
async def save(self): | |
args = list(map(self.getValueOrDefault, self.__fields__)) | |
args.append(self.getValueOrDefault(self.__primary_key__)) | |
rows = await execute(self.__insert__, args) | |
if rows != 1: | |
logging.warn('failed to insert record: affected rows: %s' % rows) | |
async def update(self): | |
args = list(map(self.getValue, self.__fields__)) | |
args.append(self.getValue(self.__primary_key__)) | |
rows = await execute(self.__update__, args) | |
if rows != 1: | |
logging.warn('failed to update by primary key: affected rows: %s' % rows) | |
async def remove(self): | |
args = [self.getValue(self.__primary_key__)] | |
rows = await execute(self.__delete__, args) | |
if rows != 1: | |
logging.warn('failed to remove by primary key: affected rows: %s' % rows) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment