I know this question has been asked many times, but even after I tried many SO suggestions, I still cannot get it right. So, posting here for your help.
I'm building a simple web server with Flask-Restful and Flask-SqlAlchemy. Basically, I think I'm getting an error because of a circular import. My Widget class is dependent on Visualization class and vice versa...
The error message:
Traceback (most recent call last):
File "server.py", line 1, in <module>
from app.resources.dashboard import Dashboards, Dashboard
File "app/__init__.py", line 14, in <module>
from models import *
File "app/models/__init__.py", line 1, in <module>
import visualization.Visualization as VisualizationModel
File "app/models/visualization.py", line 3, in <module>
from app.models import WidgetModel
ImportError: cannot import name WidgetModel
Directory structure:
├── app
│ ├── app/__init__.py
│ ├── app/models
│ │ ├── app/models/__init__.py
│ │ ├── app/models/dashboard.py
│ │ ├── app/models/visualization.py
│ │ ├── app/models/widget.py
│ └── app/resources
│ ├── app/resources/__init__.py
│ ├── app/resources/dashboard.py
│ ├── app/resources/visualization.py
│ ├── app/resources/widget.py
├── server.py
app/models/__init__.py:
from visualization import Visualization as VisualizationModel
from widget import Widget as WidgetModel
from dashboard import Dashboard as DashboardModel
app/models/visualization.py
from sqlalchemy import types
from app import db
from app.models import WidgetModel
class Visualization(db.Model):
__tablename__ = 'visualizations'
...
widget = db.relationship(WidgetModel, cascade="all, delete-orphan", backref="visualizations")
app/models/widget.py
from app import db
from app.models import VisualizationModel
class Widget(db.Model):
__tablename__ = 'widgets'
...
visualization = db.relationship(VisualizationModel, backref="widgets")
I tried changing my import to from app import models
, and then use models.WidgetModel
/ models.VisualizationModel
. However, still getting an ImportError.
Error message:
Traceback (most recent call last):
File "server.py", line 1, in <module>
from app.resources.dashboard import Dashboards, Dashboard
File "app/__init__.py", line 14, in <module>
from models import *
File "app/models/__init__.py", line 1, in <module>
from visualization import Visualization as VisualizationModel
File "app/models/visualization.py", line 3, in <module>
from app import models
ImportError: cannot import name models
I'm very new to Python. I would be grateful if you can help me out. Thanks for you help in advance!
Update
The intention of defining bi-directional relationship is that I want to attach the Visualization object in the fields of Widget object upon a return of GET request on a widget record.
In the app/resources/widget.py I have:
...
from flask_restful import fields, Resource, marshal_with, abort
from app.models import WidgetModel
import visualization as visualizationResource
widget_fields = {
'id': fields.String,
...
'dashboard_id': fields.String,
'visualization_id': fields.String,
'visualization': fields.Nested(visualizationResource.visualization_fields)
}
class Widgets(Resource):
@marshal_with(widget_fields)
def get(self):
return WidgetModel.query.all()
I also want to have the cascade delete
feature because if a widget cannot exist without a visualization.
Change your import to look like this:
from app import models
And then use models.WidgetModel
/ models.VisualizationModel
instead.
The problem is that you're creating a circular import dependency where both files require the other file to already have been processed before they can be processed. By moving to importing the entire models
namespace rather than trying to import a specific model name at import time, you avoid making the import dependent on a fully processed file. That way, both files can be fully processed before either tries to invoke an object created by the other.
It may still not work in this case however because you're trying to immediately use the import in part of the class definition, which is evaluated at processing time.
It looks like you're trying to define bi-directional relationships - the backref
parameter is intended to do this automatically without you having to specify the relationship on both models. (backref
tells sqlalchemy what field to add to the other model to point back to the original models that link to it). So you may not need to be doing both of these imports in the first place.
For instance, the fact that Visualization.widget
is defined with backref="visualizations"
means that Widget.visualizations
will already exist - you don't need to explicitly create it.
If what you specifically need is many:many relationships, then chances are what you actually want to do is define an association table for the many-to-many relationship.