Skip to content

FK/O2O db_default is dropped during _init_relations (CREATE TABLE omits DEFAULT on FK columns) #2199

@mixartemev

Description

@mixartemev

Describe the bug

When db_default is set on a ForeignKeyField / OneToOneField, the value is silently dropped at apply time and never reaches the generated DDL. The CREATE TABLE for the owning model is emitted without a DEFAULT clause on the FK column, even though the migration file faithfully records db_default=... on the relation.

Note: #2117 / PR #2129 fixed this for regular fields (the schema editor now emits DEFAULT for non-relation columns). This issue is the FK twin of that bug, in a different code path.

Root cause

Apps._init_relations (tortoise/apps.py) builds the implicit key field (<fk>_id) by copying the related model's PK field and then patching a small set of attributes from the FK object:

key_fk_object = copy(related_field)
key_fk_object.reference = fk_object
key_fk_object.source_field = fk_object.source_field or key_field
for attr in ("index", "default", "null", "generated", "description"):
    setattr(key_fk_object, attr, getattr(fk_object, attr))

db_default is not in this list. Since the schema editor iterates fields_db_projection (which contains the key field, not the relation field) and inspects key_field.has_db_default(), the DEFAULT clause is never produced for FK columns.

To Reproduce

from tortoise import fields, Model

class Dc(Model):
    id = fields.IntField(pk=True)

class App(Model):
    id = fields.IntField(pk=True)
    dc = fields.ForeignKeyField("models.Dc", db_default=2)

makemigrations produces an op that records db_default=2 on the FK:

('dc', fields.ForeignKeyField('models.Dc', source_field='dc_id',
                              db_default=2, related_name='apps',
                              on_delete=OnDelete.CASCADE)),

But tortoise upgrade emits:

CREATE TABLE "app" (
    "id" SERIAL NOT NULL PRIMARY KEY,
    "dc_id" INT NOT NULL REFERENCES "dc" ("id") ON DELETE CASCADE
);

— no DEFAULT 2.

Expected behavior

CREATE TABLE "app" (
    "id" SERIAL NOT NULL PRIMARY KEY,
    "dc_id" INT NOT NULL DEFAULT 2 REFERENCES "dc" ("id") ON DELETE CASCADE
);

i.e. db_default on FK/O2O propagates to the underlying column just like it does for plain fields after #2129.

Additional context

  • Confirmed present in 1.1.7 (release) and develop (HEAD as of today).
  • Fix is a one-line change in _init_relations: add "db_default" to the attribute-copy tuple. Tests can mirror test_create_model_includes_db_default from PR Fix CreateModel not including DEFAULT clause for db_default fields #2129, but with a ForeignKeyField(..., db_default=<id>) instead of a plain field.
  • Same path covers OneToOneField since it shares the init_fk_o2o_field codepath.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions