edelooff / sqlalchemy-json

Full-featured JSON type with mutation tracking for SQLAlchemy
http://variable-scope.com/posts/mutation-tracking-in-nested-json-structures-using-sqlalchemy
BSD 2-Clause "Simplified" License
189 stars 34 forks source link

ValueError: Attribute 'alt_names' does not accept objects of type <class 'list'> #53

Closed Soulbadguy54 closed 1 year ago

Soulbadguy54 commented 1 year ago

Hello, i took a break in programming. And for now the new type of error is appear, i think that its connected with new sqlalchemy 2.0 update.

I got this table:

class Game(Base):
    __tablename__ = "games"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    other_id:  Mapped[int] = mapped_column(Integer, index=True, unique=True, nullable=False)
    name: Mapped[str] = mapped_column(String, index=True, nullable=False)
    alt_names: Mapped[list] = mapped_column(mutable_json_type(dbtype=JSONB))

and this class to work with object:

class GameObject(Game):
    def __init__(self, session: AsyncSession, **kw: Any):
        super().__init__(**kw)
        self._session = session

    def parse_data(self, data: dict):
        alt_name_data = data.get("alternative_names", [])

        self.name = data["name"]
        self.other_id = data["id"]
        self.alt_names: list[str] = [_['name'] for _ in alt_name_data if _['comment'] in ALLOWED_TYPES]

The insertion is performed by function:

async def add_game_to_db(self):
    mutable_columns = self.__table__.columns.keys()
    del mutable_columns["id"]

    stm = insert(Game).values(**{getattr(self, column_name) for column_name in mutable_columns})
    stm = stm.on_conflict_do_update(index_elements=["other_id"],
                                    set_={field: getattr(stm.excluded, field) for field in self.__table__.columns.keys()})
    await self._session.execute(stm)

this actions result in error with traceback:

future: <Task finished name='Task-1' coro=<run.<locals>.new_coro() done, defined at G:\python projects\rate_bot\.venv\lib\site-packages\aiorun.py:209> exception=ValueError("Attribute 'alt_names' does not accept objects of type <class 'list'>")>
Traceback (most recent call last):
  File "G:\python projects\rate_bot\.venv\lib\site-packages\aiorun.py", line 219, in new_coro
    await coro
  File "G:\python projects\rate_bot\parser_main.py", line 32, in main
    game.parse_data(data)
  File "G:\python projects\rate_bot\data\game_object.py", line 26, in parse_data
    self.alt_names: list = [_['name'] for _ in alt_name_data if _['comment'] in ALLOWED_TYPES]
  File "G:\python projects\rate_bot\.venv\lib\site-packages\sqlalchemy\orm\attributes.py", line 536, in __set__
    self.impl.set(
  File "G:\python projects\rate_bot\.venv\lib\site-packages\sqlalchemy\orm\attributes.py", line 1276, in set
    value = self.fire_replace_event(
  File "G:\python projects\rate_bot\.venv\lib\site-packages\sqlalchemy\orm\attributes.py", line 1291, in fire_replace_event
    value = fn(
  File "G:\python projects\rate_bot\.venv\lib\site-packages\sqlalchemy\orm\events.py", line 2562, in wrap
    return fn(target, *arg)
  File "G:\python projects\rate_bot\.venv\lib\site-packages\sqlalchemy\ext\mutable.py", line 535, in set_
    value = cls.coerce(key, value)
  File "G:\python projects\rate_bot\.venv\lib\site-packages\sqlalchemy\ext\mutable.py", line 865, in coerce
    return Mutable.coerce(key, value)
  File "G:\python projects\rate_bot\.venv\lib\site-packages\sqlalchemy\ext\mutable.py", line 454, in coerce
    raise ValueError(msg % (key, type(value)))
ValueError: Attribute 'alt_names' does not accept objects of type <class 'list'>

I dont understand what should i do to fix this. The only way i see is to write my own mutable object as described in docs, but this way is too complex and looks like the same as this package. Can you help me with the fix? Thank you and sorry for bad english! :)

edelooff commented 1 year ago

The core here is that the non-nested mutable doesn't support the list as the base type currently. The nested one does support that right now, so adding nested=True to your mapped_column(mutable_json_type(dbtype=JSONB)) ought to help with that.

A PR to resolve this was incidentally submitted just a little ago (#51) and I'll release a new version for that one shortly.

Soulbadguy54 commented 1 year ago

Big thanks for you! waiting for new pypi release version :) And yes the nested=True flag fix the exeption!

edelooff commented 1 year ago

Released at https://pypi.org/project/sqlalchemy-json/0.7.0/