mirror of
https://github.com/PaiGramTeam/sqlmodel.git
synced 2025-02-07 02:49:19 +00:00
✨ Allow setting unique
in Field()
for a column (#83)
Co-authored-by: Raphael Gibson <raphael.araujo@estantemagica.com.br> Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
This commit is contained in:
parent
1ca288028c
commit
42b0e6eace
@ -61,6 +61,7 @@ class FieldInfo(PydanticFieldInfo):
|
|||||||
primary_key = kwargs.pop("primary_key", False)
|
primary_key = kwargs.pop("primary_key", False)
|
||||||
nullable = kwargs.pop("nullable", Undefined)
|
nullable = kwargs.pop("nullable", Undefined)
|
||||||
foreign_key = kwargs.pop("foreign_key", Undefined)
|
foreign_key = kwargs.pop("foreign_key", Undefined)
|
||||||
|
unique = kwargs.pop("unique", False)
|
||||||
index = kwargs.pop("index", Undefined)
|
index = kwargs.pop("index", Undefined)
|
||||||
sa_column = kwargs.pop("sa_column", Undefined)
|
sa_column = kwargs.pop("sa_column", Undefined)
|
||||||
sa_column_args = kwargs.pop("sa_column_args", Undefined)
|
sa_column_args = kwargs.pop("sa_column_args", Undefined)
|
||||||
@ -80,6 +81,7 @@ class FieldInfo(PydanticFieldInfo):
|
|||||||
self.primary_key = primary_key
|
self.primary_key = primary_key
|
||||||
self.nullable = nullable
|
self.nullable = nullable
|
||||||
self.foreign_key = foreign_key
|
self.foreign_key = foreign_key
|
||||||
|
self.unique = unique
|
||||||
self.index = index
|
self.index = index
|
||||||
self.sa_column = sa_column
|
self.sa_column = sa_column
|
||||||
self.sa_column_args = sa_column_args
|
self.sa_column_args = sa_column_args
|
||||||
@ -141,6 +143,7 @@ def Field(
|
|||||||
regex: Optional[str] = None,
|
regex: Optional[str] = None,
|
||||||
primary_key: bool = False,
|
primary_key: bool = False,
|
||||||
foreign_key: Optional[Any] = None,
|
foreign_key: Optional[Any] = None,
|
||||||
|
unique: bool = False,
|
||||||
nullable: Union[bool, UndefinedType] = Undefined,
|
nullable: Union[bool, UndefinedType] = Undefined,
|
||||||
index: Union[bool, UndefinedType] = Undefined,
|
index: Union[bool, UndefinedType] = Undefined,
|
||||||
sa_column: Union[Column, UndefinedType] = Undefined, # type: ignore
|
sa_column: Union[Column, UndefinedType] = Undefined, # type: ignore
|
||||||
@ -171,6 +174,7 @@ def Field(
|
|||||||
regex=regex,
|
regex=regex,
|
||||||
primary_key=primary_key,
|
primary_key=primary_key,
|
||||||
foreign_key=foreign_key,
|
foreign_key=foreign_key,
|
||||||
|
unique=unique,
|
||||||
nullable=nullable,
|
nullable=nullable,
|
||||||
index=index,
|
index=index,
|
||||||
sa_column=sa_column,
|
sa_column=sa_column,
|
||||||
@ -426,12 +430,14 @@ def get_column_from_field(field: ModelField) -> Column: # type: ignore
|
|||||||
nullable = not primary_key and _is_field_nullable(field)
|
nullable = not primary_key and _is_field_nullable(field)
|
||||||
args = []
|
args = []
|
||||||
foreign_key = getattr(field.field_info, "foreign_key", None)
|
foreign_key = getattr(field.field_info, "foreign_key", None)
|
||||||
|
unique = getattr(field.field_info, "unique", False)
|
||||||
if foreign_key:
|
if foreign_key:
|
||||||
args.append(ForeignKey(foreign_key))
|
args.append(ForeignKey(foreign_key))
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"primary_key": primary_key,
|
"primary_key": primary_key,
|
||||||
"nullable": nullable,
|
"nullable": nullable,
|
||||||
"index": index,
|
"index": index,
|
||||||
|
"unique": unique,
|
||||||
}
|
}
|
||||||
sa_default = Undefined
|
sa_default = Undefined
|
||||||
if field.field_info.default_factory:
|
if field.field_info.default_factory:
|
||||||
|
93
tests/test_main.py
Normal file
93
tests/test_main.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
from sqlmodel import Field, Session, SQLModel, create_engine
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_allow_duplicate_row_if_unique_constraint_is_not_passed(clear_sqlmodel):
|
||||||
|
class Hero(SQLModel, table=True):
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
name: str
|
||||||
|
secret_name: str
|
||||||
|
age: Optional[int] = None
|
||||||
|
|
||||||
|
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
|
||||||
|
hero_2 = Hero(name="Deadpond", secret_name="Dive Wilson")
|
||||||
|
|
||||||
|
engine = create_engine("sqlite://")
|
||||||
|
|
||||||
|
SQLModel.metadata.create_all(engine)
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
session.add(hero_1)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(hero_1)
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
session.add(hero_2)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(hero_2)
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
heroes = session.query(Hero).all()
|
||||||
|
assert len(heroes) == 2
|
||||||
|
assert heroes[0].name == heroes[1].name
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_allow_duplicate_row_if_unique_constraint_is_false(clear_sqlmodel):
|
||||||
|
class Hero(SQLModel, table=True):
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
name: str
|
||||||
|
secret_name: str = Field(unique=False)
|
||||||
|
age: Optional[int] = None
|
||||||
|
|
||||||
|
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
|
||||||
|
hero_2 = Hero(name="Deadpond", secret_name="Dive Wilson")
|
||||||
|
|
||||||
|
engine = create_engine("sqlite://")
|
||||||
|
|
||||||
|
SQLModel.metadata.create_all(engine)
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
session.add(hero_1)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(hero_1)
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
session.add(hero_2)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(hero_2)
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
heroes = session.query(Hero).all()
|
||||||
|
assert len(heroes) == 2
|
||||||
|
assert heroes[0].name == heroes[1].name
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_raise_exception_when_try_to_duplicate_row_if_unique_constraint_is_true(
|
||||||
|
clear_sqlmodel,
|
||||||
|
):
|
||||||
|
class Hero(SQLModel, table=True):
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
name: str
|
||||||
|
secret_name: str = Field(unique=True)
|
||||||
|
age: Optional[int] = None
|
||||||
|
|
||||||
|
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
|
||||||
|
hero_2 = Hero(name="Deadpond", secret_name="Dive Wilson")
|
||||||
|
|
||||||
|
engine = create_engine("sqlite://")
|
||||||
|
|
||||||
|
SQLModel.metadata.create_all(engine)
|
||||||
|
|
||||||
|
with Session(engine) as session:
|
||||||
|
session.add(hero_1)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(hero_1)
|
||||||
|
|
||||||
|
with pytest.raises(IntegrityError):
|
||||||
|
with Session(engine) as session:
|
||||||
|
session.add(hero_2)
|
||||||
|
session.commit()
|
||||||
|
session.refresh(hero_2)
|
Loading…
Reference in New Issue
Block a user