📄 sqlobject.py
字号:
## Copyright (c) 2006, 2007 Canonical## Written by Gustavo Niemeyer <gustavo@niemeyer.net>## This file is part of Storm Object Relational Mapper.## Storm is free software; you can redistribute it and/or modify# it under the terms of the GNU Lesser General Public License as# published by the Free Software Foundation; either version 2.1 of# the License, or (at your option) any later version.## Storm is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU Lesser General Public License for more details.## You should have received a copy of the GNU Lesser General Public License# along with this program. If not, see <http://www.gnu.org/licenses/>.#"""A SQLObject emulation layer for Storm.L{SQLObjectBase} is the central point of compatibility."""import refrom storm.properties import ( Unicode, Chars, Int, Bool, Float, DateTime, Date, TimeDelta)from storm.references import Reference, ReferenceSetfrom storm.properties import SimpleProperty, PropertyPublisherMetafrom storm.variables import Variablefrom storm.exceptions import StormErrorfrom storm.info import get_cls_infofrom storm.store import Storefrom storm.base import Stormfrom storm.expr import SQL, SQLRaw, Desc, And, Or, Not, In, Likefrom storm.tz import tzutcfrom storm import Undef__all__ = ["SQLObjectBase", "StringCol", "IntCol", "BoolCol", "FloatCol", "DateCol", "UtcDateTimeCol", "IntervalCol", "ForeignKey", "SQLMultipleJoin", "SQLRelatedJoin", "DESC", "AND", "OR", "NOT", "IN", "LIKE", "SQLConstant", "SQLObjectNotFound", "CONTAINSSTRING"]DESC, AND, OR, NOT, IN, LIKE, SQLConstant = Desc, And, Or, Not, In, Like, SQL_IGNORED = object()class SQLObjectNotFound(StormError): passclass SQLObjectStyle(object): longID = False def idForTable(self, table_name): if self.longID: return self.tableReference(table_name) else: return 'id' def pythonClassToAttr(self, class_name): return self._lowerword(class_name) def instanceAttrToIDAttr(self, attr_name): return attr_name + "ID" def pythonAttrToDBColumn(self, attr_name): return self._mixed_to_under(attr_name) def dbColumnToPythonAttr(self, column_name): return self._under_to_mixed(column_name) def pythonClassToDBTable(self, class_name): return class_name[0].lower()+self._mixed_to_under(class_name[1:]) def dbTableToPythonClass(self, table_name): return table_name[0].upper()+self._under_to_mixed(table_name[1:]) def pythonClassToDBTableReference(self, class_name): return self.tableReference(self.pythonClassToDBTable(class_name)) def tableReference(self, table_name): return table_name+"_id" def _mixed_to_under(self, name, _re=re.compile(r'[A-Z]+')): if name.endswith('ID'): return self._mixed_to_under(name[:-2]+"_id") name = _re.sub(self._mixed_to_under_sub, name) if name.startswith('_'): return name[1:] return name def _mixed_to_under_sub(self, match): m = match.group(0).lower() if len(m) > 1: return '_%s_%s' % (m[:-1], m[-1]) else: return '_%s' % m def _under_to_mixed(self, name, _re=re.compile('_.')): if name.endswith('_id'): return self._under_to_mixed(name[:-3] + "ID") return _re.sub(self._under_to_mixed_sub, name) def _under_to_mixed_sub(self, match): return match.group(0)[1].upper() @staticmethod def _capword(s): return s[0].upper() + s[1:] @staticmethod def _lowerword(s): return s[0].lower() + s[1:]class SQLObjectMeta(PropertyPublisherMeta): @staticmethod def _get_attr(attr, bases, dict): value = dict.get(attr) if value is None: for base in bases: value = getattr(base, attr, None) if value is not None: break return value def __new__(cls, name, bases, dict): if Storm in bases or SQLObjectBase in bases: # Do not parse abstract base classes. return type.__new__(cls, name, bases, dict) style = cls._get_attr("_style", bases, dict) if style is None: dict["_style"] = style = SQLObjectStyle() table_name = cls._get_attr("_table", bases, dict) if table_name is None: table_name = style.pythonClassToDBTable(name) id_name = cls._get_attr("_idName", bases, dict) if id_name is None: id_name = style.idForTable(table_name) # Handle this later to call _parse_orderBy() on the created class. default_order = cls._get_attr("_defaultOrder", bases, dict) dict["__storm_table__"] = table_name attr_to_prop = {} for attr, prop in dict.items(): attr_to_prop[attr] = attr if isinstance(prop, ForeignKey): db_name = prop.kwargs.get("dbName", attr) local_prop_name = style.instanceAttrToIDAttr(attr) dict[local_prop_name] = local_prop = Int(db_name) dict[attr] = Reference(local_prop, "%s.<primary key>" % prop.foreignKey) attr_to_prop[attr] = local_prop_name elif isinstance(prop, PropertyAdapter): db_name = prop.dbName or attr method_name = prop.alternateMethodName if method_name is None and prop.alternateID: method_name = "by" + db_name[0].upper() + db_name[1:] if method_name is not None: def func(cls, key, attr=attr): store = cls._get_store() obj = store.find(cls, getattr(cls, attr) == key).one() if obj is None: raise SQLObjectNotFound return obj func.func_name = method_name dict[method_name] = classmethod(func) id_type = dict.get("_idType", int) id_cls = {int: Int, str: Chars, unicode: AutoUnicode}[id_type] dict[id_name] = id_cls(primary=True) # Notice that obj is the class since this is the metaclass. obj = super(SQLObjectMeta, cls).__new__(cls, name, bases, dict) property_registry = obj._storm_property_registry property_registry.add_property(obj, getattr(obj, id_name), "<primary key>") for fake_name, real_name in attr_to_prop.items(): prop = getattr(obj, real_name) if fake_name != real_name: property_registry.add_property(obj, prop, fake_name) attr_to_prop[fake_name] = prop obj._attr_to_prop = attr_to_prop if default_order is not None: cls_info = get_cls_info(obj) cls_info.default_order = obj._parse_orderBy(default_order) return objclass DotQ(object): """A descriptor that mimics the SQLObject 'Table.q' syntax""" def __get__(self, obj, cls=None): return BoundDotQ(cls)class BoundDotQ(object): def __init__(self, cls): self._cls = cls def __getattr__(self, attr): if attr.startswith('__'): raise AttributeError(attr) elif attr == 'id': cls_info = get_cls_info(self._cls) return cls_info.primary_key[0] else: return getattr(self._cls, attr)class SQLObjectBase(Storm): """The root class of all SQLObject-emulating classes in your application. The general strategy for using Storm's SQLObject emulation layer is to create an application-specific subclass of SQLObjectBase (probably named "SQLObject") that provides an implementation of _get_store to return an instance of L{storm.store.Store}. It may even be implemented as returning a global L{Store} instance. Then all database classes should subclass that class. """ __metaclass__ = SQLObjectMeta q = DotQ() def __init__(self, *args, **kwargs): self._get_store().add(self) self._create(None, **kwargs) def __storm_loaded__(self): self._init(None) def _init(self, id, *args, **kwargs): pass def _create(self, _id_, **kwargs): self.set(**kwargs) self._init(None) def set(self, **kwargs): for attr, value in kwargs.iteritems(): setattr(self, attr, value) def destroySelf(self): Store.of(self).remove(self) @staticmethod
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -