55import datetime
66import decimal
77from enum import Enum
8+ import inspect
89import typing as t
910
1011from piccolo .columns .operators .comparison import (
2425)
2526from piccolo .columns .combination import Where
2627from piccolo .columns .defaults .base import Default
28+ from piccolo .columns .reference import LazyTableReference
2729from piccolo .querystring import QueryString
2830from piccolo .utils .warnings import colored_warning
2931
30- if t .TYPE_CHECKING :
31- from piccolo .table import Table # noqa
32- from . column_types import ForeignKey # noqa
32+ if t .TYPE_CHECKING : # pragma: no cover
33+ from piccolo .table import Table
34+ from piccolo . columns . column_types import ForeignKey
3335
3436
3537class OnDelete (str , Enum ):
@@ -62,11 +64,47 @@ def __repr__(self):
6264
6365@dataclass
6466class ForeignKeyMeta :
65- references : t .Type [Table ]
67+ references : t .Union [ t . Type [Table ], LazyTableReference ]
6668 on_delete : OnDelete
6769 on_update : OnUpdate
6870 proxy_columns : t .List [Column ] = field (default_factory = list )
6971
72+ @property
73+ def resolved_references (self ) -> t .Type [Table ]:
74+ """
75+ Evaluates the ``references`` attribute if it's a LazyTableReference,
76+ raising a ``ValueError`` if it fails, otherwise returns a ``Table``
77+ subclass.
78+ """
79+ from piccolo .table import Table
80+
81+ if isinstance (self .references , LazyTableReference ):
82+ return self .references .resolve ()
83+ elif inspect .isclass (self .references ) and issubclass (
84+ self .references , Table
85+ ):
86+ return self .references
87+ else :
88+ raise ValueError (
89+ "The references attribute is neither a Table sublclass or a "
90+ "LazyTableReference instance."
91+ )
92+
93+ def copy (self ) -> ForeignKeyMeta :
94+ kwargs = self .__dict__ .copy ()
95+ kwargs .update (proxy_columns = self .proxy_columns .copy ())
96+ return self .__class__ (** kwargs )
97+
98+ def __copy__ (self ) -> ForeignKeyMeta :
99+ return self .copy ()
100+
101+ def __deepcopy__ (self , memo ) -> ForeignKeyMeta :
102+ """
103+ We override deepcopy, as it's too slow if it has to recreate
104+ everything.
105+ """
106+ return self .copy ()
107+
70108
71109@dataclass
72110class ColumnMeta :
@@ -141,6 +179,23 @@ def get_full_name(self, just_alias=False) -> str:
141179 else :
142180 return f'{ alias } AS "{ column_name } "'
143181
182+ def copy (self ) -> ColumnMeta :
183+ kwargs = self .__dict__ .copy ()
184+ kwargs .update (
185+ params = self .params .copy (), call_chain = self .call_chain .copy (),
186+ )
187+ return self .__class__ (** kwargs )
188+
189+ def __copy__ (self ) -> ColumnMeta :
190+ return self .copy ()
191+
192+ def __deepcopy__ (self , memo ) -> ColumnMeta :
193+ """
194+ We override deepcopy, as it's too slow if it has to recreate
195+ everything.
196+ """
197+ return self .copy ()
198+
144199
145200class Selectable (metaclass = ABCMeta ):
146201 @abstractmethod
@@ -236,7 +291,7 @@ def _validate_default(
236291 """
237292 Make sure that the default value is of the allowed types.
238293 """
239- if getattr (self , "validated " , None ):
294+ if getattr (self , "_validated " , None ):
240295 # If it has previously been validated by a subclass, don't
241296 # validate again.
242297 return True
@@ -245,10 +300,10 @@ def _validate_default(
245300 and None in allowed_types
246301 or type (default ) in allowed_types
247302 ):
248- self .validated = True
303+ self ._validated = True
249304 return True
250305 elif callable (default ):
251- self .validated = True
306+ self ._validated = True
252307 return True
253308 else :
254309 raise ValueError (
@@ -409,7 +464,7 @@ def querystring(self) -> QueryString:
409464 self , "_foreign_key_meta" , None
410465 )
411466 if foreign_key_meta :
412- tablename = foreign_key_meta .references ._meta .tablename
467+ tablename = foreign_key_meta .resolved_references ._meta .tablename
413468 on_delete = foreign_key_meta .on_delete .value
414469 on_update = foreign_key_meta .on_update .value
415470 query += (
@@ -432,8 +487,29 @@ def querystring(self) -> QueryString:
432487
433488 return QueryString (query )
434489
490+ def copy (self ) -> Column :
491+ column : Column = copy .copy (self )
492+ column ._meta = self ._meta .copy ()
493+ return column
494+
495+ def __deepcopy__ (self , memo ) -> Column :
496+ """
497+ We override deepcopy, as it's too slow if it has to recreate
498+ everything.
499+ """
500+ return self .copy ()
501+
435502 def __str__ (self ):
436503 return self .querystring .__str__ ()
437504
438505 def __repr__ (self ):
439- return f"{ self ._meta .name } - { self .__class__ .__name__ } "
506+ try :
507+ table = self ._meta .table
508+ except ValueError :
509+ table_class_name = "Unknown"
510+ else :
511+ table_class_name = table .__name__
512+ return (
513+ f"{ table_class_name } .{ self ._meta .name } - "
514+ f"{ self .__class__ .__name__ } "
515+ )
0 commit comments