support in db for constraints that reference the same column by appending a # followed by some unique string
This commit is contained in:
parent
ba8870823c
commit
31983fbb8e
2 changed files with 32 additions and 25 deletions
|
@ -56,7 +56,11 @@ class TestQueryBuilder(unittest.TestCase):
|
||||||
def test_dot(self):
|
def test_dot(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
constraints_to_sql({'txo.position': 18}),
|
constraints_to_sql({'txo.position': 18}),
|
||||||
('txo.position = :txo_position', {'txo_position': 18})
|
('txo.position = :txo_position0', {'txo_position0': 18})
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
constraints_to_sql({'txo.position#6': 18}),
|
||||||
|
('txo.position = :txo_position6', {'txo_position6': 18})
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_any(self):
|
def test_any(self):
|
||||||
|
@ -67,25 +71,25 @@ class TestQueryBuilder(unittest.TestCase):
|
||||||
'txo.age__lt': 38
|
'txo.age__lt': 38
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
('(txo.age > :ages__any_txo_age__gt OR txo.age < :ages__any_txo_age__lt)', {
|
('(txo.age > :ages__any0_txo_age__gt0 OR txo.age < :ages__any0_txo_age__lt0)', {
|
||||||
'ages__any_txo_age__gt': 18,
|
'ages__any0_txo_age__gt0': 18,
|
||||||
'ages__any_txo_age__lt': 38
|
'ages__any0_txo_age__lt0': 38
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_in(self):
|
def test_in(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
constraints_to_sql({'txo.age__in': [18, 38]}),
|
constraints_to_sql({'txo.age__in#2': [18, 38]}),
|
||||||
('txo.age IN (:txo_age__in0, :txo_age__in1)', {
|
('txo.age IN (:txo_age__in2_0, :txo_age__in2_1)', {
|
||||||
'txo_age__in0': 18,
|
'txo_age__in2_0': 18,
|
||||||
'txo_age__in1': 38
|
'txo_age__in2_1': 38
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
constraints_to_sql({'txo.name__in': ('abc123', 'def456')}),
|
constraints_to_sql({'txo.name__in': ('abc123', 'def456')}),
|
||||||
('txo.name IN (:txo_name__in0, :txo_name__in1)', {
|
('txo.name IN (:txo_name__in0_0, :txo_name__in0_1)', {
|
||||||
'txo_name__in0': 'abc123',
|
'txo_name__in0_0': 'abc123',
|
||||||
'txo_name__in1': 'def456'
|
'txo_name__in0_1': 'def456'
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -96,16 +100,16 @@ class TestQueryBuilder(unittest.TestCase):
|
||||||
def test_not_in(self):
|
def test_not_in(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
constraints_to_sql({'txo.age__not_in': [18, 38]}),
|
constraints_to_sql({'txo.age__not_in': [18, 38]}),
|
||||||
('txo.age NOT IN (:txo_age__not_in0, :txo_age__not_in1)', {
|
('txo.age NOT IN (:txo_age__not_in0_0, :txo_age__not_in0_1)', {
|
||||||
'txo_age__not_in0': 18,
|
'txo_age__not_in0_0': 18,
|
||||||
'txo_age__not_in1': 38
|
'txo_age__not_in0_1': 38
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
constraints_to_sql({'txo.name__not_in': ('abc123', 'def456')}),
|
constraints_to_sql({'txo.name__not_in': ('abc123', 'def456')}),
|
||||||
('txo.name NOT IN (:txo_name__not_in0, :txo_name__not_in1)', {
|
('txo.name NOT IN (:txo_name__not_in0_0, :txo_name__not_in0_1)', {
|
||||||
'txo_name__not_in0': 'abc123',
|
'txo_name__not_in0_0': 'abc123',
|
||||||
'txo_name__not_in1': 'def456'
|
'txo_name__not_in0_1': 'def456'
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -128,10 +132,10 @@ class TestQueryBuilder(unittest.TestCase):
|
||||||
a__not='b', b__in='select * from blah where c=:$c',
|
a__not='b', b__in='select * from blah where c=:$c',
|
||||||
d__any={'one__like': 'o', 'two': 2}, limit=10, order_by='b', **{'$c': 3}),
|
d__any={'one__like': 'o', 'two': 2}, limit=10, order_by='b', **{'$c': 3}),
|
||||||
(
|
(
|
||||||
"select * from foo WHERE a != :a__not AND "
|
"select * from foo WHERE a != :a__not0 AND "
|
||||||
"b IN (select * from blah where c=:$c) AND "
|
"b IN (select * from blah where c=:$c) AND "
|
||||||
"(one LIKE :d__any_one__like OR two = :d__any_two) ORDER BY b LIMIT 10",
|
"(one LIKE :d__any0_one__like0 OR two = :d__any0_two0) ORDER BY b LIMIT 10",
|
||||||
{'a__not': 'b', 'd__any_one__like': 'o', 'd__any_two': 2, '$c': 3}
|
{'a__not0': 'b', 'd__any0_one__like0': 'o', 'd__any0_two0': 2, '$c': 3}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,9 @@ class AIOSQLite:
|
||||||
def constraints_to_sql(constraints, joiner=' AND ', prepend_key=''):
|
def constraints_to_sql(constraints, joiner=' AND ', prepend_key=''):
|
||||||
sql, values = [], {}
|
sql, values = [], {}
|
||||||
for key, constraint in constraints.items():
|
for key, constraint in constraints.items():
|
||||||
|
tag = '0'
|
||||||
|
if '#' in key:
|
||||||
|
key, tag = key[:key.index('#')], key[key.index('#')+1:]
|
||||||
col, op, key = key, '=', key.replace('.', '_')
|
col, op, key = key, '=', key.replace('.', '_')
|
||||||
if key.startswith('$'):
|
if key.startswith('$'):
|
||||||
values[key] = constraint
|
values[key] = constraint
|
||||||
|
@ -120,8 +123,8 @@ def constraints_to_sql(constraints, joiner=' AND ', prepend_key=''):
|
||||||
if isinstance(constraint, (list, set, tuple)):
|
if isinstance(constraint, (list, set, tuple)):
|
||||||
keys = []
|
keys = []
|
||||||
for i, val in enumerate(constraint):
|
for i, val in enumerate(constraint):
|
||||||
keys.append(f':{key}{i}')
|
keys.append(f':{key}{tag}_{i}')
|
||||||
values[f'{key}{i}'] = val
|
values[f'{key}{tag}_{i}'] = val
|
||||||
sql.append(f'{col} {op} ({", ".join(keys)})')
|
sql.append(f'{col} {op} ({", ".join(keys)})')
|
||||||
elif isinstance(constraint, str):
|
elif isinstance(constraint, str):
|
||||||
sql.append(f'{col} {op} ({constraint})')
|
sql.append(f'{col} {op} ({constraint})')
|
||||||
|
@ -129,12 +132,12 @@ def constraints_to_sql(constraints, joiner=' AND ', prepend_key=''):
|
||||||
raise ValueError(f"{col} requires a list, set or string as constraint value.")
|
raise ValueError(f"{col} requires a list, set or string as constraint value.")
|
||||||
continue
|
continue
|
||||||
elif key.endswith('__any'):
|
elif key.endswith('__any'):
|
||||||
where, subvalues = constraints_to_sql(constraint, ' OR ', key+'_')
|
where, subvalues = constraints_to_sql(constraint, ' OR ', key+tag+'_')
|
||||||
sql.append(f'({where})')
|
sql.append(f'({where})')
|
||||||
values.update(subvalues)
|
values.update(subvalues)
|
||||||
continue
|
continue
|
||||||
sql.append(f'{col} {op} :{prepend_key}{key}')
|
sql.append(f'{col} {op} :{prepend_key}{key}{tag}')
|
||||||
values[prepend_key+key] = constraint
|
values[prepend_key+key+tag] = constraint
|
||||||
return joiner.join(sql) if sql else '', values
|
return joiner.join(sql) if sql else '', values
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue