I'm trying to create dynamic filtering in SQLAlchemy with relationship entities. I have the following SQLAlchemy models:
Owner(Model):
__tablename__ = 'Owners'
Id = Column(Integer, primary_key=True, autoincrement=True)
Name = Column(String(100))
Vehicle(Model):
__tablename__ = 'Vehicles'
Id = Column(Integer, primary_key=True, autoincrement=True)
LicensePlate = Column(String(20))
Model = Column(String(20))
Owner_id = Column(Integer, ForeignKey('Owners.Id'))
Owner = relationship("Owner")
So lets say I have the following filters:
filters = [("LicensePlate", "TEST"), ("Model", "TEST")]
This would be equivalent to the following query:
Vehicle.query.filter(Vehicle.LicensePlate == "TEST", Vehicle.Model == "TEST")
I then construct my query with:
query_filters = []
for f in filters:
query_filter.append(getattr(Vehicle, f[0]) == f[1])
Vehicle.query.filter(*query_filters)
But now lets say I would also like to filter my Vehicle model by the Owner property: In order to achieve that I can construct the following query:
Vehicle.query.filter(Vehicle.LicensePlate == "TEST", Vehicle.Model == "TEST").join(Vehicle.Owner).filter(Owner.Name == "John")
And I would like my dynamic filters to be constructed as:
filters = [("LicensePlate", "TEST"), ("Model", "TEST"), ("Owner.Name", "John")]
I could then easily split the propery name and use it to perform the join and creating filter.
property_type, property_attr = "Owner.Name".split(".")
However I have not found a way on how to use reflection in Python go get the Object from a string name as can be done for the property by using the getattr
The join part is not a problem, since Owner is an attribute on the Vehicle:
.join(getattr(Vehicle, property_type))
So for achieving the same as:
.filter(Owner.Name == "John")
I would need to somehow get the object from its name and then use it with the attribute name to construct the filter.
my_obj = ? property_type ?
.filter(getattr(my_obj, property_attr) == "John")
I have temporarily solved this by creating a custom dictionary that has the mapping between names and objects:
mapping = {
"Owner": Owner
}
So I can then construct the filter as:
property_type, property_attr = "Owner.Name".split(".")
.filter(getattr(mapping[property_type], property_attr) == "John")
However this dictionary is something that I have to maintain manually so I would prefer a solution that would use reflection to get the object from its name.