plain.models 0.35.0__py3-none-any.whl → 0.36.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.
plain/models/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # plain-models changelog
2
2
 
3
+ ## [0.36.0](https://github.com/dropseed/plain/releases/plain-models@0.36.0) (2025-07-18)
4
+
5
+ ### What's changed
6
+
7
+ - Removed the `--merge` option from the `makemigrations` command ([d366663](https://github.com/dropseed/plain/commit/d366663))
8
+ - Improved error handling in the `restore-backup` command using Click's error system ([88f06c5](https://github.com/dropseed/plain/commit/88f06c5))
9
+
10
+ ### Upgrade instructions
11
+
12
+ - No changes required
13
+
3
14
  ## [0.35.0](https://github.com/dropseed/plain/releases/plain-models@0.35.0) (2025-07-07)
4
15
 
5
16
  ### What's changed
@@ -66,12 +66,10 @@ def restore_backup(backup_name, latest, pg_restore):
66
66
  backups_handler = DatabaseBackups()
67
67
 
68
68
  if backup_name and latest:
69
- click.secho("Only one of --latest or backup_name is allowed", fg="red")
70
- exit(1)
69
+ raise click.UsageError("Only one of --latest or backup_name is allowed")
71
70
 
72
71
  if not backup_name and not latest:
73
- click.secho("Backup name or --latest is required", fg="red")
74
- exit(1)
72
+ raise click.UsageError("Backup name or --latest is required")
75
73
 
76
74
  if not backup_name and latest:
77
75
  backup_name = backups_handler.find_backups()[0].name
plain/models/cli.py CHANGED
@@ -2,7 +2,6 @@ import os
2
2
  import subprocess
3
3
  import sys
4
4
  import time
5
- from itertools import takewhile
6
5
 
7
6
  import click
8
7
 
@@ -22,12 +21,10 @@ from .migrations.migration import Migration, SettingsTuple
22
21
  from .migrations.optimizer import MigrationOptimizer
23
22
  from .migrations.questioner import (
24
23
  InteractiveMigrationQuestioner,
25
- MigrationQuestioner,
26
24
  NonInteractiveMigrationQuestioner,
27
25
  )
28
26
  from .migrations.recorder import MigrationRecorder
29
27
  from .migrations.state import ModelState, ProjectState
30
- from .migrations.utils import get_migration_name_timestamp
31
28
  from .migrations.writer import MigrationWriter
32
29
  from .registry import models_registry
33
30
 
@@ -92,7 +89,7 @@ def db_wait():
92
89
  )
93
90
  time.sleep(1.5)
94
91
  else:
95
- click.secho("Database ready", fg="green")
92
+ click.secho("Database ready", fg="green")
96
93
  break
97
94
 
98
95
 
@@ -135,7 +132,6 @@ def list_models(package_labels, app_only):
135
132
  is_flag=True,
136
133
  help="Just show what migrations would be made; don't actually write them.",
137
134
  )
138
- @click.option("--merge", is_flag=True, help="Enable fixing of migration conflicts.")
139
135
  @click.option("--empty", is_flag=True, help="Create an empty migration.")
140
136
  @click.option(
141
137
  "--noinput",
@@ -157,9 +153,7 @@ def list_models(package_labels, app_only):
157
153
  default=1,
158
154
  help="Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output",
159
155
  )
160
- def makemigrations(
161
- package_labels, dry_run, merge, empty, no_input, name, check, verbosity
162
- ):
156
+ def makemigrations(package_labels, dry_run, empty, no_input, name, check, verbosity):
163
157
  """Creates new migration(s) for packages."""
164
158
 
165
159
  written_files = []
@@ -226,103 +220,6 @@ def makemigrations(
226
220
  )
227
221
  log(writer.as_string(), level=3)
228
222
 
