1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
import sqlalchemy as sa
from sqlalchemy.orm.attributes import InstrumentedAttribute
from sqlalchemy.util.langhelpers import symbol
from .utils import str_coercible
@str_coercible
class Path(object):
def __init__(self, path, separator='.'):
if isinstance(path, Path):
self.path = path.path
else:
self.path = path
self.separator = separator
@property
def parts(self):
return self.path.split(self.separator)
def __iter__(self):
for part in self.parts:
yield part
def __len__(self):
return len(self.parts)
def __repr__(self):
return "%s('%s')" % (self.__class__.__name__, self.path)
def index(self, element):
return self.parts.index(element)
def __getitem__(self, slice):
result = self.parts[slice]
if isinstance(result, list):
return self.__class__(
self.separator.join(result),
separator=self.separator
)
return result
def __eq__(self, other):
return self.path == other.path and self.separator == other.separator
def __ne__(self, other):
return not (self == other)
def __unicode__(self):
return self.path
def get_attr(mixed, attr):
if isinstance(mixed, InstrumentedAttribute):
return getattr(
mixed.property.mapper.class_,
attr
)
else:
return getattr(mixed, attr)
@str_coercible
class AttrPath(object):
def __init__(self, class_, path):
self.class_ = class_
self.path = Path(path)
self.parts = []
last_attr = class_
for value in self.path:
last_attr = get_attr(last_attr, value)
self.parts.append(last_attr)
def __iter__(self):
for part in self.parts:
yield part
def __invert__(self):
def get_backref(part):
prop = part.property
backref = prop.backref or prop.back_populates
if backref is None:
raise Exception(
"Invert failed because property '%s' of class "
"%s has no backref." % (
prop.key,
prop.parent.class_.__name__
)
)
if isinstance(backref, tuple):
return backref[0]
else:
return backref
if isinstance(self.parts[-1].property, sa.orm.ColumnProperty):
class_ = self.parts[-1].class_
else:
class_ = self.parts[-1].mapper.class_
return self.__class__(
class_,
'.'.join(map(get_backref, reversed(self.parts)))
)
def index(self, element):
for index, el in enumerate(self.parts):
if el is element:
return index
@property
def direction(self):
symbols = [part.property.direction for part in self.parts]
if symbol('MANYTOMANY') in symbols:
return symbol('MANYTOMANY')
elif symbol('MANYTOONE') in symbols and symbol('ONETOMANY') in symbols:
return symbol('MANYTOMANY')
return symbols[0]
@property
def uselist(self):
return any(part.property.uselist for part in self.parts)
def __getitem__(self, slice):
result = self.parts[slice]
if isinstance(result, list) and result:
if result[0] is self.parts[0]:
class_ = self.class_
else:
class_ = result[0].parent.class_
return self.__class__(
class_,
self.path[slice]
)
else:
return result
def __len__(self):
return len(self.path)
def __repr__(self):
return "%s(%s, %r)" % (
self.__class__.__name__,
self.class_.__name__,
self.path.path
)
def __eq__(self, other):
return self.path == other.path and self.class_ == other.class_
def __ne__(self, other):
return not (self == other)
def __unicode__(self):
return str(self.path)
|