Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ormar/fields/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def construct_constraints(self) -> List:
con.reference,
ondelete=con.ondelete,
onupdate=con.onupdate,
name=f"fk_{self.owner.Meta.tablename}_{self.to.Meta.tablename}"
name=con.name or f"fk_{self.owner.Meta.tablename}_{self.to.Meta.tablename}"
f"_{self.to.get_column_alias(self.to.Meta.pkname)}_{self.name}",
)
for con in self.constraints
Expand Down
24 changes: 16 additions & 8 deletions ormar/fields/foreign_key.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import string
import sys
import uuid
import string, sys, uuid, sqlalchemy
from dataclasses import dataclass
from random import choices
from typing import (
Expand All @@ -15,7 +13,6 @@
overload,
)

import sqlalchemy
from pydantic import BaseModel, create_model
from pydantic.typing import ForwardRef, evaluate_forwardref

Expand Down Expand Up @@ -93,7 +90,11 @@ def create_dummy_model(


def populate_fk_params_based_on_to_model(
to: Type["T"], nullable: bool, onupdate: str = None, ondelete: str = None
to: Type["T"],
nullable: bool,
onupdate: str = None,
ondelete: str = None,
fk_name: str = None,
) -> Tuple[Any, List, Any]:
"""
Based on target to model to which relation leads to populates the type of the
Expand All @@ -112,7 +113,7 @@ def populate_fk_params_based_on_to_model(
:return: tuple with target pydantic type, list of fk constraints and target col type
:rtype: Tuple[Any, List, Any]
"""
fk_string = to.Meta.tablename + "." + to.get_column_alias(to.Meta.pkname)
fk_string = f"{to.Meta.tablename}.{to.get_column_alias(to.Meta.pkname)}"
to_field = to.Meta.model_fields[to.Meta.pkname]
pk_only_model = create_dummy_model(to, to_field)
__type__ = (
Expand All @@ -122,7 +123,7 @@ def populate_fk_params_based_on_to_model(
)
constraints = [
ForeignKeyConstraint(
reference=fk_string, ondelete=ondelete, onupdate=onupdate, name=None
reference=fk_string, ondelete=ondelete, onupdate=onupdate, name=fk_name
)
]
column_type = to_field.column_type
Expand Down Expand Up @@ -214,6 +215,7 @@ def ForeignKey( # type: ignore # noqa CFQ002
virtual: bool = False,
onupdate: Union[ReferentialAction, str] = None,
ondelete: Union[ReferentialAction, str] = None,
fk_name: str = None,
**kwargs: Any,
) -> "T":
"""
Expand Down Expand Up @@ -241,6 +243,8 @@ def ForeignKey( # type: ignore # noqa CFQ002
:param ondelete: parameter passed to sqlalchemy.ForeignKey.
How to treat child rows on delete of parent (the one where FK is defined) model.
:type ondelete: Union[ReferentialAction, str]
:param fk_name: if fk_name is specified, it overrides foreign key constraint name which is generated automatically in migrations
:type fk_name: str
:param kwargs: all other args to be populated by BaseField
:type kwargs: Any
:return: ormar ForeignKeyField with relation to selected model
Expand All @@ -265,14 +269,15 @@ def ForeignKey( # type: ignore # noqa CFQ002
validate_not_allowed_fields(kwargs)

if to.__class__ == ForwardRef:
__type__ = to if not nullable else Optional[to]
__type__ = Optional[to] if nullable else to
constraints: List = []
column_type = None
else:
__type__, constraints, column_type = populate_fk_params_based_on_to_model(
to=to, # type: ignore
nullable=nullable,
ondelete=ondelete,
fk_name=fk_name,
onupdate=onupdate,
)

Expand All @@ -282,6 +287,7 @@ def ForeignKey( # type: ignore # noqa CFQ002
through=None,
alias=name,
name=kwargs.pop("real_name", None),
fk_name=fk_name,
nullable=nullable,
sql_nullable=sql_nullable,
constraints=constraints,
Expand Down Expand Up @@ -320,6 +326,7 @@ def __init__(self, **kwargs: Any) -> None:
self.to: Type["Model"]
self.ondelete: str = kwargs.pop("ondelete", None)
self.onupdate: str = kwargs.pop("onupdate", None)
self.fk_name: str = kwargs.pop("fk_name", None)
super().__init__(**kwargs)

def get_source_related_name(self) -> str:
Expand Down Expand Up @@ -383,6 +390,7 @@ def evaluate_forward_ref(self, globalns: Any, localns: Any) -> None:
nullable=self.nullable,
ondelete=self.ondelete,
onupdate=self.onupdate,
fk_name=self.fk_name,
)

def _extract_model_from_sequence(
Expand Down
4 changes: 4 additions & 0 deletions ormar/fields/many_to_many.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ def ManyToMany( # type: ignore
skip_reverse = kwargs.pop("skip_reverse", False)
skip_field = kwargs.pop("skip_field", False)

through_relation_fk_name = kwargs.pop("through_relation_fk_name", None)
through_reverse_relation_fk_name = kwargs.pop("through_reverse_relation_fk_name", None)
through_relation_name = kwargs.pop("through_relation_name", None)
through_reverse_relation_name = kwargs.pop("through_reverse_relation_name", None)

Expand Down Expand Up @@ -165,6 +167,8 @@ def ManyToMany( # type: ignore
related_orders_by=related_orders_by,
skip_reverse=skip_reverse,
skip_field=skip_field,
through_reverse_relation_fk_name=through_reverse_relation_fk_name,
through_relation_fk_name=through_relation_fk_name,
through_relation_name=through_relation_name,
through_reverse_relation_name=through_reverse_relation_name,
)
Expand Down
8 changes: 4 additions & 4 deletions ormar/models/helpers/sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ def adjust_through_many_to_many_model(model_field: "ManyToManyField") -> None:
)

create_and_append_m2m_fk(
model=model_field.to, model_field=model_field, field_name=parent_name
model=model_field.to, model_field=model_field, field_name=parent_name, fk_name=model_field.through_reverse_relation_fk_name,
)
create_and_append_m2m_fk(
model=model_field.owner, model_field=model_field, field_name=child_name
model=model_field.owner, model_field=model_field, field_name=child_name, fk_name=model_field.through_relation_fk_name
)

create_pydantic_field(parent_name, model_field.to, model_field)
Expand All @@ -58,7 +58,7 @@ def adjust_through_many_to_many_model(model_field: "ManyToManyField") -> None:


def create_and_append_m2m_fk(
model: Type["Model"], model_field: "ManyToManyField", field_name: str
model: Type["Model"], model_field: "ManyToManyField", field_name: str, fk_name: str = None
) -> None:
"""
Registers sqlalchemy Column with sqlalchemy.ForeignKey leading to the model.
Expand All @@ -85,7 +85,7 @@ def create_and_append_m2m_fk(
model.Meta.tablename + "." + pk_alias,
ondelete="CASCADE",
onupdate="CASCADE",
name=f"fk_{model_field.through.Meta.tablename}_{model.Meta.tablename}"
name=fk_name or f"fk_{model_field.through.Meta.tablename}_{model.Meta.tablename}"
f"_{field_name}_{pk_alias}",
),
)
Expand Down