def test_lazy_table_reference(self): # These are equivalent: references_list = [ LazyTableReference(table_class_name="Manager", app_name="music"), LazyTableReference( table_class_name="Manager", module_path="tests.example_apps.music.tables", ), ] for references in references_list: serialised = serialise_params(params={"references": references}) self.assertTrue( serialised.params["references"].__repr__() == "Manager") self.assertTrue(len(serialised.extra_imports) == 1) self.assertEqual( serialised.extra_imports[0].__str__(), "from piccolo.table import Table", ) self.assertTrue(len(serialised.extra_definitions) == 1) self.assertEqual( serialised.extra_definitions[0].__str__(), ('class Manager(Table, tablename="manager"): ' "id = Serial(null=False, primary_key=True, unique=False, " "index=False, index_method=IndexMethod.btree, " "choices=None, db_column_name='id', secret=False)"), )
def test_lazy_table_reference(self): # These are equivalent: references_list = [ LazyTableReference( table_class_name="Manager", app_name="example_app" ), LazyTableReference( table_class_name="Manager", module_path="tests.example_app.tables", ), ] for references in references_list: serialised = serialise_params(params={"references": references}) self.assertTrue( serialised.params["references"].__repr__() == "Manager" ) self.assertTrue(len(serialised.extra_imports) == 1) self.assertEqual( serialised.extra_imports[0].__str__(), "from piccolo.table import Table", ) self.assertTrue(len(serialised.extra_definitions) == 1) self.assertEqual( serialised.extra_definitions[0].__str__(), 'class Manager(Table, tablename="manager"): pass', )
def test_str(self): self.assertEqual( LazyTableReference( table_class_name="Manager", app_name="example_app", ).__str__(), "App example_app.Manager", ) self.assertEqual( LazyTableReference( table_class_name="Manager", module_path="tests.example_app.tables", ).__str__(), "Module tests.example_app.tables.Manager", )
def test_init(self): """ A ``LazyTableReference`` must be passed either an ``app_name`` or ``module_path`` argument. """ with self.assertRaises(ValueError): LazyTableReference(table_class_name="Manager") with self.assertRaises(ValueError): LazyTableReference( table_class_name="Manager", app_name="example_app", module_path="tests.example_app.tables", ) # Shouldn't raise exceptions: LazyTableReference( table_class_name="Manager", app_name="example_app", ) LazyTableReference( table_class_name="Manager", module_path="tests.example_app.tables", )
def __init_subclass__( cls, tablename: t.Optional[str] = None, db: t.Optional[Engine] = None, tags: t.List[str] = [], help_text: t.Optional[str] = None, ): """ Automatically populate the _meta, which includes the tablename, and columns. :param tablename: Specify a custom tablename. By default the classname is converted to snakecase. :param db: Manually specify an engine to use for connecting to the database. Useful when writing simple scripts. If not set, the engine is imported from piccolo_conf.py using ``engine_finder``. :param tags: Used for filtering, for example by ``table_finder``. :param help_text: A user friendly description of what the table is used for. It isn't used in the database, but will be used by tools such a Piccolo Admin for tooltips. """ tablename = tablename if tablename else _camel_to_snake(cls.__name__) if tablename in PROTECTED_TABLENAMES: raise ValueError( f"{tablename} is a protected name, please give your table a " "different name.") columns: t.List[Column] = [] default_columns: t.List[Column] = [] non_default_columns: t.List[Column] = [] foreign_key_columns: t.List[ForeignKey] = [] secret_columns: t.List[Secret] = [] cls.id = PrimaryKey() attribute_names = itertools.chain( *[i.__dict__.keys() for i in reversed(cls.__mro__)]) unique_attribute_names = list(dict.fromkeys(attribute_names)) for attribute_name in unique_attribute_names: if attribute_name.startswith("_"): continue attribute = getattr(cls, attribute_name) if isinstance(attribute, Column): # We have to copy, then override the existing column # definition, in case this column is inheritted from a mixin. # Otherwise, when we set attributes on that column, it will # effect all other users of that mixin. column = attribute.copy() setattr(cls, attribute_name, column) if isinstance(column, PrimaryKey): # We want it at the start. columns = [column] + columns # type: ignore default_columns.append(column) else: columns.append(column) non_default_columns.append(column) column._meta._name = attribute_name column._meta._table = cls if isinstance(column, Secret): secret_columns.append(column) if isinstance(column, ForeignKey): foreign_key_columns.append(column) cls._meta = TableMeta( tablename=tablename, columns=columns, default_columns=default_columns, non_default_columns=non_default_columns, foreign_key_columns=foreign_key_columns, secret_columns=secret_columns, tags=tags, help_text=help_text, _db=db, ) for foreign_key_column in foreign_key_columns: params = foreign_key_column._meta.params references = params["references"] if isinstance(references, str): if references == "self": references = cls else: if "." in references: # Don't allow relative modules - this may change in # the future. if references.startswith("."): raise ValueError("Relative imports aren't allowed") module_path, table_class_name = references.rsplit( ".", maxsplit=1) else: table_class_name = references module_path = cls.__module__ references = LazyTableReference( table_class_name=table_class_name, module_path=module_path, ) is_lazy = isinstance(references, LazyTableReference) is_table_class = inspect.isclass(references) and issubclass( references, Table) if is_lazy or is_table_class: foreign_key_column._foreign_key_meta.references = references else: raise ValueError( "Error - ``references`` must be a ``Table`` subclass, or " "a ``LazyTableReference`` instance.") # Record the reverse relationship on the target table. if is_table_class: references._meta._foreign_key_references.append( foreign_key_column) elif is_lazy: LAZY_COLUMN_REFERENCES.foreign_key_columns.append( foreign_key_column) # Allow columns on the referenced table to be accessed via # auto completion. if is_table_class: foreign_key_column.set_proxy_columns()
class Genre(Table): name = Varchar() bands = M2M(LazyTableReference("GenreToBand", module_path=__name__))