SQLAlchemy - query with secondary table with reflected Base - python

I work with a pre-existing database and SQLAlchemy. I reflect all tables like
Base = automap_base()
Base.prepare(engine, reflect=True)
Then all the classes can be accesed with e.g. a_table = Base.classes.A. From documentation I understand, that if a table has only two columns, both foreign keys, then the table
is assumed to be a “secondary” table, and will not be mapped directly
So the secondary class is not among the classes in the Base and
The two (or one, for self-referential) external tables to which the
Table refers to are matched to the classes to which they will be
mapped, if any. If mapped classes for both sides are located, a
many-to-many bi-directional relationship() / backref() pair is created
between the two classes.
But I cannot figure out how to use that in a query. To clarify my scenario, I have tables like shown below and I want to get (depending on A_id) C_ids for B, where B_id is main_B_id in A.
But whatever query I try it never sees the other table. I tried some workarounds, but I guess there must be some simple way how to achieve that and I just missed some part of the documentation. Any hint would be highly appreciated, thanks!

Related

in database related document, parent and child is refer to what?

that makes sense that we refer to the table that uses values from another table as a child(this column is defined as a foreign key), in this regard the other table is parent. this makes more sense in one to many relations.
one record in the table(parent) has a relationship with one or more records in another table(child). in short terms one parent has many child.
in this answer from StackOverflow these were mentioned.
but in this section of SQLAlchemy document that talks about many to one relationship, they are placing the ForeingKey in the parent table . what does it mean?
also in this section of SQLALchey document that talks about many to many relationship between two table, and implementing this behavior with Asociation object rather than Association table, they referring to one of the tables as parent and another one as child what does it mean?

Proper way to insert records with foreign keys in sqlalchemy

I created a number of classes in SQLAlchemy to represent my various tables. I now want to insert records into these tables from various csv files that contain the data in an unnormalized format. What is the best way to deal with foreign keys?
In a simplified model, I have two tables: Child and Parent, with a one to many relationship. The parent table is already filled up, with a unique parent_name for each primary key. I am currently doing this:
for index, row in df.iterrows():
u = session.query(Parent).filter_by(parent_name=row['parent_name']).first()
session.add(Child(child_name=row['child_name'], parent_id=u.id))
Is there a way with sqlalchemy to avoid the first query? This question implies that using relationships is the correct/easy way to do it, but only explains the hard way.

SQLalchemy - Iterate through all mapped tables

I am currently creating a web app in Flask and use SQL-alchemy (not the flask version) to deal with reading and writing to my MySQL database.
I have about 15 different tables each mapped to a different declarative class, however the application is still in beta stages and so this number will probably increase.
I would like a way to iterate through every single table and run the same command on every single one. This is part of an update function where an admin can change the name of a book, this name change should be reflected in all the other tables where that book is referred to.
Is there a way to iterate through all your SqlAlchemy tables?
Thanks!
Not exactly sure what you want to achieve here, but if you use declarative base, you can try something like this:
tables = Base.__subclasses__()
for t in tables:
rows = Session.query(t).all()
for r in rows:
... do something ...
This gets all tables by listing subclasses of Base. Then it queries everything from each table in turn and loops through selected rows.
However, I do not quite understand why you would want to do this. How you describe your question is that you should have a Book table, and all others link to it if they want to reference books. This would be the relational model instead of dragging information on Books in each and every table and trying to manage them like this manually.

SQLAlchemy Relationships

I understand that ForeignKey constrains a column to be an id value contained in another table so that entries in two different tables can be easily linked, but I do not understand the behavior of relationships(). As far as I can tell, the primary effect of declaring a relationship between Parent and Child classes is that parentobject.child will now reference the entries linked to the parentobject in the children table. What other effects does declaring a relationship have? How does declaring a relationship change the behavior of the SQL database or how SQLAlchemy interacts with the database?
It doesn't do anything at the database level, it's purely for convenience. Defining a relationship lets SQLAlchemy know how to automatically query for the related object, rather than you having to manually use the foreign key. SQLAlchemy will also do other high level management such as allowing assignment of objects and cascading changes.

Discovering referers to SQLAlchemy object

