So I am doing some learning, and I have followed several tutorials online for Flask-SQLAlchemy and dataclasses in Python. I have my app set up as bog standard as I can get:
__init__.py
from flask import Flask, request, jsonify
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "postgresql://log:pass@localhost/db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
CORS(app)
db = SQLAlchemy(app)
I got up to a point where I am using the Object Association Pattern for Many-To-Many relationships, and that worked fine, returning what I need. I now want to pull an extra column from the middle table in that relationship, x. That led me to using association_proxy. I seem to have followed the tutorials correctly that I can find, but when I start up flask, I keep getting the following:
ValueError: mutable default <class 'sqlalchemy.ext.associationproxy.AmbiguousAssociationProxyInstance'> for field x is not allowed: use default_factory
models.py
from typing import List
from dataclasses import dataclass
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import Mapped
from sqlalchemy.ext.associationproxy import association_proxy
from . import db
@dataclass
class Main(db.Model):
__tablename__="main"
id: int = db.Column(db.Integer, primary_key=True)
ones: Mapped[List[One]] = db.relationship("One", secondary="manys", overlaps="main")
@dataclass
class Many(db.Model):
__tablename__ = "manys"
main_id = db.Column(db.Integer, db.ForeignKey("main.id"), primary_key=True)
one_id = db.Column(db.Integer, db.ForeignKey("one.id"), primary_key=True)
x = db.Column(db.Boolean, nullable=False, default=False)
@dataclass
class One(db.Model):
__tablename__ = "ones"
id: int = db.Column(db.Integer, primary_key=True)
name: str = db.Column(db.String, nullable=False)
other_id: int = db.Column(db.Integer, nullable=False)
many_link = db.relationship("Many", backref="ones")
many_x: Mapped[bool] = association_proxy("many_link", "x")
Am I doing anything explicitly wrong? Am I missing something?
If I add in default_factory=None
to my proxy_association, I get
sqlalchemy.exc.ArgumentError: Attribute 'x' on class <class 'api.model.One'> includes dataclasses argument(s): 'default_factory' but class does not specify SQLAlchemy native dataclass configuration.
Solved by the following. Retains the ability to use Main.query.all() functionality. Thanks for the pointer, @snakecharmerb.
__init__.py
#Model Base Class
class Base(DeclarativeBase, MappedAsDataclass):
pass
app = Flask(__name__)
app.json = BetterJsonProvider(app)
app.config["SQLALCHEMY_DATABASE_URI"] = "postgresql://login:passwd@localhost/db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
CORS(app)
db = SQLAlchemy(app, model_class=Base)
"Dataclasses" now look like this:
class Main(db.Model):
__tablename__="main"
id: int = db.Column(db.Integer, primary_key=True)
ones: Mapped[List[One]] = db.relationship("One", secondary="manys", overlaps="many,one")
class Many(db.Model):
__tablename__ = "manys"
main_id = db.Column(db.Integer, db.ForeignKey("main.id"), primary_key=True)
one_id = db.Column(db.Integer, db.ForeignKey("one.id"), primary_key=True)
x = db.Column(db.Boolean, nullable=False, default=False)
class One(db.Model):
__tablename__ = "ones"
id: int = db.Column(db.Integer, primary_key=True)
name: str = db.Column(db.String, nullable=False)
other_id: int = db.Column(db.Integer, nullable=False)
many_link = db.relationship("Many", backref="ones", uselist=False)
many_x: Mapped[bool] = association_proxy("many_link", "x", default_factory=bool)