229
- def handle_merge(loader, conflicts):
230
- """Handle merging conflicting migrations."""
231
- if interactive:
232
- questioner = InteractiveMigrationQuestioner()
233
- else:
234
- questioner = MigrationQuestioner(defaults={"ask_merge": True})
235
-
236
- for package_label, migration_names in conflicts.items():
237
- log(click.style(f"Merging {package_label}", fg="cyan", bold=True), level=1)
238
-
239
- merge_migrations = []
240
- for migration_name in migration_names:
241
- migration = loader.get_migration(package_label, migration_name)
242
- migration.ancestry = [
243
- mig
244
- for mig in loader.graph.forwards_plan(
245
- (package_label, migration_name)
246
- )
247
- if mig[0] == migration.package_label
248
- ]
249
- merge_migrations.append(migration)
250
-
251
- def all_items_equal(seq):
252
- return all(item == seq[0] for item in seq[1:])
253
-
254
- merge_migrations_generations = zip(*(m.ancestry for m in merge_migrations))
255
- common_ancestor_count = sum(
256
- 1 for _ in takewhile(all_items_equal, merge_migrations_generations)
257
- )
258
- if not common_ancestor_count:
259
- raise ValueError(f"Could not find common ancestor of {migration_names}")
260
-
261
- for migration in merge_migrations:
262
- migration.branch = migration.ancestry[common_ancestor_count:]
263
- migrations_ops = (
264
- loader.get_migration(node_package, node_name).operations
265
- for node_package, node_name in migration.branch
266
- )
267
- migration.merged_operations = sum(migrations_ops, [])
268
-
269
- for migration in merge_migrations:
270
- log(click.style(f" Branch {migration.name}", fg="yellow"), level=1)
271
- for operation in migration.merged_operations:
272
- log(f" - {operation.describe()}", level=1)
273
-
274
- if questioner.ask_merge(package_label):
275
- numbers = [
276
- MigrationAutodetector.parse_number(migration.name)
277
- for migration in merge_migrations
278
- ]
279
- biggest_number = (
280
- max(x for x in numbers if x is not None) if numbers else 0
281
- )
282
-
283
- subclass = type(
284
- "Migration",
285
- (Migration,),
286
- {
287
- "dependencies": [
288
- (package_label, migration.name)
289
- for migration in merge_migrations
290
- ],
291
- },
292
- )
293
-
294
- parts = [f"{biggest_number + 1:04d}"]
295
- if migration_name:
296
- parts.append(migration_name)
297
- else:
298
- parts.append("merge")
299
- leaf_names = "_".join(
300
- sorted(migration.name for migration in merge_migrations)
301
- )
302
- if len(leaf_names) > 47:
303
- parts.append(get_migration_name_timestamp())
304
- else:
305
- parts.append(leaf_names)
306
-
307
- new_migration_name = "_".join(parts)
308
- new_migration = subclass(new_migration_name, package_label)
309
- writer = MigrationWriter(new_migration)
310
-
311
- if not dry_run:
312
- with open(writer.path, "w", encoding="utf-8") as fh:
313
- fh.write(writer.as_string())
314
- log(f"\nCreated new merge migration {writer.path}", level=1)
315
- elif verbosity == 3:
316
- log(
317
- click.style(
318
- f"Full merge migrations file '{writer.filename}':",
319
- fg="cyan",
320
- bold=True,
321
- ),
322
- level=3,
323
- )
324
- log(writer.as_string(), level=3)
325
-
326
223
  # Validate package labels
327
224
  package_labels = set(package_labels)
328
225
  has_bad_labels = False
@@ -351,21 +248,16 @@ def makemigrations(
351
248
  if package_label in package_labels
352
249
  }
353
250
 
354
- if conflicts and not merge:
251
+ if conflicts:
355
252
  name_str = "; ".join(
356
253
  "{} in {}".format(", ".join(names), package)
357
254
  for package, names in conflicts.items()
358
255
  )
359
256
  raise click.ClickException(
360
257
  f"Conflicting migrations detected; multiple leaf nodes in the "
361
- f"migration graph: ({name_str}).\nTo fix them run "
362
- f"'python manage.py makemigrations --merge'"
258
+ f"migration graph: ({name_str})."
363
259
  )
364
260
 
365
- # Handle merge if requested
366
- if merge and conflicts:
367
- return handle_merge(loader, conflicts)
368
-
369
261
  # Set up questioner
