piccolo 0.113.0__py3-none-any.whl → 0.115.0__py3-none-any.whl
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.
- piccolo/__init__.py +1 -1
- piccolo/apps/fixtures/commands/load.py +47 -6
- piccolo/apps/migrations/auto/migration_manager.py +1 -1
- piccolo/columns/m2m.py +11 -7
- piccolo/query/methods/count.py +26 -11
- piccolo/query/methods/select.py +67 -28
- piccolo/table.py +43 -5
- {piccolo-0.113.0.dist-info → piccolo-0.115.0.dist-info}/METADATA +1 -1
- {piccolo-0.113.0.dist-info → piccolo-0.115.0.dist-info}/RECORD +21 -18
- tests/apps/fixtures/commands/test_dump_load.py +39 -1
- tests/apps/migrations/auto/integration/test_migrations.py +34 -2
- tests/columns/m2m/__init__.py +0 -0
- tests/columns/{test_m2m.py → m2m/base.py} +55 -426
- tests/columns/m2m/test_m2m.py +436 -0
- tests/columns/m2m/test_m2m_schema.py +48 -0
- tests/table/test_count.py +73 -7
- tests/table/test_objects.py +4 -1
- {piccolo-0.113.0.dist-info → piccolo-0.115.0.dist-info}/LICENSE +0 -0
- {piccolo-0.113.0.dist-info → piccolo-0.115.0.dist-info}/WHEEL +0 -0
- {piccolo-0.113.0.dist-info → piccolo-0.115.0.dist-info}/entry_points.txt +0 -0
- {piccolo-0.113.0.dist-info → piccolo-0.115.0.dist-info}/top_level.txt +0 -0
piccolo/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__VERSION__ = "0.
|
1
|
+
__VERSION__ = "0.115.0"
|
@@ -1,19 +1,27 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import sys
|
3
4
|
import typing as t
|
4
5
|
|
6
|
+
import typing_extensions
|
7
|
+
|
5
8
|
from piccolo.apps.fixtures.commands.shared import (
|
6
9
|
FixtureConfig,
|
7
10
|
create_pydantic_fixture_model,
|
8
11
|
)
|
9
12
|
from piccolo.conf.apps import Finder
|
10
13
|
from piccolo.engine import engine_finder
|
14
|
+
from piccolo.query.mixins import OnConflictAction
|
11
15
|
from piccolo.table import Table, sort_table_classes
|
12
16
|
from piccolo.utils.encoding import load_json
|
13
17
|
from piccolo.utils.list import batch
|
14
18
|
|
15
19
|
|
16
|
-
async def load_json_string(
|
20
|
+
async def load_json_string(
|
21
|
+
json_string: str,
|
22
|
+
chunk_size: int = 1000,
|
23
|
+
on_conflict_action: t.Optional[OnConflictAction] = None,
|
24
|
+
):
|
17
25
|
"""
|
18
26
|
Parses the JSON string, and inserts the parsed data into the database.
|
19
27
|
"""
|
@@ -71,10 +79,23 @@ async def load_json_string(json_string: str, chunk_size: int = 1000):
|
|
71
79
|
rows = data[table_class]
|
72
80
|
|
73
81
|
for chunk in batch(data=rows, chunk_size=chunk_size):
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
82
|
+
query = table_class.insert(*chunk)
|
83
|
+
if on_conflict_action is not None:
|
84
|
+
query = query.on_conflict(
|
85
|
+
target=table_class._meta.primary_key,
|
86
|
+
action=on_conflict_action,
|
87
|
+
values=table_class._meta.columns,
|
88
|
+
)
|
89
|
+
await query.run()
|
90
|
+
|
91
|
+
|
92
|
+
async def load(
|
93
|
+
path: str = "fixture.json",
|
94
|
+
chunk_size: int = 1000,
|
95
|
+
on_conflict: t.Optional[
|
96
|
+
typing_extensions.Literal["DO NOTHING", "DO UPDATE"]
|
97
|
+
] = None,
|
98
|
+
):
|
78
99
|
"""
|
79
100
|
Reads the fixture file, and loads the contents into the database.
|
80
101
|
|
@@ -86,8 +107,28 @@ async def load(path: str = "fixture.json", chunk_size: int = 1000):
|
|
86
107
|
determined by the database adapter, which has a max number of
|
87
108
|
parameters per query.
|
88
109
|
|
110
|
+
:param on_conflict:
|
111
|
+
If specified, the fixture will be upserted, meaning that if a row
|
112
|
+
already exists with a matching primary key, then it will be overridden
|
113
|
+
if "DO UPDATE", or it will be ignored if "DO NOTHING".
|
114
|
+
|
89
115
|
"""
|
90
116
|
with open(path, "r") as f:
|
91
117
|
contents = f.read()
|
92
118
|
|
93
|
-
|
119
|
+
on_conflict_action: t.Optional[OnConflictAction] = None
|
120
|
+
|
121
|
+
if on_conflict:
|
122
|
+
try:
|
123
|
+
on_conflict_action = OnConflictAction(on_conflict.upper())
|
124
|
+
except ValueError:
|
125
|
+
sys.exit(
|
126
|
+
f"{on_conflict} isn't a valid option - use 'DO NOTHING' or "
|
127
|
+
"'DO UPDATE'."
|
128
|
+
)
|
129
|
+
|
130
|
+
await load_json_string(
|
131
|
+
contents,
|
132
|
+
chunk_size=chunk_size,
|
133
|
+
on_conflict_action=on_conflict_action,
|
134
|
+
)
|
@@ -826,7 +826,7 @@ class MigrationManager:
|
|
826
826
|
await self._run_query(
|
827
827
|
schema_manager.move_table(
|
828
828
|
table_name=change_table_schema.tablename,
|
829
|
-
new_schema=change_table_schema.new_schema,
|
829
|
+
new_schema=change_table_schema.new_schema or "public",
|
830
830
|
current_schema=change_table_schema.old_schema,
|
831
831
|
)
|
832
832
|
)
|
piccolo/columns/m2m.py
CHANGED
@@ -57,30 +57,34 @@ class M2MSelect(Selectable):
|
|
57
57
|
)
|
58
58
|
|
59
59
|
def get_select_string(self, engine_type: str, with_alias=True) -> str:
|
60
|
-
|
60
|
+
m2m_table_name_with_schema = (
|
61
|
+
self.m2m._meta.resolved_joining_table._meta.get_formatted_tablename() # noqa: E501
|
62
|
+
) # noqa: E501
|
61
63
|
m2m_relationship_name = self.m2m._meta.name
|
62
64
|
|
63
65
|
fk_1 = self.m2m._meta.primary_foreign_key
|
64
66
|
fk_1_name = fk_1._meta.db_column_name
|
65
67
|
table_1 = fk_1._foreign_key_meta.resolved_references
|
66
68
|
table_1_name = table_1._meta.tablename
|
69
|
+
table_1_name_with_schema = table_1._meta.get_formatted_tablename()
|
67
70
|
table_1_pk_name = table_1._meta.primary_key._meta.db_column_name
|
68
71
|
|
69
72
|
fk_2 = self.m2m._meta.secondary_foreign_key
|
70
73
|
fk_2_name = fk_2._meta.db_column_name
|
71
74
|
table_2 = fk_2._foreign_key_meta.resolved_references
|
72
75
|
table_2_name = table_2._meta.tablename
|
76
|
+
table_2_name_with_schema = table_2._meta.get_formatted_tablename()
|
73
77
|
table_2_pk_name = table_2._meta.primary_key._meta.db_column_name
|
74
78
|
|
75
79
|
inner_select = f"""
|
76
|
-
|
77
|
-
JOIN
|
78
|
-
|
80
|
+
{m2m_table_name_with_schema}
|
81
|
+
JOIN {table_1_name_with_schema} "inner_{table_1_name}" ON (
|
82
|
+
{m2m_table_name_with_schema}."{fk_1_name}" = "inner_{table_1_name}"."{table_1_pk_name}"
|
79
83
|
)
|
80
|
-
JOIN
|
81
|
-
|
84
|
+
JOIN {table_2_name_with_schema} "inner_{table_2_name}" ON (
|
85
|
+
{m2m_table_name_with_schema}."{fk_2_name}" = "inner_{table_2_name}"."{table_2_pk_name}"
|
82
86
|
)
|
83
|
-
WHERE
|
87
|
+
WHERE {m2m_table_name_with_schema}."{fk_1_name}" = "{table_1_name}"."{table_1_pk_name}"
|
84
88
|
""" # noqa: E501
|
85
89
|
|
86
90
|
if engine_type in ("postgres", "cockroach"):
|
piccolo/query/methods/count.py
CHANGED
@@ -4,19 +4,29 @@ import typing as t
|
|
4
4
|
|
5
5
|
from piccolo.custom_types import Combinable
|
6
6
|
from piccolo.query.base import Query
|
7
|
-
from piccolo.query.methods.select import
|
7
|
+
from piccolo.query.methods.select import Count as SelectCount
|
8
8
|
from piccolo.query.mixins import WhereDelegate
|
9
9
|
from piccolo.querystring import QueryString
|
10
10
|
|
11
11
|
if t.TYPE_CHECKING: # pragma: no cover
|
12
|
+
from piccolo.columns import Column
|
12
13
|
from piccolo.table import Table
|
13
14
|
|
14
15
|
|
15
16
|
class Count(Query):
|
16
|
-
__slots__ = ("where_delegate",)
|
17
17
|
|
18
|
-
|
18
|
+
__slots__ = ("where_delegate", "column", "_distinct")
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
table: t.Type[Table],
|
23
|
+
column: t.Optional[Column] = None,
|
24
|
+
distinct: t.Optional[t.Sequence[Column]] = None,
|
25
|
+
**kwargs,
|
26
|
+
):
|
19
27
|
super().__init__(table, **kwargs)
|
28
|
+
self.column = column
|
29
|
+
self._distinct = distinct
|
20
30
|
self.where_delegate = WhereDelegate()
|
21
31
|
|
22
32
|
###########################################################################
|
@@ -26,6 +36,10 @@ class Count(Query):
|
|
26
36
|
self.where_delegate.where(*where)
|
27
37
|
return self
|
28
38
|
|
39
|
+
def distinct(self: Self, columns: t.Optional[t.Sequence[Column]]) -> Self:
|
40
|
+
self._distinct = columns
|
41
|
+
return self
|
42
|
+
|
29
43
|
###########################################################################
|
30
44
|
|
31
45
|
async def response_handler(self, response) -> bool:
|
@@ -33,14 +47,15 @@ class Count(Query):
|
|
33
47
|
|
34
48
|
@property
|
35
49
|
def default_querystrings(self) -> t.Sequence[QueryString]:
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
50
|
+
table: t.Type[Table] = self.table
|
51
|
+
|
52
|
+
query = table.select(
|
53
|
+
SelectCount(column=self.column, distinct=self._distinct)
|
54
|
+
)
|
55
|
+
|
56
|
+
query.where_delegate._where = self.where_delegate._where
|
57
|
+
|
58
|
+
return query.querystrings
|
44
59
|
|
45
60
|
|
46
61
|
Self = t.TypeVar("Self", bound=Count)
|
piccolo/query/methods/select.py
CHANGED
@@ -99,43 +99,84 @@ class Avg(Selectable):
|
|
99
99
|
|
100
100
|
class Count(Selectable):
|
101
101
|
"""
|
102
|
-
Used in conjunction with the ``group_by``
|
102
|
+
Used in ``Select`` queries, usually in conjunction with the ``group_by``
|
103
|
+
clause::
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
|
105
|
+
>>> await Band.select(
|
106
|
+
... Band.manager.name.as_alias('manager_name'),
|
107
|
+
... Count(alias='band_count')
|
108
|
+
... ).group_by(Band.manager)
|
109
|
+
[{'manager_name': 'Guido', 'count': 1}, ...]
|
107
110
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
# We can use an alias. These two are equivalent:
|
113
|
-
|
114
|
-
await Band.select(
|
115
|
-
Band.name, Count(alias="total")
|
116
|
-
).group_by(Band.name)
|
111
|
+
It can also be used without the ``group_by`` clause (though you may prefer
|
112
|
+
to the :meth:`Table.count <piccolo.table.Table.count>` method instead, as
|
113
|
+
it's more convenient)::
|
117
114
|
|
118
|
-
await Band.select(
|
119
|
-
|
120
|
-
Count().as_alias("total")
|
121
|
-
).group_by(Band.name)
|
115
|
+
>>> await Band.select(Count())
|
116
|
+
[{'count': 3}]
|
122
117
|
|
123
118
|
"""
|
124
119
|
|
125
120
|
def __init__(
|
126
|
-
self,
|
121
|
+
self,
|
122
|
+
column: t.Optional[Column] = None,
|
123
|
+
distinct: t.Optional[t.Sequence[Column]] = None,
|
124
|
+
alias: str = "count",
|
127
125
|
):
|
126
|
+
"""
|
127
|
+
:param column:
|
128
|
+
If specified, the count is for non-null values in that column.
|
129
|
+
:param distinct:
|
130
|
+
If specified, the count is for distinct values in those columns.
|
131
|
+
:param alias:
|
132
|
+
The name of the value in the response::
|
133
|
+
|
134
|
+
# These two are equivalent:
|
135
|
+
|
136
|
+
await Band.select(
|
137
|
+
Band.name, Count(alias="total")
|
138
|
+
).group_by(Band.name)
|
139
|
+
|
140
|
+
await Band.select(
|
141
|
+
Band.name,
|
142
|
+
Count().as_alias("total")
|
143
|
+
).group_by(Band.name)
|
144
|
+
|
145
|
+
"""
|
146
|
+
if distinct and column:
|
147
|
+
raise ValueError("Only specify `column` or `distinct`")
|
148
|
+
|
128
149
|
self.column = column
|
150
|
+
self.distinct = distinct
|
129
151
|
self._alias = alias
|
130
152
|
|
131
153
|
def get_select_string(
|
132
154
|
self, engine_type: str, with_alias: bool = True
|
133
155
|
) -> str:
|
134
|
-
|
135
|
-
|
156
|
+
expression: str
|
157
|
+
|
158
|
+
if self.distinct:
|
159
|
+
if engine_type == "sqlite":
|
160
|
+
# SQLite doesn't allow us to specify multiple columns, so
|
161
|
+
# instead we concatenate the values.
|
162
|
+
column_names = " || ".join(
|
163
|
+
i._meta.get_full_name(with_alias=False)
|
164
|
+
for i in self.distinct
|
165
|
+
)
|
166
|
+
else:
|
167
|
+
column_names = ", ".join(
|
168
|
+
i._meta.get_full_name(with_alias=False)
|
169
|
+
for i in self.distinct
|
170
|
+
)
|
171
|
+
|
172
|
+
expression = f"DISTINCT ({column_names})"
|
136
173
|
else:
|
137
|
-
|
138
|
-
|
174
|
+
if self.column:
|
175
|
+
expression = self.column._meta.get_full_name(with_alias=False)
|
176
|
+
else:
|
177
|
+
expression = "*"
|
178
|
+
|
179
|
+
return f'COUNT({expression}) AS "{self._alias}"'
|
139
180
|
|
140
181
|
|
141
182
|
class Max(Selectable):
|
@@ -737,12 +778,10 @@ class Select(Query[TableInstance, t.List[t.Dict[str, t.Any]]]):
|
|
737
778
|
query = "SELECT"
|
738
779
|
|
739
780
|
distinct = self.distinct_delegate._distinct
|
740
|
-
if distinct:
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
query += "{}"
|
745
|
-
args.append(distinct.querystring)
|
781
|
+
if distinct.on:
|
782
|
+
distinct.validate_on(self.order_by_delegate._order_by)
|
783
|
+
query += "{}"
|
784
|
+
args.append(distinct.querystring)
|
746
785
|
|
747
786
|
query += f" {columns_str} FROM {self.table._meta.get_formatted_tablename()}" # noqa: E501
|
748
787
|
|
piccolo/table.py
CHANGED
@@ -1114,16 +1114,54 @@ class Table(metaclass=TableMetaclass):
|
|
1114
1114
|
return Objects[TableInstance](table=cls, prefetch=prefetch)
|
1115
1115
|
|
1116
1116
|
@classmethod
|
1117
|
-
def count(
|
1118
|
-
|
1119
|
-
|
1117
|
+
def count(
|
1118
|
+
cls,
|
1119
|
+
column: t.Optional[Column] = None,
|
1120
|
+
distinct: t.Optional[t.Sequence[Column]] = None,
|
1121
|
+
) -> Count:
|
1120
1122
|
|
1121
|
-
|
1123
|
+
"""
|
1124
|
+
Count the number of matching rows::
|
1122
1125
|
|
1123
1126
|
await Band.count().where(Band.popularity > 1000)
|
1124
1127
|
|
1128
|
+
:param column:
|
1129
|
+
If specified, just count rows where this column isn't null.
|
1130
|
+
|
1131
|
+
:param distinct:
|
1132
|
+
Counts the number of distinct values for these columns. For
|
1133
|
+
example, if we have a concerts table::
|
1134
|
+
|
1135
|
+
class Concert(Table):
|
1136
|
+
band = Varchar()
|
1137
|
+
start_date = Date()
|
1138
|
+
|
1139
|
+
With this data:
|
1140
|
+
|
1141
|
+
.. table::
|
1142
|
+
:widths: auto
|
1143
|
+
|
1144
|
+
=========== ==========
|
1145
|
+
band start_date
|
1146
|
+
=========== ==========
|
1147
|
+
Pythonistas 2023-01-01
|
1148
|
+
Pythonistas 2023-02-03
|
1149
|
+
Rustaceans 2023-01-01
|
1150
|
+
=========== ==========
|
1151
|
+
|
1152
|
+
Without the ``distinct`` argument, we get the count of all
|
1153
|
+
rows::
|
1154
|
+
|
1155
|
+
>>> await Concert.count()
|
1156
|
+
3
|
1157
|
+
|
1158
|
+
To get the number of unique concert dates::
|
1159
|
+
|
1160
|
+
>>> await Concert.count(distinct=[Concert.start_date])
|
1161
|
+
2
|
1162
|
+
|
1125
1163
|
"""
|
1126
|
-
return Count(table=cls)
|
1164
|
+
return Count(table=cls, column=column, distinct=distinct)
|
1127
1165
|
|
1128
1166
|
@classmethod
|
1129
1167
|
def exists(cls) -> Exists:
|
@@ -1,10 +1,10 @@
|
|
1
|
-
piccolo/__init__.py,sha256
|
1
|
+
piccolo/__init__.py,sha256=-CLJ49iCidYB3xsIzg9Dqc7jRuCmSb4xu1RlklEi9y8,24
|
2
2
|
piccolo/custom_types.py,sha256=7HMQAze-5mieNLfbQ5QgbRQgR2abR7ol0qehv2SqROY,604
|
3
3
|
piccolo/main.py,sha256=2W2EXXEr-EN1PG8s8xHIWCvU7t7kT004fBChK9CZhzo,5024
|
4
4
|
piccolo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
piccolo/querystring.py,sha256=pjtp0jLtZc7AaVESR4QTZu9wlsGGxeyfLjJZx26U7IA,5859
|
6
6
|
piccolo/schema.py,sha256=aWPuZxEulgBRD5NTqKN-RAZchxu-PoIrn0iFrWGZuq4,7731
|
7
|
-
piccolo/table.py,sha256=
|
7
|
+
piccolo/table.py,sha256=99O73bkpvI5tzFSMD54yOvTa7Mha3mWjWrSL0ILpTsA,49374
|
8
8
|
piccolo/table_reflection.py,sha256=jrN1nHerDJ4tU09GtNN3hz7ap-7rXnSUjljFO6LB2H0,7094
|
9
9
|
piccolo/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
piccolo/apps/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -45,7 +45,7 @@ piccolo/apps/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
45
45
|
piccolo/apps/fixtures/piccolo_app.py,sha256=4O1Cznl1zms2gIw2iVjCjidkgCfFcB83nZIAJwcNTtg,268
|
46
46
|
piccolo/apps/fixtures/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
47
47
|
piccolo/apps/fixtures/commands/dump.py,sha256=fC0XsLaTLAP_VtfFSWPNj9XsZLfWGpl-Og_y71_OL7o,3650
|
48
|
-
piccolo/apps/fixtures/commands/load.py,sha256=
|
48
|
+
piccolo/apps/fixtures/commands/load.py,sha256=nqjqrOrEww5Q_gvhTP_Nhu4urD6JqwpsC3NR9FWZq7o,4188
|
49
49
|
piccolo/apps/fixtures/commands/shared.py,sha256=BMG8ku5FyK5vLewaDYwEfe_5HCnEVz1oKxHfL-GpO08,1462
|
50
50
|
piccolo/apps/meta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
51
51
|
piccolo/apps/meta/piccolo_app.py,sha256=EdAB74BvwpwO9L8DOIMXPly9f4hiW3oaWdzoLNNV2a4,224
|
@@ -56,7 +56,7 @@ piccolo/apps/migrations/piccolo_app.py,sha256=1EcS2ComBPCaMCC2C3WaPR_GqLwt3XiIJN
|
|
56
56
|
piccolo/apps/migrations/tables.py,sha256=jqBnK-Rk545v1Eu6GaLHTVz7-uwBTUnz2m58OA-mxTc,799
|
57
57
|
piccolo/apps/migrations/auto/__init__.py,sha256=5bgdh5EsROSvq4rrZAxpzmXiJNj5LJKwf06dxI2LZsc,191
|
58
58
|
piccolo/apps/migrations/auto/diffable_table.py,sha256=ZjUIjqcEAWsoFJuShSDy6W1HUBOM1G7kEsne-R5VJ-Q,6935
|
59
|
-
piccolo/apps/migrations/auto/migration_manager.py,sha256=
|
59
|
+
piccolo/apps/migrations/auto/migration_manager.py,sha256=RqegV19ZM2hcb03x5IBP2615CD1uEhrgPXKNZIZjq5o,31765
|
60
60
|
piccolo/apps/migrations/auto/operations.py,sha256=eAiTK9wCxFwhE13W0Tf2-FUg7kyPUWjDEGHyyRHwZUg,1154
|
61
61
|
piccolo/apps/migrations/auto/schema_differ.py,sha256=oNJVqqDxokUhMU-JjoA0aC21j0sbLEHMZRiyQwGG-rg,25348
|
62
62
|
piccolo/apps/migrations/auto/schema_snapshot.py,sha256=ZqUg4NpChOeoACKF2gkhqsz1BW3wOWFnzJCccq-CNNQ,4719
|
@@ -115,7 +115,7 @@ piccolo/columns/choices.py,sha256=-HNQuk9vMmVZIPZ5PMeXGTfr23o4nzKPSAkvcG1k0y8,72
|
|
115
115
|
piccolo/columns/column_types.py,sha256=X0nTvlc8epfcu8Q8uuIBlnHv_R_GTJ58dehC343vNgA,77278
|
116
116
|
piccolo/columns/combination.py,sha256=vMXC2dfY7pvnCFhsT71XFVyb4gdQzfRsCMaiduu04Ss,6900
|
117
117
|
piccolo/columns/indexes.py,sha256=NfNok3v_791jgDlN28KmhP9ZCjl6031BXmjxV3ovXJk,372
|
118
|
-
piccolo/columns/m2m.py,sha256=
|
118
|
+
piccolo/columns/m2m.py,sha256=JNgulzZjmwzPDNlpHLMIE_GP8UoBEcWFS4mEIkbKpvk,14357
|
119
119
|
piccolo/columns/readable.py,sha256=vKIEc_vWxKo4GSPOeJZz-q5a1i4DNaoBcCNG_i_2OSA,1485
|
120
120
|
piccolo/columns/reference.py,sha256=-e1nu-yWeqiyJtDqG0cPVVVrLai6ClEAYAa8c6a0hnA,3573
|
121
121
|
piccolo/columns/defaults/__init__.py,sha256=7hpB13baEJgc1zbZjRKDFr-5hltxM2VGj8KnKfOiS8c,145
|
@@ -146,7 +146,7 @@ piccolo/query/mixins.py,sha256=N6HAN_A4kd-PC07q3OIzwrkRy3ZwGMtB2xLueYefBSM,21649
|
|
146
146
|
piccolo/query/proxy.py,sha256=Hg5S6tp1EiKD899eYdDKHscFYucHdKtL3YC2GTcL2Jk,1833
|
147
147
|
piccolo/query/methods/__init__.py,sha256=_PfGUdOd6AsKq1sqXeZUHhESHE-e1cNpwFr8Lyz7QoY,421
|
148
148
|
piccolo/query/methods/alter.py,sha256=AI9YkJeip2EitrWJN_TDExXhA8HGAG3XuDz1NR-KirQ,16728
|
149
|
-
piccolo/query/methods/count.py,sha256=
|
149
|
+
piccolo/query/methods/count.py,sha256=vSwn52IG0wlhPC6L-jYVlCsD4BPb2EkGHGwWn7z7gH4,1717
|
150
150
|
piccolo/query/methods/create.py,sha256=hJ-6VVsWczzKDH6fQRN1WmYhcitixuXJ-eNOuCo_JgM,2742
|
151
151
|
piccolo/query/methods/create_index.py,sha256=RV9yVHwPvfQCk-g6YpmUTKamgOj0uxWe8Zr97YHIPGo,2216
|
152
152
|
piccolo/query/methods/delete.py,sha256=c4LO6-sGKfX-pi1nTZPC3aKvevgKWXuO28sFetbQ7WY,2212
|
@@ -157,7 +157,7 @@ piccolo/query/methods/insert.py,sha256=ygQQBHMEtZRpPDYKK9qv4mdJsCcSZOA0d5drwqb57
|
|
157
157
|
piccolo/query/methods/objects.py,sha256=i71GHPJZJcRpgM6e69Vk7vVcCuAFokOsMnR5EXbZq1w,11673
|
158
158
|
piccolo/query/methods/raw.py,sha256=VhYpCB52mZk4zqFTsqK5CHKTDGskUjISXTBV7UjohmA,600
|
159
159
|
piccolo/query/methods/refresh.py,sha256=P1Eo_HYU_L7kcGM_cvDDgyLi1boCXY7Pc4tv_eDAzvc,2769
|
160
|
-
piccolo/query/methods/select.py,sha256=
|
160
|
+
piccolo/query/methods/select.py,sha256=lkR46W2oatozkTgyKjACtpf8UVRZBISVAf607e2ljWs,26903
|
161
161
|
piccolo/query/methods/table_exists.py,sha256=0yb3n6Jd2ovSBWlZ-gl00K4E7Jnbj7J8qAAX5d7hvNk,1259
|
162
162
|
piccolo/query/methods/update.py,sha256=KJrdS98uvbio_95h979xGd6HTtn9d0LP7SqwPjBn7tU,3655
|
163
163
|
piccolo/testing/__init__.py,sha256=pRFSqRInfx95AakOq54atmvqoB-ue073q2aR8u8zR40,83
|
@@ -190,7 +190,7 @@ tests/apps/asgi/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
190
190
|
tests/apps/asgi/commands/test_new.py,sha256=CxlY2TGK-fOAPUroKK4CIXRyBwnsetAehAAIc4wheUE,3097
|
191
191
|
tests/apps/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
192
192
|
tests/apps/fixtures/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
193
|
-
tests/apps/fixtures/commands/test_dump_load.py,sha256=
|
193
|
+
tests/apps/fixtures/commands/test_dump_load.py,sha256=aS4as-Uve4W65c2hL5oWvxYwL4HJrsD5gaUBzmzqm_Y,9344
|
194
194
|
tests/apps/fixtures/commands/test_shared.py,sha256=z-0-lrkATlt98hJbu3udKne_DW7BT6p_iPZLjUuwyb4,2139
|
195
195
|
tests/apps/meta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
196
196
|
tests/apps/meta/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -204,7 +204,7 @@ tests/apps/migrations/auto/test_schema_differ.py,sha256=0P7TCUyggPuqmO5QnzoMu-u_
|
|
204
204
|
tests/apps/migrations/auto/test_schema_snapshot.py,sha256=ZyvGZqn3N3cwd-3S-FME5AJ8buDSHesw7yPIvY6mE5k,6196
|
205
205
|
tests/apps/migrations/auto/test_serialisation.py,sha256=EFkhES1w9h51UCamWrhxs3mf4I718ggeP7Yl5J_UID4,13548
|
206
206
|
tests/apps/migrations/auto/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
207
|
-
tests/apps/migrations/auto/integration/test_migrations.py,sha256=
|
207
|
+
tests/apps/migrations/auto/integration/test_migrations.py,sha256=9CM7PI79DS3CRjkXXiXcU5JhQR-CceTT2LfV4P-G7HI,42960
|
208
208
|
tests/apps/migrations/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
209
209
|
tests/apps/migrations/commands/test_base.py,sha256=NgHgVjNd3Hil9eODvW7Ic2D9muTa_grNaH3YpRFfR8I,1829
|
210
210
|
tests/apps/migrations/commands/test_check.py,sha256=hOX_sVk1nfCRfbQ8tJoFEUBFhih9O4QuQLHTp5TQaiY,630
|
@@ -245,7 +245,6 @@ tests/columns/test_double_precision.py,sha256=YuC-mVMvwYgtiBzZDIajf6HbLkb08OWs0N
|
|
245
245
|
tests/columns/test_interval.py,sha256=1kn0ufhAMe0vG7343rQyoQ2EDJ3o1ER8p3HCC1sSRhc,2921
|
246
246
|
tests/columns/test_json.py,sha256=T5v7GD6sOro8X2OB3Nwhw8-BbsmKmbD9w3o4c8bzv8M,3814
|
247
247
|
tests/columns/test_jsonb.py,sha256=YomSvGyLKfmXhJiFIE1StfDyPMBuXeEETZZcVQHbiwE,6821
|
248
|
-
tests/columns/test_m2m.py,sha256=-WB52gOti74t1RsJJhUyKD2TAsYvmYVjj9UylAJp5zI,25767
|
249
248
|
tests/columns/test_numeric.py,sha256=8h6LwYI_3Jer9R6w2Ew_xtH09JSPsDZ0ahVIXEKAnnM,813
|
250
249
|
tests/columns/test_primary_key.py,sha256=KrV5sbH7MOZk0Uu8WtK8Pi3KtmZRVEmSKloN-DVFoKs,5396
|
251
250
|
tests/columns/test_readable.py,sha256=kMuge5X_PfVeE1PjStwEHY-W0zwrxVBXV0CiW2Gw7kE,802
|
@@ -258,6 +257,10 @@ tests/columns/test_timestamp.py,sha256=q8idev7hO8TrFXeLb8ZVBFVBb3kqWck17s-RZP747
|
|
258
257
|
tests/columns/test_timestamptz.py,sha256=IvBC4S1MLkYRG6jOGn9631qmwKkg2lqftC_8i71zDcA,2771
|
259
258
|
tests/columns/test_uuid.py,sha256=HDh_qvznN7nUjCaJ-TJwRcPjMTYMLhrO_oOHtW9MAAc,466
|
260
259
|
tests/columns/test_varchar.py,sha256=5IY9J5m1Hmuq1vZ78aApwPYI7J-XZFVtZVHPhlKGn8Y,813
|
260
|
+
tests/columns/m2m/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
261
|
+
tests/columns/m2m/base.py,sha256=PgdTs4bFHNH5V7SgfhkOwE3ch2XH8J-AbX7yo0DO7J4,14145
|
262
|
+
tests/columns/m2m/test_m2m.py,sha256=XD20-MCci7T2gSc7loI1JScsYpItdiwacrOxBK5BP8s,13084
|
263
|
+
tests/columns/m2m/test_m2m_schema.py,sha256=wTsTOEaAnReECeNfjP_lRojuMDFy4-_wQbZDkdsmsYw,1184
|
261
264
|
tests/conf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
262
265
|
tests/conf/example.py,sha256=kOa8EUzxG-iP-BGJuJv3-mqyvr1vW7XLVMeuCXzypo8,346
|
263
266
|
tests/conf/test_apps.py,sha256=mqkkdEnFjphbrdRh9yKu4Red05-3l0fG2pOxYhT_9tU,7578
|
@@ -294,7 +297,7 @@ tests/table/test_alter.py,sha256=k_tXdiuG6grybRNKmXdt7PdwaENOsvoNX-lodB_hCF8,123
|
|
294
297
|
tests/table/test_batch.py,sha256=5XdwZfsQWhnWSEMVG_yetW-28yYTU6bBCZJGYjx4sOU,3740
|
295
298
|
tests/table/test_callback.py,sha256=mNp4NOQ4PM_PIKn-WLG4HyvN-YHk7w0Ka_Bljg5Ugd4,6381
|
296
299
|
tests/table/test_constructor.py,sha256=zPbzhKQWzzsQQUK7P9WM8OgCi1ndhXedP6rU0tg6XIM,832
|
297
|
-
tests/table/test_count.py,sha256=
|
300
|
+
tests/table/test_count.py,sha256=qm4dwlQJ5gv8FPSsgYTS-3Gsd_KLgvWlFnmXweKydxw,2297
|
298
301
|
tests/table/test_create.py,sha256=d-X7faDGI6NesvElu9JhrAo0Ialmrl6163Whd_LAUDQ,2887
|
299
302
|
tests/table/test_create_db_tables.py,sha256=9ZVlv9jGX5uneMfM5c2j1LlOphgorFNHN1cQ1pay4gM,903
|
300
303
|
tests/table/test_create_table_class.py,sha256=N56XnaixbiZlakAexdMOw6FI4XnJjy_ybVjpsyhgMQ0,1341
|
@@ -308,7 +311,7 @@ tests/table/test_insert.py,sha256=LeYNvApZ2T-PZ9fseINLZ6hnrY5F1Axe3QHqidwzbAQ,13
|
|
308
311
|
tests/table/test_join.py,sha256=tk2r5OUaay9-4U37aj2-qul1XybchBG3xr-k7K_IQh0,14705
|
309
312
|
tests/table/test_join_on.py,sha256=NhJRg_7_YQ0o2ox5mF330ZaIvmtq09Xl2lfDTwKtUng,2719
|
310
313
|
tests/table/test_metaclass.py,sha256=pimcSDH8GYcTTmrwEz7agezgZPyb6_aUTi2CqwsLqFY,3562
|
311
|
-
tests/table/test_objects.py,sha256=
|
314
|
+
tests/table/test_objects.py,sha256=kMvR-6vah0dU25lQSKlVJkjJOPX3Qd27d5mGKwmm1CY,7910
|
312
315
|
tests/table/test_output.py,sha256=BvALFil1VlWKPmlRiqlrhhiAVXcuj3E-Bg85JMqGNlQ,2984
|
313
316
|
tests/table/test_raw.py,sha256=9PTvYngQi41nYd5lKzkJdTqsEcwrdOXcvZjq-W26CwQ,1683
|
314
317
|
tests/table/test_ref.py,sha256=eYNRnYHzNMXuMbV3B1ca5EidpIg4500q6hr1ccuVaso,269
|
@@ -341,9 +344,9 @@ tests/utils/test_sql_values.py,sha256=vzxRmy16FfLZPH-sAQexBvsF9MXB8n4smr14qoEOS5
|
|
341
344
|
tests/utils/test_sync.py,sha256=9ytVo56y2vPQePvTeIi9lHIouEhWJbodl1TmzkGFrSo,799
|
342
345
|
tests/utils/test_table_reflection.py,sha256=SIzuat-IpcVj1GCFyOWKShI8YkhdOPPFH7qVrvfyPNE,3794
|
343
346
|
tests/utils/test_warnings.py,sha256=NvSC_cvJ6uZcwAGf1m-hLzETXCqprXELL8zg3TNLVMw,269
|
344
|
-
piccolo-0.
|
345
|
-
piccolo-0.
|
346
|
-
piccolo-0.
|
347
|
-
piccolo-0.
|
348
|
-
piccolo-0.
|
349
|
-
piccolo-0.
|
347
|
+
piccolo-0.115.0.dist-info/LICENSE,sha256=zFIpi-16uIJ420UMIG75NU0JbDBykvrdnXcj5U_EYBI,1059
|
348
|
+
piccolo-0.115.0.dist-info/METADATA,sha256=8DSsJLdSVNh-fxAo23bFxWrtJc9x-cwV9Pmqa9-7afQ,5113
|
349
|
+
piccolo-0.115.0.dist-info/WHEEL,sha256=00yskusixUoUt5ob_CiUp6LsnN5lqzTJpoqOFg_FVIc,92
|
350
|
+
piccolo-0.115.0.dist-info/entry_points.txt,sha256=zYhu-YNtMlh2N_8wptCS8YWKOgc81UPL3Ji5gly8ouc,47
|
351
|
+
piccolo-0.115.0.dist-info/top_level.txt,sha256=-SR74VGbk43VoPy1HH-mHm97yoGukLK87HE5kdBW6qM,24
|
352
|
+
piccolo-0.115.0.dist-info/RECORD,,
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import datetime
|
2
2
|
import decimal
|
3
|
+
import os
|
4
|
+
import tempfile
|
3
5
|
import typing as t
|
4
6
|
import uuid
|
5
7
|
from unittest import TestCase
|
@@ -8,7 +10,7 @@ from piccolo.apps.fixtures.commands.dump import (
|
|
8
10
|
FixtureConfig,
|
9
11
|
dump_to_json_string,
|
10
12
|
)
|
11
|
-
from piccolo.apps.fixtures.commands.load import load_json_string
|
13
|
+
from piccolo.apps.fixtures.commands.load import load, load_json_string
|
12
14
|
from piccolo.utils.sync import run_sync
|
13
15
|
from tests.base import engines_only
|
14
16
|
from tests.example_apps.mega.tables import MegaTable, SmallTable
|
@@ -240,3 +242,39 @@ class TestDumpLoad(TestCase):
|
|
240
242
|
"not_null_col": "hello",
|
241
243
|
},
|
242
244
|
)
|
245
|
+
|
246
|
+
|
247
|
+
class TestOnConflict(TestCase):
|
248
|
+
def setUp(self) -> None:
|
249
|
+
SmallTable.create_table().run_sync()
|
250
|
+
SmallTable({SmallTable.varchar_col: "Test"}).save().run_sync()
|
251
|
+
|
252
|
+
def tearDown(self) -> None:
|
253
|
+
SmallTable.alter().drop_table().run_sync()
|
254
|
+
|
255
|
+
def test_on_conflict(self):
|
256
|
+
temp_dir = tempfile.gettempdir()
|
257
|
+
|
258
|
+
json_file_path = os.path.join(temp_dir, "fixture.json")
|
259
|
+
|
260
|
+
json_string = run_sync(
|
261
|
+
dump_to_json_string(
|
262
|
+
fixture_configs=[
|
263
|
+
FixtureConfig(
|
264
|
+
app_name="mega",
|
265
|
+
table_class_names=["SmallTable"],
|
266
|
+
)
|
267
|
+
]
|
268
|
+
)
|
269
|
+
)
|
270
|
+
|
271
|
+
if os.path.exists(json_file_path):
|
272
|
+
os.unlink(json_file_path)
|
273
|
+
|
274
|
+
with open(json_file_path, "w") as f:
|
275
|
+
f.write(json_string)
|
276
|
+
|
277
|
+
run_sync(load(path=json_file_path, on_conflict="DO NOTHING"))
|
278
|
+
run_sync(load(path=json_file_path, on_conflict="DO UPDATE"))
|
279
|
+
run_sync(load(path=json_file_path, on_conflict="do nothing"))
|
280
|
+
run_sync(load(path=json_file_path, on_conflict="do update"))
|
@@ -1138,9 +1138,10 @@ class TestSchemas(MigrationTestCase):
|
|
1138
1138
|
).run_sync(),
|
1139
1139
|
)
|
1140
1140
|
|
1141
|
-
def
|
1141
|
+
def test_move_table_from_public_schema(self):
|
1142
1142
|
"""
|
1143
|
-
Make sure the auto migrations
|
1143
|
+
Make sure the auto migrations can move a table from the public schema
|
1144
|
+
to a different schema.
|
1144
1145
|
"""
|
1145
1146
|
self._test_migrations(
|
1146
1147
|
table_snapshots=[
|
@@ -1181,6 +1182,37 @@ class TestSchemas(MigrationTestCase):
|
|
1181
1182
|
self.schema_manager.list_schemas().run_sync(),
|
1182
1183
|
)
|
1183
1184
|
|
1185
|
+
def test_move_table_to_public_schema(self):
|
1186
|
+
"""
|
1187
|
+
Make sure the auto migrations can move a table from a schema to the
|
1188
|
+
public schema.
|
1189
|
+
"""
|
1190
|
+
self._test_migrations(
|
1191
|
+
table_snapshots=[
|
1192
|
+
[self.manager_2],
|
1193
|
+
[self.manager_1],
|
1194
|
+
],
|
1195
|
+
)
|
1196
|
+
|
1197
|
+
# Make sure that the table is in the public schema.
|
1198
|
+
self.assertIn(
|
1199
|
+
"manager",
|
1200
|
+
self.schema_manager.list_tables(schema_name="public").run_sync(),
|
1201
|
+
)
|
1202
|
+
|
1203
|
+
#######################################################################
|
1204
|
+
|
1205
|
+
# Reverse the last migration, which should move the table back to the
|
1206
|
+
# non-public schema.
|
1207
|
+
self._run_backwards(migration_id="1")
|
1208
|
+
|
1209
|
+
self.assertIn(
|
1210
|
+
"manager",
|
1211
|
+
self.schema_manager.list_tables(
|
1212
|
+
schema_name=self.new_schema
|
1213
|
+
).run_sync(),
|
1214
|
+
)
|
1215
|
+
|
1184
1216
|
|
1185
1217
|
@engines_only("postgres", "cockroach")
|
1186
1218
|
class TestSameTableName(MigrationTestCase):
|
File without changes
|