SQLalchemy class 可以同时具有一对多和一对一关系吗?
Can a SQLalchemy class have both a one-to-many and a one-to-one relationship?
我正在尝试将歌曲和评级之间的一对一关系添加到我的烧瓶模型中,但是当我 运行 我的查询时出现错误。我已经按照 here 中的步骤进行操作,但是当涉及到 sqlalchemy 时,我仍然感到迷茫。歌曲已经具有有效的一对多关系,也许不能同时具有一对多和一对一关系。
from application import db
association_table = db.Table('association',
db.Column('songs_id', db.Integer,
db.ForeignKey('songs.id')),
db.Column('genres_id', db.Integer,
db.ForeignKey('genres.id'))
)
class Rating(db.Model):
__tablename__ = 'songs_ratings'
id = db.Column(db.Integer, primary_key=True)
rating = db.Column(db.Numeric(precision=3, scale=2),
index=True, nullable=False)
song_id = db.Column(db.Integer, db.ForeignKey('songs.id'))
song = db.relationship("Song", uselist=False,
back_populates="songs_ratings")
def __repr__(self):
return '{}'.format(self.rating)
class Song(db.Model):
__tablename__ = 'songs'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80), index=True, unique=True, nullable=False)
artist = db.Column(db.String(30), primary_key=False,
unique=False, nullable=False)
added = db.Column(db.Date, nullable=False)
rating = db.relationship("Rating", back_populates="songs")
genres = db.relationship(
"Genre", secondary=association_table, backref=db.backref('songs'))
def __repr__(self):
return '{};{};{}'.format(self.title, self.artist, self.added)
class Genre(db.Model):
__tablename__ = 'genres'
id = db.Column(db.Integer, primary_key=True)
category = db.Column(db.String(80), index=True,
unique=True, nullable=False)
def __repr__(self):
return '{}'.format(self.category)
错误
我在 运行 编译代码时遇到的错误。
---------------------------------------------------------------------------
InvalidRequestError Traceback (most recent call last)
<ipython-input-7-99ffacdf2d91> in <module>
----> 1 query = db.session.query(Rating, Song).filter(Rating.id==Song.id)
2 df = pd.read_sql_query(query, db.engine)
3 df
<string> in query(self, *entities, **kwargs)
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\session.py in query(self, *entities, **kwargs)
2066 """
2067
-> 2068 return self._query_cls(entities, self, **kwargs)
2069
2070 def _identity_lookup(
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\query.py in __init__(self, entities, session)
173
174 self.session = session
--> 175 self._set_entities(entities)
176
177 def _set_propagate_attrs(self, values):
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\query.py in _set_entities(self, entities)
187 post_inspect=True,
188 )
--> 189 for ent in util.to_list(entities)
190 ]
191
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\query.py in <listcomp>(.0)
187 post_inspect=True,
188 )
--> 189 for ent in util.to_list(entities)
190 ]
191
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\sql\coercions.py in expect(role, element, apply_propagate_attrs, argname, post_inspect, **kw)
166 if insp is not None:
167 if post_inspect:
--> 168 insp._post_inspect
169 try:
170 resolved = insp.__clause_element__()
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\util\langhelpers.py in __get__(self, obj, cls)
1158 if obj is None:
1159 return self
-> 1160 obj.__dict__[self.__name__] = result = self.fget(obj)
1161 obj._memoized_keys |= {self.__name__}
1162 return result
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\mapper.py in _post_inspect(self)
2095
2096 """
-> 2097 self._check_configure()
2098
2099 @HasMemoized.memoized_attribute
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\mapper.py in _check_configure(self)
1872 def _check_configure(self):
1873 if self.registry._new_mappers:
-> 1874 _configure_registries({self.registry}, cascade=True)
1875
1876 def _post_configure_properties(self):
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\mapper.py in _configure_registries(registries, cascade)
3382 # the order of mapper compilation
3383
-> 3384 _do_configure_registries(registries, cascade)
3385 finally:
3386 _already_compiling = False
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\mapper.py in _do_configure_registries(registries, cascade)
3417 )
3418 e._configure_failed = mapper._configure_failed
-> 3419 raise e
3420
3421 if not mapper.configured:
InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class Rating->songs_ratings'. Original exception was: Mapper 'mapped class Song->songs' has no property 'songs_ratings'
代码有几个问题:
- 您的关系命名应与
back_populates
匹配。你有:
song
/ song_rating
在一段关系中,并且
rating
/ songs
在另一个规范中。
只需清理一下,如下所示:
class Rating(db.Model):
# ...
song_id = db.Column(db.Integer, db.ForeignKey("songs.id"))
song = db.relationship("Song", back_populates="rating")
class Song(db.Model):
# ...
rating = db.relationship("Rating", uselist=False, back_populates="song")
- 上面也已经解决了你需要
uselist=False
的第二个关系,而不是第一个关系(虽然它并没有真正伤害),因为 ForeignKey 从 Rating
到Song
.
但是,考虑到它是 1 对 1 的关系,并且没有歌曲就不能存在评级,您实际上可以重用您的原始模型并在 ForeignKey
上指定关系=21=] 列本身通过改变 Rating
模型如下:
class Rating(db.Model):
__tablename__ = "songs_ratings"
id = db.Column(db.Integer, db.ForeignKey("songs.id"), primary_key=True) # <- added FK
rating = db.Column(db.Numeric(precision=3, scale=2), index=True, nullable=False)
song = db.relationship("Song", back_populates="rating")
我正在尝试将歌曲和评级之间的一对一关系添加到我的烧瓶模型中,但是当我 运行 我的查询时出现错误。我已经按照 here 中的步骤进行操作,但是当涉及到 sqlalchemy 时,我仍然感到迷茫。歌曲已经具有有效的一对多关系,也许不能同时具有一对多和一对一关系。
from application import db
association_table = db.Table('association',
db.Column('songs_id', db.Integer,
db.ForeignKey('songs.id')),
db.Column('genres_id', db.Integer,
db.ForeignKey('genres.id'))
)
class Rating(db.Model):
__tablename__ = 'songs_ratings'
id = db.Column(db.Integer, primary_key=True)
rating = db.Column(db.Numeric(precision=3, scale=2),
index=True, nullable=False)
song_id = db.Column(db.Integer, db.ForeignKey('songs.id'))
song = db.relationship("Song", uselist=False,
back_populates="songs_ratings")
def __repr__(self):
return '{}'.format(self.rating)
class Song(db.Model):
__tablename__ = 'songs'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80), index=True, unique=True, nullable=False)
artist = db.Column(db.String(30), primary_key=False,
unique=False, nullable=False)
added = db.Column(db.Date, nullable=False)
rating = db.relationship("Rating", back_populates="songs")
genres = db.relationship(
"Genre", secondary=association_table, backref=db.backref('songs'))
def __repr__(self):
return '{};{};{}'.format(self.title, self.artist, self.added)
class Genre(db.Model):
__tablename__ = 'genres'
id = db.Column(db.Integer, primary_key=True)
category = db.Column(db.String(80), index=True,
unique=True, nullable=False)
def __repr__(self):
return '{}'.format(self.category)
错误
我在 运行 编译代码时遇到的错误。
---------------------------------------------------------------------------
InvalidRequestError Traceback (most recent call last)
<ipython-input-7-99ffacdf2d91> in <module>
----> 1 query = db.session.query(Rating, Song).filter(Rating.id==Song.id)
2 df = pd.read_sql_query(query, db.engine)
3 df
<string> in query(self, *entities, **kwargs)
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\session.py in query(self, *entities, **kwargs)
2066 """
2067
-> 2068 return self._query_cls(entities, self, **kwargs)
2069
2070 def _identity_lookup(
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\query.py in __init__(self, entities, session)
173
174 self.session = session
--> 175 self._set_entities(entities)
176
177 def _set_propagate_attrs(self, values):
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\query.py in _set_entities(self, entities)
187 post_inspect=True,
188 )
--> 189 for ent in util.to_list(entities)
190 ]
191
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\query.py in <listcomp>(.0)
187 post_inspect=True,
188 )
--> 189 for ent in util.to_list(entities)
190 ]
191
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\sql\coercions.py in expect(role, element, apply_propagate_attrs, argname, post_inspect, **kw)
166 if insp is not None:
167 if post_inspect:
--> 168 insp._post_inspect
169 try:
170 resolved = insp.__clause_element__()
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\util\langhelpers.py in __get__(self, obj, cls)
1158 if obj is None:
1159 return self
-> 1160 obj.__dict__[self.__name__] = result = self.fget(obj)
1161 obj._memoized_keys |= {self.__name__}
1162 return result
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\mapper.py in _post_inspect(self)
2095
2096 """
-> 2097 self._check_configure()
2098
2099 @HasMemoized.memoized_attribute
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\mapper.py in _check_configure(self)
1872 def _check_configure(self):
1873 if self.registry._new_mappers:
-> 1874 _configure_registries({self.registry}, cascade=True)
1875
1876 def _post_configure_properties(self):
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\mapper.py in _configure_registries(registries, cascade)
3382 # the order of mapper compilation
3383
-> 3384 _do_configure_registries(registries, cascade)
3385 finally:
3386 _already_compiling = False
~\AppData\Local\Programs\Python\Python37\lib\site-packages\sqlalchemy\orm\mapper.py in _do_configure_registries(registries, cascade)
3417 )
3418 e._configure_failed = mapper._configure_failed
-> 3419 raise e
3420
3421 if not mapper.configured:
InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class Rating->songs_ratings'. Original exception was: Mapper 'mapped class Song->songs' has no property 'songs_ratings'
代码有几个问题:
- 您的关系命名应与
back_populates
匹配。你有:
song
/song_rating
在一段关系中,并且rating
/songs
在另一个规范中。
只需清理一下,如下所示:
class Rating(db.Model):
# ...
song_id = db.Column(db.Integer, db.ForeignKey("songs.id"))
song = db.relationship("Song", back_populates="rating")
class Song(db.Model):
# ...
rating = db.relationship("Rating", uselist=False, back_populates="song")
- 上面也已经解决了你需要
uselist=False
的第二个关系,而不是第一个关系(虽然它并没有真正伤害),因为 ForeignKey 从Rating
到Song
.
但是,考虑到它是 1 对 1 的关系,并且没有歌曲就不能存在评级,您实际上可以重用您的原始模型并在 ForeignKey
上指定关系=21=] 列本身通过改变 Rating
模型如下:
class Rating(db.Model):
__tablename__ = "songs_ratings"
id = db.Column(db.Integer, db.ForeignKey("songs.id"), primary_key=True) # <- added FK
rating = db.Column(db.Numeric(precision=3, scale=2), index=True, nullable=False)
song = db.relationship("Song", back_populates="rating")