77from .autopopulate import AutoPopulate
88from .errors import DataJointError
99from .table import Table
10- from .utils import ClassProperty , from_camel_case
10+ from .utils import from_camel_case
1111
1212_base_regexp = r"[a-z][a-z0-9]*(_[a-z][a-z0-9]*)*"
1313
@@ -78,6 +78,26 @@ def __add__(cls, arg):
7878 def __iter__ (cls ):
7979 return iter (cls ())
8080
81+ # Class properties - defined on metaclass to work at class level
82+ @property
83+ def connection (cls ):
84+ """The database connection for this table."""
85+ return cls ._connection
86+
87+ @property
88+ def table_name (cls ):
89+ """The table name formatted for MySQL."""
90+ if cls ._prefix is None :
91+ raise AttributeError ("Class prefix is not defined!" )
92+ return cls ._prefix + from_camel_case (cls .__name__ )
93+
94+ @property
95+ def full_table_name (cls ):
96+ """The fully qualified table name (`database`.`table`)."""
97+ if cls .database is None :
98+ return None
99+ return r"`{0:s}`.`{1:s}`" .format (cls .database , cls .table_name )
100+
81101
82102class UserTable (Table , metaclass = TableMeta ):
83103 """
@@ -101,27 +121,6 @@ def definition(self):
101121 """
102122 raise NotImplementedError ('Subclasses of Table must implement the property "definition"' )
103123
104- @ClassProperty
105- def connection (cls ):
106- return cls ._connection
107-
108- @ClassProperty
109- def table_name (cls ):
110- """
111- :return: the table name of the table formatted for mysql.
112- """
113- if cls ._prefix is None :
114- raise AttributeError ("Class prefix is not defined!" )
115- return cls ._prefix + from_camel_case (cls .__name__ )
116-
117- @ClassProperty
118- def full_table_name (cls ):
119- if cls not in {Manual , Imported , Lookup , Computed , Part , UserTable }:
120- # for derived classes only
121- if cls .database is None :
122- raise DataJointError ("Class %s is not properly declared (schema decorator not applied?)" % cls .__name__ )
123- return r"`{0:s}`.`{1:s}`" .format (cls .database , cls .table_name )
124-
125124
126125class Manual (UserTable ):
127126 """
@@ -163,7 +162,28 @@ class Computed(UserTable, AutoPopulate):
163162 tier_regexp = r"(?P<computed>" + _prefix + _base_regexp + ")"
164163
165164
166- class Part (UserTable ):
165+ class PartMeta (TableMeta ):
166+ """Metaclass for Part tables with overridden class properties."""
167+
168+ @property
169+ def table_name (cls ):
170+ """The table name for a Part is derived from its master table."""
171+ return None if cls .master is None else cls .master .table_name + "__" + from_camel_case (cls .__name__ )
172+
173+ @property
174+ def full_table_name (cls ):
175+ """The fully qualified table name (`database`.`table`)."""
176+ if cls .database is None or cls .table_name is None :
177+ return None
178+ return r"`{0:s}`.`{1:s}`" .format (cls .database , cls .table_name )
179+
180+ @property
181+ def master (cls ):
182+ """The master table for this Part table."""
183+ return cls ._master
184+
185+
186+ class Part (UserTable , metaclass = PartMeta ):
167187 """
168188 Inherit from this class if the table's values are details of an entry in another table
169189 and if this table is populated by the other table. For example, the entries inheriting from
@@ -184,24 +204,6 @@ class Part(UserTable):
184204 + ")"
185205 )
186206
187- @ClassProperty
188- def connection (cls ):
189- return cls ._connection
190-
191- @ClassProperty
192- def full_table_name (cls ):
193- return (
194- None if cls .database is None or cls .table_name is None else r"`{0:s}`.`{1:s}`" .format (cls .database , cls .table_name )
195- )
196-
197- @ClassProperty
198- def master (cls ):
199- return cls ._master
200-
201- @ClassProperty
202- def table_name (cls ):
203- return None if cls .master is None else cls .master .table_name + "__" + from_camel_case (cls .__name__ )
204-
205207 def delete (self , force = False ):
206208 """
207209 unless force is True, prohibits direct deletes from parts.
0 commit comments