370
262
  if interactive:
371
263
  questioner = InteractiveMigrationQuestioner(
@@ -533,8 +425,7 @@ def migrate(
533
425
  )
534
426
  raise click.ClickException(
535
427
  "Conflicting migrations detected; multiple leaf nodes in the "
536
- f"migration graph: ({name_str}).\nTo fix them run "
537
- "'python manage.py makemigrations --merge'"
428
+ f"migration graph: ({name_str})."
538
429
  )
539
430
 
540
431
  # If they supplied command line arguments, work out what they mean.
@@ -74,10 +74,6 @@ class MigrationQuestioner:
74
74
  """Was this model really renamed?"""
75
75
  return self.defaults.get("ask_rename_model", False)
76
76
 
77
- def ask_merge(self, package_label):
78
- """Should these migrations really be merged?"""
79
- return self.defaults.get("ask_merge", False)
80
-
81
77
  def ask_auto_now_add_addition(self, field_name, model_name):
82
78
  """Adding an auto_now_add field to a model."""
83
79
  # None means quit
@@ -227,16 +223,6 @@ class InteractiveMigrationQuestioner(MigrationQuestioner):
227
223
  default=False,
228
224
  )
229
225
 
230
- def ask_merge(self, package_label):
231
- return self._boolean_input(
232
- (
233
- "\nMerging will only work if the operations printed above do not conflict\n"
234
- "with each other (working on different fields or models)\n"
235
- "Should these migration branches be merged?"
236
- ),
237
- default=False,
238
- )
239
-
240
226
  def ask_auto_now_add_addition(self, field_name, model_name):
241
227
  """Adding an auto_now_add field to a model."""
242
228
  if not self.dry_run:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.models
3
- Version: 0.35.0
3
+ Version: 0.36.0
4
4
  Summary: Database models for Plain.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-File: LICENSE
@@ -1,9 +1,9 @@
1
- plain/models/CHANGELOG.md,sha256=bUinpgx_HxNjgItauzHyszRtwU7F3Zm33u0mMAZBoDs,4127
1
+ plain/models/CHANGELOG.md,sha256=eZWX8ouq5qD7hAMQVKAc7yv8EPI3HQJdvZlmwtDzcrA,4561
2
2
  plain/models/README.md,sha256=vsZPev3Fna-Irdcs3-wrOcAoII5LOhXdcWLBMT87CS0,1626
3
3
  plain/models/__init__.py,sha256=dnU6MOXs3lGoK31nLWjCqbf7zigkaUccomchz9lNDJ8,2950
4
4
  plain/models/aggregates.py,sha256=P0mhsMl1VZt2CVHMuCHnNI8SxZ9citjDLEgioN6NOpo,7240
5
5
  plain/models/base.py,sha256=8-F9DvSF5s2_zoGyEmZU5xg9GdEigthVT0HENv9iigI,67952
6
- plain/models/cli.py,sha256=4iZRHRPXxUCrsW-SzeimIE_lrSkWkLhgYOSk4BxUF_8,44985
6
+ plain/models/cli.py,sha256=f303FCV4HWJMVHCW-bK3CUOblXblpCknGjgvgnnbs-U,40409
7
7
  plain/models/config.py,sha256=OF7eIEtXNZyGwgc3eMEpb5uEAup5RXeT-0um60dfBeU,636
8
8
  plain/models/connections.py,sha256=RBNa2FZ0x3C9un6PaYL-IYzH_OesRSpdHNGKvYHGiOM,2276
9
9
  plain/models/constants.py,sha256=ndnj9TOTKW0p4YcIPLOLEbsH6mOgFi6B1-rIzr_iwwU,210
@@ -67,7 +67,7 @@ plain/models/backends/sqlite3/introspection.py,sha256=RMzb8RUfNUvrcbcmgIKjxwp5D2
67
67
  plain/models/backends/sqlite3/operations.py,sha256=K2PFCsvzGhBrPEzz1AXFenge2B4Ap8lsQAABsC4e_UI,15302
68
68
  plain/models/backends/sqlite3/schema.py,sha256=sbtI0PBGe0fK0IOFWh0bkpVntCMMVsfzPb9dpL7o4r8,22566
