1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
from __future__ import annotations
from typing import Final
from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.associationproxy import AssociationProxy
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "user"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(64))
kw: Mapped[list[Keyword]] = relationship(
secondary=lambda: user_keyword_table
)
def __init__(self, name: str):
self.name = name
# proxy the 'keyword' attribute from the 'kw' relationship
keywords: AssociationProxy[list[str]] = association_proxy("kw", "keyword")
class Keyword(Base):
__tablename__ = "keyword"
id: Mapped[int] = mapped_column(primary_key=True)
keyword: Mapped[str] = mapped_column(String(64))
def __init__(self, keyword: str):
self.keyword = keyword
user_keyword_table: Final[Table] = Table(
"user_keyword",
Base.metadata,
Column("user_id", Integer, ForeignKey("user.id"), primary_key=True),
Column("keyword_id", Integer, ForeignKey("keyword.id"), primary_key=True),
)
user = User("jek")
# EXPECTED_TYPE: list[Keyword]
reveal_type(user.kw)
user.kw.append(Keyword("cheese-inspector"))
user.keywords.append("cheese-inspector")
# EXPECTED_TYPE: list[str]
reveal_type(user.keywords)
user.keywords.append("snack ninja")
|