Skip to content

Commit 59ada00

Browse files
author
Daniel Townsend
committed
refactoring to use QueryString everwhere instead of raw strings
1 parent c167f68 commit 59ada00

26 files changed

+428
-174
lines changed

dev-requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
ipdb==0.11
22
ipython==7.1.1
33
twine==1.12.1
4+
mypy==0.650
5+

piccolo/columns/base.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
NotIn,
1313
NotLike
1414
)
15-
from ..custom_types import Iterable
1615
from .combination import Where
16+
from ..custom_types import Iterable
17+
from ..querystring import QueryString
1718

1819
if t.TYPE_CHECKING:
1920
from ..table import Table # noqa
@@ -53,14 +54,6 @@ def not_like(self, value: str) -> Where:
5354
raise ValueError('% is required for like operators')
5455
return Where(column=self, value=value, operator=NotLike)
5556

56-
def format_value(self, value):
57-
"""
58-
Takes the raw Python value and return a string usable in the database
59-
query.
60-
"""
61-
value = value if value else 'null'
62-
return f'{value}'
63-
6457
def __lt__(self, value) -> Where:
6558
return Where(column=self, value=value, operator=LessThan)
6659

@@ -82,7 +75,8 @@ def __ne__(self, value) -> Where: # type: ignore
8275
def __hash__(self):
8376
return hash(self._name)
8477

85-
def __str__(self):
78+
@property
79+
def querystring(self) -> QueryString:
8680
name = getattr(self, '_name', '')
8781
column_type = getattr(
8882
self,
@@ -99,4 +93,7 @@ def __str__(self):
9993
if references:
10094
query += f' REFERENCES {references.Meta.tablename}'
10195

102-
return query
96+
return QueryString(query)
97+
98+
def __str__(self):
99+
return self.querystring.__str__()

piccolo/columns/column_types.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import inspect
44
import typing as t
55

6-
from .base import Column
6+
from piccolo.columns.base import Column
7+
from piccolo.querystring import Unquoted
78

89
if t.TYPE_CHECKING:
910
import table # noqa
10-
from ..custom_types import Datetime # noqa
11+
from piccolo.custom_types import Datetime # noqa
1112

1213

1314
class Varchar(Column):
@@ -18,12 +19,6 @@ def __init__(self, length: int = 255, default: str = None,
1819
self.default = default
1920
super().__init__(**kwargs)
2021

21-
def format_value(self, value: str):
22-
if not value:
23-
return 'null'
24-
# TODO sanitize input - use prepared statements
25-
return f"'{value}'"
26-
2722

2823
class Integer(Column):
2924

@@ -47,7 +42,7 @@ def __init__(self, **kwargs) -> None:
4742
'primary': True,
4843
'key': True
4944
})
50-
self.default = 'DEFAULT'
45+
self.default = Unquoted('DEFAULT')
5146
super().__init__(**kwargs)
5247

5348

@@ -57,12 +52,6 @@ def __init__(self, default: 'Datetime' = None, **kwargs) -> None:
5752
self.default = default
5853
super().__init__(**kwargs)
5954

60-
def format_value(self, value: t.Optional[datetime.datetime]):
61-
if not value:
62-
return 'null'
63-
dt_string = value.isoformat().replace('T', ' ')
64-
return f"'{dt_string}'"
65-
6655

6756
class Boolean(Column):
6857

piccolo/columns/combination.py

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import typing
1+
import typing as t
22

33
from .operators import Operator
44
from ..custom_types import Combinable, Iterable
5+
from piccolo.querystring import QueryString
56

6-
if typing.TYPE_CHECKING:
7+
if t.TYPE_CHECKING:
78
from .base import Column # noqa
89

910

@@ -23,11 +24,17 @@ def __init__(self, first: Combinable, second: Combinable) -> None:
2324
self.first = first
2425
self.second = second
2526

