@@ -153,6 +153,7 @@ class ModelMeta(type):
153153 __fields__ : List
154154 __field_types__ : List
155155 __aliases__ : Dict
156+ __primary_keys__ : List
156157 # Class-level cache
157158 _base_class_cache = {}
158159
@@ -162,6 +163,8 @@ def _initialize_fields(attrs, annotations, strict):
162163 _types_local = {}
163164 _typing_args = {}
164165 aliases = {}
166+ primary_keys = [] # New list to collect primary key fields
167+
165168 for field , _type in annotations .items ():
166169 if isinstance (_type , InitVar ) or _type == InitVar :
167170 # Skip InitVar fields;
@@ -202,6 +205,10 @@ def _initialize_fields(attrs, annotations, strict):
202205 df .name = field
203206 df .type = _type
204207
208+ # Check for primary_key in field metadata
209+ if df .metadata .get ("primary_key" , False ):
210+ primary_keys .append (field )
211+
205212 # Cache reflection info so we DON’T need to call
206213 # get_origin/get_args repeatedly:
207214 args = get_args (_type )
@@ -281,7 +288,7 @@ def _initialize_fields(attrs, annotations, strict):
281288 # Assign the field object to the attrs so dataclass can pick it up
282289 attrs [field ] = df
283290 cols [field ] = df
284- return cols , _types_local , _typing_args , aliases
291+ return cols , _types_local , _typing_args , aliases , primary_keys
285292
286293 def __new__ (cls , name , bases , attrs , ** kwargs ): # noqa
287294 annotations = attrs .get ('__annotations__' , {})
@@ -295,12 +302,14 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
295302 _types = cached ['types' ].copy ()
296303 _typing_args = cached ['_typing_args' ].copy ()
297304 aliases = cached ['aliases' ].copy ()
305+ primary_keys = cached ['primary_keys' ].copy ()
298306 else :
299307 # Compute field from Bases:
300308 cols = OrderedDict ()
301309 _types = {}
302310 _typing_args = {}
303311 aliases = {}
312+ primary_keys = []
304313
305314 # Step 1: Collect fields from parent classes
306315 for base in bases :
@@ -311,9 +320,11 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
311320 _typing_args |= base .__typing_args__
312321 if hasattr (base , '__aliases__' ):
313322 aliases |= base .__aliases__
323+ if hasattr (base , '__primary_keys__' ):
324+ primary_keys += base .__primary_keys__
314325
315326 # Now initialize subclass-specific fields
316- new_cols , new_types , new_typing_args , new_aliases = cls ._initialize_fields (
327+ new_cols , new_types , new_typing_args , new_aliases , nw_primary_keys = cls ._initialize_fields ( # noqa
317328 attrs , annotations , strict
318329 )
319330
@@ -322,13 +333,15 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
322333 _types .update (new_types )
323334 _typing_args .update (new_typing_args )
324335 aliases .update (new_aliases )
336+ primary_keys .extend (nw_primary_keys )
325337
326338 # Store computed results in cache
327339 cls ._base_class_cache [base_key ] = {
328340 'cols' : cols .copy (),
329341 'types' : _types .copy (),
330342 '_typing_args' : _typing_args .copy (),
331343 'aliases' : aliases .copy (),
344+ 'primary_keys' : primary_keys .copy (),
332345 }
333346
334347 _columns = cols .keys ()
@@ -393,11 +406,33 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
393406 dc .__initialised__ = False
394407 dc .__field_types__ = _types
395408 dc .__aliases__ = aliases
409+ # Set the primary_keys on the dataclass
410+ dc .__primary_keys__ = primary_keys
396411 dc .__typing_args__ = _typing_args
397412 dc .modelName = dc .__name__
398413
399414 # Override __setattr__ method
400415 setattr (dc , "__setattr__" , _dc_method_setattr_ )
416+
417+ # Method to get primary keys without further introspection
418+ def get_primary_keys (cls ):
419+ return cls .__primary_keys__
420+
421+ # Add the method to the class
422+ dc .get_primary_keys = classmethod (get_primary_keys )
423+
424+ def get_primary_key_fields (cls ):
425+ """Return a dictionary of primary key fields with their types."""
426+ return {name : cls .__columns__ [name ] for name in cls .__primary_keys__ }
427+
428+ dc .get_primary_key_fields = classmethod (get_primary_key_fields )
429+
430+ def get_primary_key_values (self ):
431+ """Get primary key values for this instance as a dictionary."""
432+ return {key : getattr (self , key ) for key in self .__primary_keys__ }
433+
434+ dc .get_primary_key_values = get_primary_key_values
435+
401436 return dc
402437
403438 def __init__ (cls , * args , ** kwargs ) -> None :
0 commit comments