diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b111c9525..c14473a2f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -21,6 +21,7 @@ Added Fixed ^^^^^ - ``MigrationRecorder`` now uses parameterized queries; fixes MariaDB/MySQL rejecting ISO-8601 ``applied_at`` values. (#2132) +- Fix migration ``deconstruct()`` and ``CreateModel`` serialization for PK fields with ``generated=False`` or ``source_field``. (#2195) 1.1.7 ----- diff --git a/tests/fields/test_db_default.py b/tests/fields/test_db_default.py index 718933f34..83d84e3ff 100644 --- a/tests/fields/test_db_default.py +++ b/tests/fields/test_db_default.py @@ -213,6 +213,29 @@ def test_deconstruct_with_both_default_and_db_default(): assert kwargs["db_default"] == 2 +def test_deconstruct_pk_generated_false(): + f = fields.IntField(primary_key=True, generated=False) + f.model_field_name = "test_field" + path, args, kwargs = f.deconstruct() + assert kwargs.get("generated") is False + assert kwargs.get("primary_key") is True + + +def test_deconstruct_pk_generated_default(): + f = fields.IntField(primary_key=True) + f.model_field_name = "test_field" + path, args, kwargs = f.deconstruct() + assert kwargs.get("generated") is True + assert kwargs.get("primary_key") is True + + +def test_deconstruct_non_pk_generated_false(): + f = fields.IntField(generated=False) + f.model_field_name = "test_field" + path, args, kwargs = f.deconstruct() + assert "generated" not in kwargs + + # ============================================================================ # Sentinel behavior # ============================================================================ diff --git a/tests/migrations/test_writer.py b/tests/migrations/test_writer.py index 248d679a8..f5166d363 100644 --- a/tests/migrations/test_writer.py +++ b/tests/migrations/test_writer.py @@ -292,6 +292,37 @@ class Migration(migrations.Migration): _write_migration(tmp_path, monkeypatch, "0005_fk_source", operations, expected) +def test_writer_includes_pk_with_source_field(tmp_path: Path, monkeypatch) -> None: + operations = [ + CreateModel( + name="Widget", + fields=[ + ("id", fields.IntField(primary_key=True, source_field="id")), + ("name", fields.CharField(max_length=100)), + ], + ) + ] + expected = textwrap.dedent( + """\ + from tortoise import migrations + from tortoise.migrations import operations as ops + from tortoise import fields + + class Migration(migrations.Migration): + operations = [ + ops.CreateModel( + name='Widget', + fields=[ + ('id', fields.IntField(source_field='id', generated=True, primary_key=True, unique=True, db_index=True)), + ('name', fields.CharField(max_length=100)), + ], + ), + ] + """ + ) + _write_migration(tmp_path, monkeypatch, "0011_pk_source", operations, expected) + + def test_writer_serializes_on_delete_enum(tmp_path: Path, monkeypatch) -> None: operations = [ CreateModel( diff --git a/tortoise/fields/base.py b/tortoise/fields/base.py index 9f947e6f8..e78b9438e 100644 --- a/tortoise/fields/base.py +++ b/tortoise/fields/base.py @@ -551,7 +551,7 @@ def deconstruct(self) -> tuple[str, list[Any], dict[str, Any]]: kwargs: dict[str, Any] = {} if self.source_field: kwargs["source_field"] = self.source_field - if self.generated: + if self.generated or self.pk: kwargs["generated"] = self.generated if self.pk: kwargs["primary_key"] = self.pk diff --git a/tortoise/migrations/writer.py b/tortoise/migrations/writer.py index d99332a9f..0b12f595e 100644 --- a/tortoise/migrations/writer.py +++ b/tortoise/migrations/writer.py @@ -484,7 +484,7 @@ def _format_create_model( for name, field in operation.fields: if field is None: continue - if name in source_fields: + if name in source_fields and not getattr(field, "pk", False): continue field_expr = self._render_field(field, imports) field_lines.append(f"{indent} ({name!r}, {field_expr}),")