Database API support SQLAlchemy

master
Douglas Blank 2018-07-24 12:55:52 -04:00
rodzic 2c6cb1e48d
commit 16d646c510
3 zmienionych plików z 183 dodań i 3 usunięć

Wyświetl plik

@ -3,3 +3,4 @@ from .base import Database, Table
from .listdb import ListDatabase from .listdb import ListDatabase
from .mongodb import MongoDatabase from .mongodb import MongoDatabase
from .redisdb import RedisDatabase from .redisdb import RedisDatabase
from .sqldb import SQLDatabase

Wyświetl plik

@ -256,14 +256,31 @@ class ListTable(Table):
[] []
>>> table.find({"d": 4}) # doctest: +ELLIPSIS >>> table.find({"d": 4}) # doctest: +ELLIPSIS
[{'c': 3, 'd': 4, '_id': ObjectId('...')}] [{'c': 3, 'd': 4, '_id': ObjectId('...')}]
>>> table.remove({"d": 4})
>>> table.find({"d": 4})
[]
>>> table.find({"b": 2}) # doctest: +ELLIPSIS
[{'a': 1, 'b': 2, '_id': ObjectId('...')}]
""" """
if query: if query:
items = [doc for doc in self.data if self.match(doc, query)] items = [(i,doc) for (i,doc) in enumerate(self.data) if self.match(doc, query)]
# delete them for i,doc in items:
del self.data[i]
else: else:
self.data = [] self.data.clear()
def find_one(self, query): def find_one(self, query):
"""
>>> table = ListTable()
>>> table.insert_one({"a": 1, "b": 2})
>>> table.insert_one({"a": 3, "b": 4})
>>> table.find_one({"b": 2}) # doctest: +ELLIPSIS
{'a': 1, 'b': 2, '_id': ObjectId('...')}
>>> table.find_one({"b": 3})
>>> table.find_one({"b": 4}) # doctest: +ELLIPSIS
{'a': 3, 'b': 4, '_id': ObjectId('...')}
"""
results = [doc for doc in self.data if self.match(doc, query)] results = [doc for doc in self.data if self.match(doc, query)]
if results: if results:
return results[0] return results[0]

Wyświetl plik

@ -0,0 +1,162 @@
import json
from ..bson import ObjectId
from .base import Database
from .listdb import ListTable
class JSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, ObjectId):
return {"$oid": str(o)}
return super().default(o)
class JSONDecoder(json.JSONDecoder):
def __init__(self, *args, **kwargs):
super().__init__(object_hook=self.object_hook, *args, **kwargs)
def object_hook(self, obj):
if '$oid' not in obj:
return obj
return ObjectId(obj['$oid'])
class SQLList():
def __init__(self, database, name):
self.database = database
self.name = name
def __getitem__(self, item):
if isinstance(item, int):
result = self.database.execute(
"""SELECT blob_data FROM %s WHERE rowid = :rowid"""
% (self.name), {"rowid": item})
item = result.fetchone()
if item:
item = json.loads(item[0], cls=JSONDecoder)
elif isinstance(item, slice):
items = [item in islice(self, item.start, item.stop, item.step)]
return items
if item:
return item
else:
raise IndexError("list index out of range")
def __setitem__(self, item, value):
s = json.dumps(value, cls=JSONEncoder)
# first see if it exists:
try:
old_item = self[item]
except:
old_item = None
if old_item:
# update it
try:
self.database.execute(
"""UPDATE %s SET blob_data = :s WHERE rowid = :rowid;"""
% (self.name), {"s": s, "rowid": item})
self.database.commit()
except:
self.database.rollback()
raise
else:
# insert it
oid = str(value["_id"])
try:
self.database.execute(
"""INSERT INTO %s (blob_data, oid, rowid) VALUES (:s, :oid, :rowid);"""
% (self.name), {"s": s, "rowid": item, "oid": oid})
self.database.commit()
except:
self.database.rollback()
raise
def __delitem__(self, key):
try:
self.database.execute(
"""DELETE FROM %s WHERE rowid = :rowid;"""
% (self.name), {"rowid": key})
self.database.execute(
"""UPDATE %s SET rowid = (rowid - 1) WHERE rowid > :rowid;"""
% self.name, {"rowid": key})
self.database.commit()
except:
self.database.rollback()
raise
def clear(self):
try:
self.database.execute("DELETE from %s;" % self.name)
self.database.commit()
except:
self.database.rollback()
raise
def append(self, item):
pos = len(self)
self[pos] = item
def __len__(self):
result = self.database.execute("SELECT count(1) FROM %s" % self.name)
row = result.fetchone()
return row[0]
class SQLTable(ListTable):
def __init__(self, database, name):
super().__init__(database, name)
if not self.table_exists(name):
self.build_table(name)
self.data = SQLList(database, name)
def table_exists(self, table):
result = self.database.execute("""SELECT COUNT(*)
FROM sqlite_master
WHERE type='table' AND name='%s';""" % table)
return result.fetchone()[0] != 0
def build_table(self, name):
try:
self.database.execute(
"""CREATE TABLE %s (
rowid INTEGER PRIMARY KEY ASC,
oid CHAR(24),
blob_data BLOB
)""" % name)
self.database.commit()
except:
self.database.rollback()
raise
def sort(self, sort_key, sort_order):
# sort_key = "_id"
# sort_order = 1 or -1
## Always use ListTable here:
return ListTable(data=sorted(
self.data,
key=lambda row: self.get_item_in_dict(row, sort_key),
reverse=(sort_order == -1)))
class SQLDatabase(Database):
Table = SQLTable
def __init__(self, *args, **kwargs):
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
#from sqlalchemy.pool import StaticPool
#from sqlalchemy.pool import QueuePool
super().__init__()
self.engine = create_engine(*args, **kwargs)
# poolclass=QueuePool,
# convert_unicode=True,
# connect_args={'check_same_thread':False},
# poolclass=StaticPool,
self.session = scoped_session(sessionmaker(bind=self.engine))
def commit(self):
self.session.commit()
def rollback(self):
self.session.rollback()
def execute(self, *args, **kwargs):
return self.session.execute(*args, **kwargs)