sql-blocks 0.0.1__tar.gz → 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: Allows you to create objects for parts of SQL query commands. Also to combine these objects by joining them, adding or removing parts...
5
5
  Home-page: https://github.com/julio-cascalles/sql_blocks
6
6
  Author: Júlio Cascalles
@@ -35,7 +35,7 @@ You can specify your own alias: `a = Select('Actor a')`
35
35
 
36
36
  - Select(
37
37
  'Actor a',
38
- name=NamedField('actors_name'), Distinct
38
+ name=NamedField('actors_name', Distinct)
39
39
  )
40
40
 
41
41
  ---
@@ -53,7 +53,6 @@ You can specify your own alias: `a = Select('Actor a')`
53
53
  `a = Select( 'Actor a', age=Between(45, 69) )`
54
54
 
55
55
  3.2 -- Sub-queries:
56
-
57
56
  ```
58
57
  query = Select('Movie m', title=Field,
59
58
  id=SubSelect(
@@ -85,6 +84,15 @@ query = Select('Movie m', title=Field,
85
84
  ```
86
85
  > Could be AND=Options(...)
87
86
 
87
+ 3.4 -- Negative conditions use the _Not_ class instead of _Where_
88
+ ```
89
+ franchise
90
+ based_on_book=Not.is_null()
91
+ ```
92
+
93
+ 3.5 -- List of values
94
+ hash_tag=Where.list(['space', 'monster', 'gore'])
95
+
88
96
  ---
89
97
  ### 4 - A field can be two things at the same time:
90
98
 