69
69
  plain/models/backups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
- plain/models/backups/cli.py,sha256=du_751qWPtJT7C3GF_nLfHucMoUzlUOz9OKUwqHQ16s,2990
70
+ plain/models/backups/cli.py,sha256=AKVh_Go0LxnxFi2jQjmpPEbbJvYCurjJsGCWESm1X8A,2960
71
71
  plain/models/backups/clients.py,sha256=WQ4X4KrkNeiKYJcpGRwnfRiAeeyduHWEnq4tA4mtjvw,4047
72
72
  plain/models/backups/core.py,sha256=09IZUhBEe1Yej3PC8AidtkaI0c8tt7VnqGBCWK-WrFg,3318
73
73
  plain/models/fields/__init__.py,sha256=b0qggzhZf89cXSwokr6rikWyF6kKPTuceIT3pI1Rz3s,80093
@@ -92,7 +92,7 @@ plain/models/migrations/graph.py,sha256=nrztu_8dU0wAUSxKUqqFWpvZcSQxGEqE6dXWkPyt
92
92
  plain/models/migrations/loader.py,sha256=qUTmaEYI1_mV6goQPQYZKjSz8rMbE6G1wqvrAsmuGwA,16464
93
93
  plain/models/migrations/migration.py,sha256=22YwRHnaRnCkBpW5p7K89tAU6h4QSsG5yiq-o7W-cSI,6505
94
94
  plain/models/migrations/optimizer.py,sha256=HH-uz-jnWw_Ni6F2_rRW1nax1Dxmf1s_F_8s8N2OlVc,3266
95
- plain/models/migrations/questioner.py,sha256=Q0JSiumYYGHkv4O8LFWioNvtJZa0J2JHIvf0NX7eSZM,12490
95
+ plain/models/migrations/questioner.py,sha256=qAsePI5JHiSJrlY_kmpgMuK9Dom22q17edov7RtBeGw,11967
96
96
  plain/models/migrations/recorder.py,sha256=9UfNdzM5meeOXSxMNxTN69uqX0K2uh5Ma7dNA2fL2rI,3655
97
97
  plain/models/migrations/serializer.py,sha256=IRO1-8ojalOFt27DSfCDhRWjTULQZQ-aSroL-A9MPbk,12947
98
98
  plain/models/migrations/state.py,sha256=Xb09rIdh3D-LVx3gPgaJ2mcXDL6WWW92gq6coJsjycM,35476
@@ -113,8 +113,8 @@ plain/models/sql/where.py,sha256=ezE9Clt2BmKo-I7ARsgqZ_aVA-1UdayCwr6ULSWZL6c,126
113
113
  plain/models/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
114
  plain/models/test/pytest.py,sha256=5mxY1MHqfCFJ7G39W1DuE0vCHfsfehQQmOyE6vI6caw,3323
115
115
  plain/models/test/utils.py,sha256=MxBNWoGMtwAtka7LbxWgilgzv7T5qqJL8ystF2SDJrs,345
116
- plain_models-0.35.0.dist-info/METADATA,sha256=v8A_EGrAuBGpAa_7thYuC1eim5Alt7jdfLskHWfUerg,1921
117
- plain_models-0.35.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
118
- plain_models-0.35.0.dist-info/entry_points.txt,sha256=IYJAW9MpL3PXyXFWmKmALagAGXC_5rzBn2eEGJlcV04,112
119
- plain_models-0.35.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
120
- plain_models-0.35.0.dist-info/RECORD,,
116
+ plain_models-0.36.0.dist-info/METADATA,sha256=Pm1uJVIOrhVQl1t-vEBk0jHD0NYU3mEhieE1_m9TX-Q,1921
117
+ plain_models-0.36.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
118
+ plain_models-0.36.0.dist-info/entry_points.txt,sha256=IYJAW9MpL3PXyXFWmKmALagAGXC_5rzBn2eEGJlcV04,112
119
+ plain_models-0.36.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
120
+ plain_models-0.36.0.dist-info/RECORD,,