Many-to-Many relationships broken on SQLAlchemy>1.3.5
See original GitHub issueTake the following model:
entry_tags_table = db.Table(
"entry_tags",
db.Model.metadata,
db.Column("entry_id", db.Integer, db.ForeignKey("entry.id")),
db.Column("tag_id", db.Integer, db.ForeignKey("tag.id")),
)
class Entry(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
tags = db.relationship("Tag", secondary=entry_tags_table)
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode, nullable=False)
entries = db.relationship("Entry", secondary=entry_tags_table)
[...]
class DefaultView(sqla.ModelView):
column_searchable_list = [
"title",
"tags.name",
]
admin = admin.Admin(app, name='Test', template_mode='bootstrap4', index_view=DefaultView(Entry, db.session))
This will throw an error on SQLALchemy 1.4.x
/home/me/.virtualenvs/vp/lib/python3.9/site-packages/sqlalchemy/orm/relationships.py:3435: SAWarning:
relationship 'Tag.entries' will copy column tag.id to column entry_tags.tag_id,
which conflicts with relationship(s): 'Entry.tags' (copies tag.id to entry_tags.tag_id).
If this is not the intention, consider if these relationships should be linked with back_populates,
or if viewonly=True should be applied to one or more if they are read-only.
For the less common case that foreign key constraints are partially overlapping,
the orm.foreign() annotation can be used to isolate the columns that should be written towards.
The 'overlaps' parameter may be used to remove this warning.
Adding back_populates
or backrefs as suggested by the error message and as documented in Basic Relationship Patterns like this:
[...]
entries = db.relationship(Entry, secondary=entry_tags_table, back_populates="tags")
[...]
tags = db.relationship(Tag, secondary=entry_tags_table, back_populates="entries")
results in a new error:
Traceback (most recent call last):
File "/home/me/data/sixfive/main.py", line 24, in <module>
from admin import admin
File "/home/me/data/sixfive/admin/__init__.py", line 1, in <module>
from .view import admin
File "/home/me/data/sixfive/admin/view.py", line 727, in <module>
index_view=DefaultView(Entry, db.session, url="/admin"),
File "/home/me/data/sixfive/admin/view.py", line 45, in __init__
super(DefaultView, self).__init__(model, session, *args, **kwargs)
File "/home/me/.virtualenvs/vp/lib/python3.9/site-packages/flask_admin/contrib/sqla/view.py", line 327, in __init__
super(ModelView, self).__init__(model, name, category, endpoint, url, static_folder,
File "/home/me/.virtualenvs/vp/lib/python3.9/site-packages/flask_admin/model/base.py", line 818, in __init__
self._refresh_cache()
File "/home/me/.virtualenvs/vp/lib/python3.9/site-packages/flask_admin/model/base.py", line 913, in _refresh_cache
self._search_supported = self.init_search()
File "/home/me/.virtualenvs/vp/lib/python3.9/site-packages/flask_admin/contrib/sqla/view.py", line 581, in init_search
if tools.is_hybrid_property(self.model, name):
File "/home/me/.virtualenvs/vp/lib/python3.9/site-packages/flask_admin/contrib/sqla/tools.py", line 215, in is_hybrid_property
return last_name in get_hybrid_properties(last_model)
File "/home/me/.virtualenvs/vp/lib/python3.9/site-packages/flask_admin/contrib/sqla/tools.py", line 195, in get_hybrid_properties
for key, prop in inspect(model).all_orm_descriptors.items()
File "/home/me/.virtualenvs/vp/lib/python3.9/site-packages/sqlalchemy/inspection.py", line 71, in inspect
raise exc.NoInspectionAvailable(
sqlalchemy.exc.NoInspectionAvailable: No inspection system is available for object of type <class 'str'>
make: *** [Makefile:22: runserver] Error 1
The call to inspect()
fails at is_hybrid_property()
for the attr_name tags.name
Link to gist for reproducing: app.py
Library versions:
Flask-Admin 1.5.7
Flask-SQLAlchemy 2.5.1
SQLAlchemy 1.4.2
Possibly related:
- https://github.com/flask-admin/flask-admin/issues/1976
- https://github.com/flask-admin/flask-admin/pull/1379
- https://github.com/flask-admin/flask-admin/commit/47080aeb1ffee4ea5cfd3254c18209ea9b4a04ae
- https://github.com/kvesteri/sqlalchemy-utils/blob/2e8ee0093f4a33a5c7479bc9aaf16d7863a74a16/sqlalchemy_utils/functions/orm.py#L697
sqlalchemy_utils
uses aget_mapper
func to resolve object instead of blindly callinginspect
- Docker users discovering their fancy airflow house is built out of of cards
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (7 by maintainers)
Top Results From Across the Web
Many to one relationships within table in SqlAlchemy
Following Many to One relationship with SQLAlchemy in the same table I found a way to make that a 1 to many relationship....
Read more >Configuring how Relationship Joins
One of the most common situations to deal with is when there are more than one foreign key path between two tables. Consider...
Read more >Configuring Relationships — SQLAlchemy 1.3 Documentation
Evaluation of relationship arguments; Configuring Many-to-Many Relationships ... Declarative system interacts with SQLAlchemy ORM relationship constructs.
Read more >1.3 Changelog — SQLAlchemy 2.0 Documentation
Fixed bug where a many-to-one relationship that specified uselist=True would fail to update correctly during a primary key change where a related column...
Read more >Working with Engines and Connections — SQLAlchemy 1.3 ...
The Engine is not synonymous to the DBAPI connect function, which represents just one connection resource - the Engine is most efficient when...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
is_hybrid_property
has been fixed recently in #2098If this is not the intention, consider if these relationships should be linked with back_populates, flask sqlalchemy
I change to model relation
users = db.relationship('User', secondary=association_table,back_populates="events")