@@ -292,5 +300,15 @@ m2 = Select(
292
300
  **m1 == m2 # --- True!**
293
301
 
294
302
  ---
295
- ---
296
303
 
304
+ ### 10 - CASE...WHEN...THEN
305
+ Select(
306
+ 'Product',
307
+ label=Case('price').when(
308
+ lt(50), 'cheap'
309
+ ).when(
310
+ gt(100), 'expensive'
311
+ ).else_value(
312
+ 'normal'
313
+ )
314
+ )
@@ -20,7 +20,7 @@ You can specify your own alias: `a = Select('Actor a')`
20
20
 
21
21
  - Select(
22
22
  'Actor a',
23
- name=NamedField('actors_name'), Distinct
23
+ name=NamedField('actors_name', Distinct)
24
24
  )
25
25
 
26
26
  ---
@@ -38,7 +38,6 @@ You can specify your own alias: `a = Select('Actor a')`
38
38
  `a = Select( 'Actor a', age=Between(45, 69) )`
39
39
 
40
40
  3.2 -- Sub-queries:
41
-
42
41
  ```
43
42
  query = Select('Movie m', title=Field,
44
43
  id=SubSelect(
@@ -70,6 +69,15 @@ query = Select('Movie m', title=Field,
70
69
  ```
71
70
  > Could be AND=Options(...)
72
71
 
72
+ 3.4 -- Negative conditions use the _Not_ class instead of _Where_
73
+ ```
74
+ franchise
75
+ based_on_book=Not.is_null()
76
+ ```
77
+
78
+ 3.5 -- List of values
79
+ hash_tag=Where.list(['space', 'monster', 'gore'])
80
+
73
81
  ---
74
82
  ### 4 - A field can be two things at the same time:
75
83
 
@@ -277,5 +285,15 @@ m2 = Select(
277
285
  **m1 == m2 # --- True!**
278
286
 
279
287
  ---
280
- ---
281
288
 
289
+ ### 10 - CASE...WHEN...THEN
290
+ Select(
291
+ 'Product',
292
+ label=Case('price').when(
293
+ lt(50), 'cheap'
294
+ ).when(
295
+ gt(100), 'expensive'
296
+ ).else_value(
297
+ 'normal'
298
+ )
299
+ )
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sql_blocks"
3
- version = "0.0.1"
3
+ version = "0.0.3"
4
4
  authors = [
5
5
  { name="Julio Cascalles", email="julio.cascalles@outlook.com" },
6
6
  ]
@@ -3,7 +3,7 @@ from setuptools import setup
3
3
 
4
4
  setup(
5
5
  name = 'sql_blocks',
6
- version = '1.0.0',
6
+ version = '0.0.3',
7
7
  author = 'Júlio Cascalles',
8
8
  author_email = 'julio.cascalles@outlook.com',
9
9
  packages = ['sql_blocks'],
@@ -11,6 +11,7 @@ KEYWORD = {
11
11
  SELECT, FROM, WHERE, GROUP_BY, ORDER_BY, LIMIT = KEYWORD.keys()
12
12
  USUAL_KEYS = [SELECT, WHERE, GROUP_BY, ORDER_BY]
13
13
 
14
+
14
15
  class SQLObject:
15
16
  def __init__(self, table_name: str=''):
16
17
  self.alias = ''
@@ -123,25 +124,36 @@ class ForeignKey:
123
124
  def __init__(self, table_name: str):
124
125
  self.table_name = table_name
125
126
 
127
+ @staticmethod
128
+ def get_key(obj1: SQLObject, obj2: SQLObject) -> tuple:
129
+ return obj1.table_name, obj2.table_name
130
+
126
131
  def add(self, name: str, main: SQLObject):
127
- key = f'{main.table_name}_{self.table_name}'
128
- ForeignKey.references[key] = name
132
+ key = self.get_key(main, self)
133
+ ForeignKey.references[key] = (name, '')
129
134
 
130
135
  @classmethod
131
- def find(cls, obj1: SQLObject, obj2: SQLObject) -> list:
132
- key = f'{obj1.table_name}_{obj2.table_name}'
133
- return cls.references.get(key, '').split('_')
136
+ def find(cls, obj1: SQLObject, obj2: SQLObject) -> tuple:
137
+ key = cls.get_key(obj1, obj2)
138
+ a, b = cls.references.get(key, ('', ''))
139
+ return a, (b or obj2.key_field)
140
+
141
+
142
+ def quoted(value) -> str:
143
+ if isinstance(value, str):
144
+ value = f"'{value}'"
145
+ return str(value)
134
146
 
135
147
 
136
148
  class Where:
149
+ prefix = ''
150
+
137
151
  def __init__(self, expr: str):
138
- self.expr = expr
152
+ self.expr = f'{self.prefix}{expr}'
139
153
 
140
154
  @classmethod
141
155
  def __constructor(cls, operator: str, value):
142
- if isinstance(value, str):
143
- return cls(expr=f"{operator} '{value}'")
144
- return cls(expr=f'{operator} {value}')
156
+ return cls(expr=f'{operator} {quoted(value)}')
145
157
 
146
158
  @classmethod
147
159
  def eq(cls, value):
@@ -164,8 +176,18 @@ class Where:
164
176
  return cls.__constructor('<', value)
165
177
 
166
178
  @classmethod
167
- def lte(cls, value) -> str:
179
+ def lte(cls, value):
168
180
  return cls.__constructor('<=', value)
181
+
182
+ @classmethod
183
+ def is_null(cls):
184
+ return cls('IS NULL')
185
+
186
+ @classmethod
187
+ def list(cls, values):
188
+ if isinstance(values, list):
189
+ values = ','.join(quoted(v) for v in values)
190
+ return cls(f'IN ({values})')
169
191
 
170
192
  def add(self, name: str, main: SQLObject):
171
193
  main.values.setdefault(WHERE, []).append('{} {}'.format(
@@ -173,6 +195,41 @@ class Where:
173
195
  ))
174
196
 
175
197
 
198
+ class Not(Where):
199
+ prefix = 'NOT '
200
+
201
+ @classmethod
202
+ def eq(cls, value):
203
+ return Where.__constructor('<>', value)
204
+
205
+
206
+ class Case:
207
+ def __init__(self, field: str):
208
+ self.__conditions = {}
209
+ self.default = None
210
+ self.field = field
211
+
212
+ def when(self, condition: Where, result: str):
213
+ self.__conditions[result] = condition
214
+ return self
215
+
216
+ def else_value(self, default: str):
217
+ self.default = default
218
+ return self
219
+
220
+ def add(self, name: str, main: SQLObject):
221
+ field = Field.format(self.field, main)
222
+ default = quoted(self.default)
223
+ name = 'CASE \n{}\n\tEND AS {}'.format(
224
+ '\n'.join(
225
+ f'\t\tWHEN {field} {cond.expr} THEN {quoted(res)}'
226
+ for res, cond in self.__conditions.items()
227
+ ) + f'\n\t\tELSE {default}' if default else '',
228
+ name
229
+ )
230
+ main.values.setdefault(SELECT, []).append(name)
231
+
232
+
176
233
  class Options:
177
234
  def __init__(self, **values):
178
235
  self.__children: dict = values
@@ -266,10 +323,7 @@ class Select(SQLObject):
266
323
 
267
324
  def __init__(self, table_name: str='', **values):
268
325
  super().__init__(table_name)
269
- to_list = lambda x: x if isinstance(x, list) else [x]
270
- for name, params in values.items():
271
- for obj in to_list(params):
272
- obj.add(name, self)
326
+ self.__call__(**values)
273
327
  self.break_lines = True
274
328
 
275
329
  def add(self, name: str, main: SQLObject):
@@ -292,17 +346,17 @@ class Select(SQLObject):
292
346
  update_values(key, self.values.get(key, []))
293
347
 
294
348
  def __add__(self, other: SQLObject):
295
- foreign_field, *primary_key = ForeignKey.find(self, other)
349
+ foreign_field, primary_key = ForeignKey.find(self, other)
296
350
  if not foreign_field:
297
- foreign_field, *primary_key = ForeignKey.find(other, self)
351
+ foreign_field, primary_key = ForeignKey.find(other, self)
298
352
  if foreign_field:
299
- if primary_key:
300
- PrimaryKey.add(primary_key[0], self)
353
+ if primary_key and not self.key_field:
354
+ PrimaryKey.add(primary_key, self)
301
355
  self.add(foreign_field, other)
302
356
  return other
303
357
  raise ValueError(f'No relationship found between {self.table_name} and {other.table_name}.')
304
358
  elif primary_key:
305
- PrimaryKey.add(primary_key[0], other)
359
+ PrimaryKey.add(primary_key, other)
306
360
  other.add(foreign_field, self)
307
361
  return self
308
362
 
@@ -319,10 +373,12 @@ class Select(SQLObject):
319
373
  return f'{select}{_from}{where}{groupBy}{orderBy}{limit}'.strip()
320
374
 
321
375
  def __call__(self, **values):
322
- for name, obj in values.items():
323
- obj.add(name, self)
376
+ to_list = lambda x: x if isinstance(x, list) else [x]
377
+ for name, params in values.items():
378
+ for obj in to_list(params):
379
+ obj.add(name, self)
324
380
  return self
325
-
381
+
326
382
  def __eq__(self, other: SQLObject) -> bool:
327
383
  def sorted_values(obj: SQLObject, key: str) -> list:
328
384
  return sorted(obj.values.get(key, []))
@@ -368,7 +424,7 @@ class Select(SQLObject):
368
424
  fld.strip() for fld in re.split(
369
425
  separator, values[key]
370
426
  ) if len(tables) == 1
371
- or re.findall(f'^[( ]*{obj.alias}\.', fld)
427
+ or re.findall(f'^[( ]*{obj.alias}.', fld)
372
428
  ]
373
429
  obj.values[key] = [
374
430
  f'{obj.alias}.{f}' if not '.' in f else f for f in fields
@@ -380,4 +436,4 @@ class Select(SQLObject):
380
436
  class SubSelect(Select):
381
437
  def add(self, name: str, main: SQLObject):
382
438
  self.break_lines = False
383
- Where(f'IN ({self})').add(name, main)
439
+ Where.list(self).add(name, main)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sql_blocks
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: Allows you to create objects for parts of SQL query commands. Also to combine these objects by joining them, adding or removing parts...
5
5
  Home-page: https://github.com/julio-cascalles/sql_blocks
6
6
  Author: Júlio Cascalles
@@ -35,7 +35,7 @@ You can specify your own alias: `a = Select('Actor a')`
35
35
 
36
36
  - Select(
37
37
  'Actor a',
38
- name=NamedField('actors_name'), Distinct
38
+ name=NamedField('actors_name', Distinct)
39
39
  )
40
40
 
41
41
  ---
@@ -53,7 +53,6 @@ You can specify your own alias: `a = Select('Actor a')`
53
53
  `a = Select( 'Actor a', age=Between(45, 69) )`
54
54
 
55
55
  3.2 -- Sub-queries:
56
-
57
56
  ```
58
57
  query = Select('Movie m', title=Field,
59
58
  id=SubSelect(
@@ -85,6 +84,15 @@ query = Select('Movie m', title=Field,
85
84
  ```
86
85
  > Could be AND=Options(...)
87
86
 
87
+ 3.4 -- Negative conditions use the _Not_ class instead of _Where_
88
+ ```
89
+ franchise
90
+ based_on_book=Not.is_null()
91
+ ```
92
+
93
+ 3.5 -- List of values
94
+ hash_tag=Where.list(['space', 'monster', 'gore'])
95
+
88
96
  ---
89
97
  ### 4 - A field can be two things at the same time:
90
98
 
@@ -292,5 +300,15 @@ m2 = Select(
292
300
  **m1 == m2 # --- True!**
293
301
 
294
302
  ---
295
- ---
296
303
 
304
+ ### 10 - CASE...WHEN...THEN
305
+ Select(
306
+ 'Product',
307
+ label=Case('price').when(
308
+ lt(50), 'cheap'
309
+ ).when(
310
+ gt(100), 'expensive'
311
+ ).else_value(
312
+ 'normal'
313
+ )
314
+ )
@@ -1,6 +1,9 @@
1
- from sql_blocks import *
1
+ from sql_blocks.sql_blocks import *
2
2
 
3
3
 
4
+ Select.join_type = JoinType.LEFT
5
+ OrderBy.sort = SortType.DESC
6
+
4
7
  def best_movies() -> SubSelect:
5
8
  return SubSelect(
6
9
  'Review r', movie=[GroupBy, Distinct], rate=Having.avg(Where.gt(4.5))
@@ -24,7 +27,7 @@ def detached_objects() -> tuple:
24
27
  )
25
28
  return select_actor(), select_cast(), select_movie()
26
29
 
27
- def expected_result() -> Select:
30
+ def query_reference() -> Select:
28
31
  return Select('Actor a', age=Between(45, 69),
29
32
  cast=Select(
30
33
  Cast=Table('role'), id=PrimaryKey,
@@ -65,8 +68,8 @@ def single_text_to_objects():
65
68
 
66
69
  def many_texts_to_objects():
67
70
  ForeignKey.references = {
68
- 'Actor_Cast': 'cast_id',
69
- 'Cast_Movie': 'movie_id',
71
+ ('Actor', 'Cast'): ('cast', 'id'),
72
+ ('Cast', 'Movie'): ('movie', 'id'),
70
73
  }
71
74
  actor = Select.parse('''
72
75
  SELECT name as actors_name FROM Actor a
@@ -78,13 +81,3 @@ def many_texts_to_objects():
78
81
  WHERE ( m.genre = 'Sci-Fi' OR m.awards LIKE '%Oscar%' ) GROUP BY director
79
82
  """)[0]
80
83
  return actor, cast, movie
81
-
82
- Select.join_type = JoinType.LEFT
83
- OrderBy.sort = SortType.DESC
84
- b = best_movies()
85
- for func in [single_text_to_objects, detached_objects, many_texts_to_objects]:
86
- a, c, m = func()
87
- m.delete('director')
88
- m = m(id=b)
89
- query = a + (m + c)
90
- assert query == expected_result()
File without changes
File without changes