I have a lot of model classes with ralations between them with a CRUD interface to edit. The problem is that some objects can't be deleted since there are other objects refering to them. Sometimes I can setup ON DELETE rule to handle this case, but in most cases I don't want automatic deletion of related objects till they are unbound manually. Anyway, I'd like to present editor a list of objects refering to currently viewed one and highlight those that prevent its deletion due to FOREIGN KEY constraint. Is there a ready solution to automatically discover referers?
Update
The task seems to be quite common (e.g. django ORM shows all dependencies), so I wonder that there is no solution to it yet.
There are two directions suggested:
Enumerate all relations of current object and go through their backref. But there is no guarantee that all relations have backref defined. Moreover, there are some cases when backref is meaningless. Although I can define it everywhere I don't like doing this way and it's not reliable.
(Suggested by van and stephan) Check all tables of MetaData object and collect dependencies from their foreign_keys property (the code of sqlalchemy_schemadisplay can be used as example, thanks to stephan's comments). This will allow to catch all dependencies between tables, but what I need is dependencies between model classes. Some foreign keys are defined in intermediate tables and have no models corresponding to them (used as secondary in relations). Sure, I can go farther and find related model (have to find a way to do it yet), but it looks too complicated.
Solution
Below is a method of base model class (designed for declarative extention) that I use as solution. It is not perfect and doesn't meet all my requirements, but it works for current state of my project. The result is collected as dictionary of dictionaries, so I can show them groupped by objects and their properties. I havn't decided yet whether it's good idea, since the list of referers sometimes is huge and I'm forced to limit it to some reasonable number.
def _get_referers(self):
db = object_session(self)
cls, ident = identity_key(instance=self)
medatada = cls.__table__.metadata
result = {}
# _mapped_models is my extension. It is collected by metaclass, so I didn't
# look for other ways to find all model classes.
for other_class in medatada._mapped_models:
queries = {}
for prop in class_mapper(other_class).iterate_properties:
if not (isinstance(prop, PropertyLoader) and \
issubclass(cls, prop.mapper.class_)):
continue
query = db.query(prop.parent)
comp = prop.comparator
if prop.uselist:
query = query.filter(comp.contains(self))
else:
query = query.filter(comp==self)
count = query.count()
if count:
queries[prop] = (count, query)
if queries:
result[other_class] = queries
return result
Thanks to all who helped me, especially stephan and van.
SQL: I have to absolutely disagree with S.Lott' answer.
I am not aware of out-of-the-box solution, but it is definitely possible to discover all the tables that have ForeignKey constraints to a given table. One needs to use properly the INFORMATION_SCHEMA views such as REFERENTIAL_CONSTRAINTS, KEY_COLUMN_USAGE, TABLE_CONSTRAINTS, etc. See SQL Server example. With some limitations and extensions, most versions of new relational databases support INFORMATION_SCHEMA standard. When you have all the FK information and the object (row) in the table, it is a matter of running few SELECT statements to get all other rows in other tables that refer to given row and prevent it from being deleted.
SqlAlchemy: As noted by stephan in his comment, if you use orm with backref for relations, then it should be quite easy for you to get the list of parent objects that keep reference to the object you are trying to delete, because those objects are basically mapped properties of your object (child1.Parent).
If you work with Table objects of sql alchemy (or not always use backref for relations), then you would have to get values of foreign_keys for all the tables, and then for all those ForeignKeys call references(...) method, providing your table as a parameter. In this way you will find all the FKs (and tables) that have reference to the table your object maps to. Then you can query all the objects that keep reference to your object by constructing the query for each of those FKs.
In general, there's no way to "discover" all of the references in a relational database.
In some databases, they may use declarative referential integrity in the form of explicit Foreign Key or Check constraints.
But there's no requirement to do this. It can be incomplete or inconsistent.
Any query can include a FK relationship that is not declared. Without the universe of all queries, you can't know the relationships which are used but not declared.
To find "referers" in general, you must actually know the database design and have all queries.
For each model class, you can easily see if all its one-to-many relations are empty simply by asking for the list in each case and seeing how many entries it contains. (There is probably a more efficient way implemented in terms of COUNT, too.) If there are any foreign keys relating to the object, and you have your object relations set up correctly, then at least one of these lists will be non-zero in length.

Categories

Resources