26-
def __str__(self):
27-
return (
28-
f'({self.first.__str__()} {self.operator} {self.second.__str__()})'
27+
@property
28+
def querystring(self):
29+
return QueryString(
30+
'({} ' + self.operator + ' {})',
31+
self.first.querystring,
32+
self.second.querystring
2933
)
3034

35+
def __str__(self):
36+
self.querystring.__str__()
37+
3138

3239
class And(Combination):
3340
operator = 'AND'
@@ -42,28 +49,42 @@ class Where(CombinableMixin):
4249
def __init__(
4350
self,
4451
column: 'Column',
45-
value: typing.Any = None,
52+
value: t.Any = None,
4653
values: Iterable = [],
47-
operator: typing.Type[Operator] = None
54+
operator: t.Type[Operator] = Operator
4855
) -> None:
4956
self.column = column
5057
self.value = value
5158
self.values = values
5259
self.operator = operator
5360

5461
@property
55-
def values_str(self) -> str:
56-
return ', '.join(
57-
[self.column.format_value(v) for v in self.values]
62+
def values_querystring(self) -> QueryString:
63+
template = ', '.join(['{}' for i in self.values])
64+
return QueryString(
65+
template,
66+
*self.values
5867
)
5968

60-
def __str__(self):
61-
kwargs = {
62-
'name': self.column._name
63-
}
69+
@property
70+
def querystring(self) -> QueryString:
71+
args: t.List[t.Any] = []
6472
if self.value:
65-
kwargs['value'] = self.column.format_value(self.value)
73+
args.append(self.value)
6674
if self.values:
67-
kwargs['values'] = self.values_str
75+
args.append(self.values_querystring)
6876

69-
return self.operator.template.format(**kwargs)
77+
# TODO Want something cleaner than this.
78+
template = self.operator.template.format(
79+
name=self.column._name,
80+
value='{}',
81+
values='{}'
82+
)
83+
84+
return QueryString(
85+
template,
86+
*args
87+
)
88+
89+
def __str__(self):
90+
return self.querystring.__str__()

piccolo/columns/operators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class Operator():
2-
pass
2+
template = ''
33

44

55
class Equal(Operator):

piccolo/query/base.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,22 @@
44

55
import ujson as json
66

7-
from ..utils.sync import run_sync
7+
from piccolo.querystring import QueryString
8+
from piccolo.utils.sync import run_sync
89

910
if t.TYPE_CHECKING:
1011
from table import Table # noqa
1112

1213

1314
class Query(object):
1415

15-
def __init__(self, table: 'Table', base: str = '', *args,
16-
**kwargs) -> None:
16+
def __init__(
17+
self,
18+
table: 'Table',
19+
base: QueryString = QueryString(''),
20+
*args,
21+
**kwargs
22+
) -> None:
1723
self.base = base
1824
self.table = table
1925
super().__init__()
@@ -26,9 +32,13 @@ async def run(self, as_dict=True, in_pool=True):
2632
)
2733

2834
if in_pool:
29-
results = await engine.run_in_pool(self.__str__())
35+
results = await engine.run_in_pool(
36+
*self.querystring.compile_string()
37+
)
3038
else:
31-
results = await engine.run(self.__str__())
39+
results = await engine.run(
40+
*self.querystring.compile_string()
41+
)
3242

3343
if results:
3444
keys = results[0].keys()
@@ -40,7 +50,7 @@ async def run(self, as_dict=True, in_pool=True):
4050
if hasattr(self, 'run_callback'):
4151
self.run_callback(raw)
4252

43-
# I have multiple ways of modifying the final output
53+
# TODO - I have multiple ways of modifying the final output
4454
# response_handlers, and output ...
4555
# Might try and merge them.
4656
raw = self.response_handler(raw)
@@ -88,3 +98,10 @@ def response_handler(self, response):
8898
the database driver.
8999
"""
90100
return response
101+
102+
@property
103+
def querystring(self):
104+
"""
105+
Subclasses need to override and return a QueryString object.
106+
"""
107+
raise NotImplementedError()

piccolo/query/methods/alter.py

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import dataclasses
2+
import itertools
23

34
from piccolo.columns.base import Column
4-
from ..base import Query
5+
from piccolo.query.base import Query
6+
from piccolo.querystring import QueryString
57

68

79
@dataclasses.dataclass
@@ -12,8 +14,14 @@ class Rename():
1214
column: Column
1315
new_name: str
1416

15-
def __str__(self):
16-
return f' RENAME "{self.column._name}" TO "{self.new_name}"'
17+
@property
18+
def querystring(self) -> QueryString:
19+
return QueryString(
20+
f'RENAME {self.column._name} TO {self.new_name}',
21+
)
22+
23+
def __str__(self) -> str:
24+
return self.querystring.__str__()
1725

1826

1927
@dataclasses.dataclass
@@ -23,8 +31,14 @@ class Drop():
2331
"""
2432
column: Column
2533

26-
def __str__(self):
27-
return f' DROP "{self.column._name}"'
34+
@property
35+
def querystring(self) -> QueryString:
36+
return QueryString(
37+
f'DROP {self.column._name}'
38+
)
39+
40+
def __str__(self) -> str:
41+
return self.querystring.__str__()
2842

2943

3044
@dataclasses.dataclass
@@ -35,40 +49,63 @@ class Add():
3549
name: str
3650
column: Column
3751

38-
def __str__(self):
52+
@property
53+
def querystring(self) -> QueryString:
3954
self.column._name = self.name
40-
return f' ADD {self.column.__str__()}'
55+
return QueryString(
56+
'ADD {}',
57+
self.column.querystring
58+
)
59+
60+
def __str__(self) -> str:
61+
return self.querystring.__str__()
4162

4263

4364
class Alter(Query):
4465

4566
def __init__(self, *args, **kwargs):
4667
super().__init__(*args, **kwargs)
47-
self._add = []
48-
self._drop = []
49-
self._rename = []
68+
self._add: t.List[Add] = []
69+
self._drop: t.List[Drop] = []
70+
self._rename: t.List[Rename] = []
5071

51-
def add(self, name: str, column: Column):
72+
def add(self, name: str, column: Column) -> 'Alter':
5273
self._add.append(
5374
Add(name, column)
5475
)
5576
return self
5677

57-
def rename(self, column: Column, new_name: str):
78+
def rename(self, column: Column, new_name: str) -> 'Alter':
5879
self._rename.append(
5980
Rename(column, new_name)
6081
)
6182
return self
6283

63-
def drop(self, column: Column):
84+
def drop(self, column: Column) -> 'Alter':
6485
self._drop.append(
6586
Drop(column)
6687
)
6788
return self
6889

69-
def __str__(self):
70-
query = f'ALTER TABLE "{self.table.Meta.tablename}"'
71-
for alterations in [self._add, self._rename, self._drop]:
72-
for a in alterations:
73-
query += a.__str__()
74-
return query
90+
@property
91+
def querystring(self) -> QueryString:
92+
query = f'ALTER TABLE {self.table.Meta.tablename}'
93+
94+
alterations = [
95+
i.querystring for i in itertools.chain(
96+
self._add,
97+
self._rename,
98+
self._drop
99+
)
100+
]
101+
102+
for a in alterations:
103+
query += ' {}'
104+
105+
return QueryString(
106+
query,
107+
*alterations
108+
)
109+
110+
def __str__(self) -> str:
111+
return self.querystring.__str__()

0 commit comments

Comments
 (0)