1212
1313
1414class SneakerBase (SQLModel ):
15- brand : str | None = None
15+ id : int | None = Field ( default = None , primary_key = True )
1616 sku : str | None = None
17+ brand : str | None = None
1718 parent_sku : str | None = None
1819 name : str | None = None
1920 colorway : str | None = None
2021 audience : Audience | None = None
2122 release_date : datetime | None = None
2223 description : str | None = None
23- retail_price : int | None = None # Monetary values stored as US cents
24+ retail_price : int | None = None
2425
2526
2627class Sneaker (SneakerBase , TimestampedModel , table = True ):
2728 __table_args__ = (
2829 UniqueConstraint ("sku" , "brand" , name = "uix_sneaker_sku_brand" ),
2930 Index ("ix_sneaker_sku_brand" , "sku" , "brand" ),
3031 )
31- id : int | None = Field (default = None , primary_key = True )
3232 last_observed : datetime | None = None
3333 stockx_id : str | None = None
3434 stadium_goods_id : str | None = None
3535 source : Platform | None = None
3636 meta : dict = Field (sa_type = JSONB , default_factory = dict )
3737
3838 # Relationships
39- links : list ["Link" ] = Relationship (back_populates = "sneaker" , cascade_delete = True )
40- images : list ["Image" ] = Relationship (back_populates = "sneaker" , cascade_delete = True )
41- sizes : list ["SneakerSize" ] = Relationship (
39+ sneaker_links : list ["Link" ] = Relationship (
40+ back_populates = "sneaker" , cascade_delete = True
41+ )
42+ sneaker_images : list ["Image" ] = Relationship (
43+ back_populates = "sneaker" , cascade_delete = True
44+ )
45+ sneaker_sizes : list ["SneakerSize" ] = Relationship (
4246 back_populates = "sneaker" ,
4347 cascade_delete = True ,
4448 )
4549 nike_launches : list ["NikeLaunch" ] = Relationship (
4650 back_populates = "sneaker" , cascade_delete = True
4751 )
4852
49- def get_links (self ) -> list [str ]:
50- return [link .url for link in self .links ]
51-
52- def get_images (self ) -> list [str ]:
53- return [image .url for image in sorted (self .images , key = lambda i : i .position )]
54-
55- def get_sizes (
56- self ,
57- size_standard : SizeStandard = SizeStandard .MENS_US ,
58- ) -> list [str ]:
59- return [size .get_standardized (size_standard ) for size in self .sizes ]
60-
61- @property
62- def prices (self ) -> list ["Price" ]:
63- return list (reduce (lambda x , y : x .prices + y .prices , self .sizes , []))
64-
65- def get_prices (self ) -> list [int ]:
66- return [price .amount for price in self .prices ]
67-
68- def merge (self , other = None ):
69- if other :
70- stockx_images = [
71- img for img in other .images if img .platform == Platform .stockx
72- ]
73- if stockx_images :
74- self .images = stockx_images
75-
76- if len (other .colorway ) > len (self .colorway ):
77- self .colorway = other .colorway
78-
7953
8054class SneakerPublic (SneakerBase ):
81- id : int
55+ sneaker_links : list ["Link" ] = Field (default_factory = list , exclude = True )
56+ sneaker_images : list ["Image" ] = Field (default_factory = list , exclude = True )
57+
58+ sneaker_sizes : list ["SneakerSizePublic" ] = Field (
59+ default_factory = list , alias = "sizes"
60+ )
8261
8362 @computed_field
8463 @property
@@ -88,20 +67,17 @@ def retail_price_formatted(self) -> str | None:
8867 retail_price_in_dollars = Decimal (self .retail_price ) / 100
8968 return f"${ retail_price_in_dollars :.2f} "
9069
91- @computed_field
92- @property
93- def links (self ) -> list [str ]:
94- return self .get_links () if hasattr (self , "get_links" ) else []
95-
9670 @computed_field
9771 @property
9872 def images (self ) -> list [str ]:
99- return self .get_images () if hasattr (self , "get_images" ) else []
73+ return [
74+ img .url for img in sorted (self .sneaker_images , key = lambda x : x .position )
75+ ]
10076
10177 @computed_field
10278 @property
103- def sizes (self ) -> list [ str ]:
104- return self . get_sizes () if hasattr ( self , "get_sizes" ) else []
79+ def links (self ) -> dict [ Platform , str ]:
80+ return { link . platform : link . url for link in self . sneaker_links }
10581
10682
10783class PaginatedSneakersPublic (SQLModel ):
@@ -129,9 +105,9 @@ class SneakerSize(TimestampedModel, table=True):
129105 meta : dict = Field (sa_type = JSONB , default_factory = dict )
130106
131107 sneaker_id : int | None = Field (default = None , foreign_key = "sneaker.id" )
132- sneaker : Sneaker | None = Relationship (back_populates = "sizes " )
108+ sneaker : Sneaker | None = Relationship (back_populates = "sneaker_sizes " )
133109
134- prices : list ["Price" ] = Relationship (back_populates = "sneaker_size" )
110+ sneaker_size_prices : list ["Price" ] = Relationship (back_populates = "sneaker_size" )
135111
136112
137113class Price (SQLModel , table = True ):
@@ -149,7 +125,9 @@ class Price(SQLModel, table=True):
149125 last_observed : datetime | None = None
150126
151127 sneaker_size_id : int | None = Field (default = None , foreign_key = "sneakersize.id" )
152- sneaker_size : SneakerSize | None = Relationship (back_populates = "prices" )
128+ sneaker_size : SneakerSize | None = Relationship (
129+ back_populates = "sneaker_size_prices"
130+ )
153131
154132 @property
155133 def in_dollars (self ) -> Decimal :
@@ -167,7 +145,7 @@ class Link(TimestampedModel, table=True):
167145 url : str
168146
169147 sneaker_id : int | None = Field (default = None , foreign_key = "sneaker.id" )
170- sneaker : Sneaker | None = Relationship (back_populates = "links " )
148+ sneaker : Sneaker | None = Relationship (back_populates = "sneaker_links " )
171149
172150
173151class Image (TimestampedModel , table = True ):
@@ -181,7 +159,7 @@ class Image(TimestampedModel, table=True):
181159 url : str
182160
183161 sneaker_id : int | None = Field (default = None , foreign_key = "sneaker.id" )
184- sneaker : Sneaker | None = Relationship (back_populates = "images " )
162+ sneaker : Sneaker | None = Relationship (back_populates = "sneaker_images " )
185163
186164
187165class NikeLaunch (SQLModel , table = True ):
@@ -205,3 +183,22 @@ class NikeLaunch(SQLModel, table=True):
205183 merch_product_status : str | None = None
206184 is_launch_product : bool | None = None
207185 last_observed : datetime | None = None
186+
187+
188+ class SneakerSizePublic (SQLModel ):
189+ value : str
190+ size_standard : SizeStandard
191+ sneaker_size_prices : list ["PricePublic" ] = []
192+
193+
194+ class PricePublic (SQLModel ):
195+ platform : Platform | None = None
196+ amount : int
197+
198+ @computed_field
199+ @property
200+ def amount_formatted (self ) -> str :
201+ return f"${ Decimal (self .amount ) / 100 :.2f} "
202+
203+ last_observed : datetime | None = None
204+ first_observed : datetime | None = None
0 commit comments