I have a number of SQLAlchemy ORM classes that map to a database. I've written quite a few functions that utilise a number of these classes in different combinations. For example I might want to get the first from the Tournament
table or the first record from the Player
table. I only need one query for this as the queried fields are the same between the tables. To maintain type hints I could do the following:
def get_first_record(table: Type[Union[Tournament, Player]]):
# query the table for the first record
If I don't want to keep repeating the type hint for other functions then I could create a variable:
Tables = Type[Union[Tournament, Player]]
def get_last_record(table: Tables):
# query the table for the last record
As I have a large number of tables and add to them frequently then it's a pain to maintain these Union
variables.
Coincidentally for locking and unlocking certain combinations of tables I've written some functions to create custom lists of classes in modules. I was hoping to be able to unpack these lists when creating the Union
variables but Pylance tells me Union
needs more than two variables:
tables = [Tournament, Player]
Tables = Type[Union[*tables]] # no bueno :(
Is there a solution to this?
I think it's not possible to do literally what you're asking for here:
tables = [Tournament, Player]
Tables = Type[Union[*tables]] # no bueno :(
See these other questions for more detail:
Passing a tuple arg (i.e. Union[tuple(tables)]
is valid python and will work at runtime, but is not accepted by any type checker such as mypy, because you can only use literals and not expressions (i.e. the function call result) for defining types.
An alternative that may work for you is:
from typing import TypeVar
T = TypeVar('T', bound=django.models.Model)
def get_last_record(table: Type[T]) -> T:
...
If you need to restrict calls of get_last_record
to a subset of specific models this annotation won't help you unfortunately - you'd still need to define a Union
of models to use as a bound
.
A possible work around would be... instead of defining a a list of models and try to create a Union from that, do it the other way around, e.g.:
T = Union[Model1, Model2, Model3]
models = list(T.__args__)
But since you say " I've written some functions to create custom lists of classes in modules" I think this won't work for you either because the result of the function call won't be acceptable as a type for mypy.
Actually, probably what would work better for you is to materialise the union of models into the type heirarchy as a base class that they all share.
e.g. instead of:
class Model1(django.models.Model):
...
class Model2(django.models.Model):
...
class Model3(django.models.Model):
...
T = Union[Model1, Model2, Model3]
def get_last_record(table: Type[T]) -> T:
...
have:
class OrderableRecord(django.models.Model):
class Meta:
abstract = True
class Model1(OrderableRecord):
...
class Model2(OrderableRecord):
...
class Model3(OrderableRecord):
...
T = TypeVar('T', bound=OrderableRecord)
def get_last_record(table: Type[T]) -> T:
...