3 Commits

View File

@@ -8,7 +8,7 @@ PYTHON3 = True
if sys.version_info[0] < 3: if sys.version_info[0] < 3:
PYTHON3 = False PYTHON3 = False
VERSION = "0.0.18" VERSION = "0.0.15"
def is_map(obj): def is_map(obj):
@@ -23,12 +23,13 @@ def ensureUtf(s):
if isinstance(s, str): if isinstance(s, str):
return s return s
else: else:
return s.encode("utf8", "ignore") return s.encode('utf8', 'ignore')
except: except:
return str(s) return str(s)
class classproperty(object): class classproperty(object):
def __init__(self, getter): def __init__(self, getter):
self.getter = getter self.getter = getter
@@ -37,10 +38,10 @@ class classproperty(object):
class QMixin(object): class QMixin(object):
AND = "AND" AND = 'AND'
OR = "OR" OR = 'OR'
NOT = "NOT" NOT = 'NOT'
UNION = "UNION" UNION = 'UNION'
def _combine(self, other, conn): def _combine(self, other, conn):
return Operator(conn, self, other) return Operator(conn, self, other)
@@ -53,21 +54,18 @@ class QMixin(object):
def __and__(self, other): def __and__(self, other):
return self._combine(other, self.AND) return self._combine(other, self.AND)
def __invert__( def __invert__(self,):
self,
):
return Operator(self.NOT, self) return Operator(self.NOT, self)
class Operator(QMixin): class Operator(QMixin):
def __init__(self, op=None, left=None, right=None): def __init__(self, op=None, left=None, right=None):
self.op = op self.op = op
self.left = left self.left = left
self.right = right self.right = right
def __repr__( def __repr__(self,):
self,
):
if self.left and self.right: if self.left and self.right:
return "(%s %s %s)" % (self.left, self.op, self.right) return "(%s %s %s)" % (self.left, self.op, self.right)
@@ -88,12 +86,11 @@ class Operator(QMixin):
class F(object): class F(object):
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
def __repr__( def __repr__(self,):
self,
):
return "%s" % self.value return "%s" % self.value
__str__ = __repr__ __str__ = __repr__
@@ -108,28 +105,16 @@ class Q(QMixin):
_mode = "MYSQL" _mode = "MYSQL"
lookup_types = [ lookup_types = [
"icontains", 'icontains', 'istartswith', 'iendswith',
"istartswith", 'contains', 'startswith', 'endswith',
"iendswith", 'year', 'month', 'day', 'week_day', 'hour', 'minute', 'second',
"contains", 'isnull', 'in']
"startswith",
"endswith",
"year",
"month",
"day",
"week_day",
"hour",
"minute",
"second",
"isnull",
"in",
]
op_map = { op_map = {
"lte": "<=", 'lte': '<=',
"gte": ">=", 'gte': '>=',
"lt": "<", 'lt': '<',
"gt": ">", 'gt': '>',
} }
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -137,9 +122,7 @@ class Q(QMixin):
for arg in args: for arg in args:
self.conditions[arg] = None self.conditions[arg] = None
def __repr__( def __repr__(self,):
self,
):
return self._compile() return self._compile()
def __bool__(self): def __bool__(self):
@@ -149,13 +132,13 @@ class Q(QMixin):
@property @property
def date_format(self): def date_format(self):
if self._mode == "SQL_SERVER": if self._mode == 'SQL_SERVER':
return "%Y-%d-%m" return "%Y-%d-%m"
return "%Y-%m-%d" return "%Y-%m-%d"
@property @property
def datetime_format(self): def datetime_format(self):
if self._mode == "SQL_SERVER": if self._mode == 'SQL_SERVER':
return "%Y-%d-%m %H:%M:%S" return "%Y-%d-%m %H:%M:%S"
return "%Y-%m-%d %H:%M:%S" return "%Y-%m-%d %H:%M:%S"
@@ -172,11 +155,7 @@ class Q(QMixin):
if isinstance(value, list) or isinstance(value, set) or is_map(value): if isinstance(value, list) or isinstance(value, set) or is_map(value):
return ", ".join([self._get_value(item) for item in value]) return ", ".join([self._get_value(item) for item in value])
if ( if isinstance(value, F) or isinstance(value, QMixin) or isinstance(value, SQLQuery):
isinstance(value, F)
or isinstance(value, QMixin)
or isinstance(value, SQLQuery)
):
return ensureUtf(value) return ensureUtf(value)
return "'%s'" % value return "'%s'" % value
@@ -184,7 +163,7 @@ class Q(QMixin):
def _process(self, compose_column, value): def _process(self, compose_column, value):
arr = compose_column.split("__") arr = compose_column.split("__")
column = arr.pop(0) column = arr.pop(0)
if column == "": if column == '':
column += "__" + arr.pop(0) column += "__" + arr.pop(0)
try: try:
@@ -214,34 +193,28 @@ class Q(QMixin):
if lookup == "in": if lookup == "in":
return "{0} in ({1})".format(column, self._get_value(value)) return "{0} in ({1})".format(column, self._get_value(value))
if lookup == "isnull": if lookup == 'isnull':
op = "" op = ""
if not value: if not value:
op = "NOT " op = "NOT "
return "{0} is {1}NULL".format(column, op) return "{0} is {1}NULL".format(column, op)
if lookup in ["year", "month", "day", "hour", "minute", "second"]: if lookup in ['year', 'month', 'day', 'hour', 'minute', 'second']:
if arr: if arr:
column = "DATEPART({0}, {1})__{2}".format( column = "DATEPART({0}, {1})__{2}".format(lookup, column, arr.pop(0))
lookup, column, arr.pop(0)
)
return self._process(column, value) return self._process(column, value)
else: else:
return "DATEPART({0}, {1})={2}".format(lookup, column, value) return "DATEPART({0}, {1})={2}".format(lookup, column, value)
if lookup in self.op_map.keys(): if lookup in self.op_map.keys():
return "{0}{1}{2}".format( return "{0}{1}{2}".format(column, self.op_map[lookup], self._get_value(value))
column, self.op_map[lookup], self._get_value(value)
)
if value is not None: if value is not None:
return "{0}{1}{2}".format(column, "=", self._get_value(value)) return "{0}{1}{2}".format(column, "=", self._get_value(value))
return column return column
def _compile( def _compile(self,):
self,
):
filters = [] filters = []
for k, v in self.conditions.items(): for k, v in self.conditions.items():
filters.append(self._process(k, v)) filters.append(self._process(k, v))
@@ -253,6 +226,7 @@ class Q(QMixin):
class SQLQuery(object): class SQLQuery(object):
def __init__(self, table=None, sql_mode="MYSQL", sql=None, **kwargs): def __init__(self, table=None, sql_mode="MYSQL", sql=None, **kwargs):
self.kwargs = kwargs self.kwargs = kwargs
self._table = table self._table = table
@@ -267,19 +241,11 @@ class SQLQuery(object):
self._limits = None self._limits = None
self._sql = sql self._sql = sql
self._nolock = False self._nolock = False
self._distinct = False
def has_filters(self): def has_filters(self,):
return ( return self._order_by or self._group_by or self._joins\
self._order_by or self._filters or self._excludes or self._extra \
or self._group_by or self._limits or self._values != ['*']
or self._joins
or self._filters
or self._excludes
or self._extra
or self._limits
or self._values != ["*"]
)
def _q(self, *args, **kwargs): def _q(self, *args, **kwargs):
conds = Q() conds = Q()
@@ -293,7 +259,7 @@ class SQLQuery(object):
_conds._mode = self.sql_mode _conds._mode = self.sql_mode
return conds & _conds return conds & _conds
def _clone(self): def _clone(self,):
return copy.deepcopy(self) return copy.deepcopy(self)
def values(self, *args): def values(self, *args):
@@ -311,11 +277,6 @@ class SQLQuery(object):
clone._filters &= self._q(*args, **kwargs) clone._filters &= self._q(*args, **kwargs)
return clone return clone
def distinct(self, enabled=True):
clone = self._clone()
clone._distinct = enabled
return clone
def exclude(self, *args, **kwargs): def exclude(self, *args, **kwargs):
clone = self._clone() clone = self._clone()
clone._excludes &= self._q(*args, **kwargs) clone._excludes &= self._q(*args, **kwargs)
@@ -338,9 +299,7 @@ class SQLQuery(object):
clone = self._clone() clone = self._clone()
if on: if on:
on = "ON " + on.format(table=self._table) on = "ON " + on.format(table=self._table)
clone._joins.append( clone._joins.append("{how} {table} {on}".format(how=how, table=table, on=on))
"{how} {table} {on}".format(how=how, table=table, on=on)
)
return clone return clone
def extra(self, extra=None, **kwargs): def extra(self, extra=None, **kwargs):
@@ -352,28 +311,30 @@ class SQLQuery(object):
return clone return clone
def __getitem__(self, slice): def __getitem__(self, slice):
if isinstance(slice, str):
raise IndexError
clone = self._clone() clone = self._clone()
clone._limits = slice clone._limits = slice
return clone return clone
class SQLCompiler(object): class SQLCompiler(object):
def get_columns(self):
def get_columns(self,):
extra_columns = self.get_extra_columns() extra_columns = self.get_extra_columns()
columns = ", ".join(self._values) columns = ", ".join(self._values)
return ", ".join([item for item in [columns, extra_columns] if item]) return ", ".join([item for item in [columns, extra_columns] if item])
def get_extra_columns(self): def get_extra_columns(self,):
return self._extra.get("select", "") return self._extra.get("select", "")
def get_extra_where(self): def get_extra_where(self,):
where = self._extra.get("where", []) where = self._extra.get("where", [])
if where: if where:
return " AND ".join(where) return " AND ".join(where)
def get_table( def get_table(self,):
self,
):
return self._table return self._table
def get_where(self): def get_where(self):
@@ -381,13 +342,9 @@ class SQLCompiler(object):
extra_where = self.get_extra_where() extra_where = self.get_extra_where()
if filters or extra_where: if filters or extra_where:
return "WHERE " + " AND ".join( return "WHERE " + " AND ".join([item for item in [filters, extra_where] if item])
[item for item in [filters, extra_where] if item]
)
def get_order_by( def get_order_by(self,):
self,
):
conds = [] conds = []
for cond in self._order_by: for cond in self._order_by:
order = "" order = ""
@@ -407,22 +364,20 @@ class SQLCompiler(object):
if conds: if conds:
return "ORDER BY " + ", ".join(conds) return "ORDER BY " + ", ".join(conds)
def get_group_by( def get_group_by(self,):
self,
):
if self._group_by: if self._group_by:
return "GROUP BY " + ", ".join(self._group_by) return "GROUP BY " + ", ".join(self._group_by)
def get_joins(self): def get_joins(self,):
if self._joins: if self._joins:
return " ".join(self._joins) return " ".join(self._joins)
def get_nolock(self): def get_nolock(self,):
if self._nolock: if self._nolock:
return " WITH (NOLOCK)" return " WITH (NOLOCK)"
return "" return ""
def get_limits(self): def get_limits(self,):
if self._limits and self.sql_mode != "SQL_SERVER": if self._limits and self.sql_mode != "SQL_SERVER":
offset = self._limits.start offset = self._limits.start
limit = self._limits.stop limit = self._limits.stop
@@ -433,12 +388,7 @@ class SQLCompiler(object):
str += " OFFSET {0}".format(offset) str += " OFFSET {0}".format(offset)
return str return str
def get_distinct(self): def get_top(self,):
if self._distinct:
return " DISTINCT "
return ""
def get_top(self):
if self._limits and self.sql_mode == "SQL_SERVER" and not self._limits.start: if self._limits and self.sql_mode == "SQL_SERVER" and not self._limits.start:
return "TOP {0}".format(self._limits.stop) return "TOP {0}".format(self._limits.stop)
@@ -450,27 +400,15 @@ class SQLCompiler(object):
else: else:
table = self.get_table() table = self.get_table()
sql = [ sql = ["SELECT", self.get_top(), self.get_columns(),
"SELECT", "FROM", table,
self.get_distinct(), self.get_nolock(),
self.get_top(), self.get_joins(), self.get_where(),
self.get_columns(), self.get_group_by(), self.get_order_by(),
"FROM", self.get_limits()]
table,
self.get_nolock(),
self.get_joins(),
self.get_where(),
self.get_group_by(),
self.get_order_by(),
self.get_limits(),
]
if ( if self.sql_mode == "SQL_SERVER" and self._limits and \
self.sql_mode == "SQL_SERVER" self._limits.start is not None and self._limits.stop is not None:
and self._limits
and self._limits.start is not None
and self._limits.stop is not None
):
conds = [] conds = []
if self._limits.start is not None: if self._limits.start is not None:
conds.append("row_number > %s" % self._limits.start) conds.append("row_number > %s" % self._limits.start)
@@ -481,19 +419,37 @@ class SQLCompiler(object):
conds = " AND ".join(conds) conds = " AND ".join(conds)
paginate = "ROW_NUMBER() OVER (%s) as row_number" % self.get_order_by() paginate = "ROW_NUMBER() OVER (%s) as row_number" % self.get_order_by()
return [ return ["SELECT * FROM (", "SELECT", ",".join([paginate, self.get_columns()]),
"SELECT * FROM (", "FROM", table,
"SELECT", self.get_joins(),
",".join([paginate, self.get_columns()]), self.get_where(),
"FROM", self.get_group_by(),
table, self.get_limits(), ") as tbl_paginated WHERE ", conds]
self.get_joins(),
self.get_where(), if self.sql_mode == "ACCESS" and self._limits and \
self.get_group_by(), self._limits.start is not None and self._limits.stop is not None:
self.get_limits(), conds = []
") as tbl_paginated WHERE ",
conds, if self._limits.start is not None:
] conds.append("row_number > %s" % self._limits.start)
if self._limits.stop is not None:
conds.append("row_number <= %s" % self._limits.stop)
conds = " AND ".join(conds)
count = "(select count(*) FROM {table} as t2 WHERE t2.{id} <= {table}.{id}) as row_number".format(table=table,
id=self._order_by[0])
return ["SELECT * FROM (", "SELECT", self.get_columns(), ",",
count,
# "FROM",
# table,
self.get_joins(),
self.get_where(),
self.get_group_by(),
" FROM ",
table,
") WHERE ", conds]
return sql return sql
@@ -505,8 +461,8 @@ class SQLCompiler(object):
__str__ = __repr__ __str__ = __repr__
@property @ property
def sql(self): def sql(self,):
return self.__str__() return self.__str__()
def __or__(self, other): def __or__(self, other):
@@ -518,6 +474,7 @@ class Queryset(SQLCompiler, SQLQuery):
class SQLModel(object): class SQLModel(object):
@classproperty
@ classproperty
def objects(cls): def objects(cls):
return Queryset(cls.table, getattr(cls, "sql_mode", None)) return Queryset(cls.table, getattr(cls, 'sql_mode', None))