cloud-utils/sqlalchemy-aurora-data-api

Double casting UUID field with parameter as_uuid=True

Kostiantyn-Salnykov opened this issue · 5 comments

Hello, I've got an error when trying to use something like that:

id = Column(
    UUID(as_uuid=True),  # I need to use id as UUID object
    default=uuid.uuid4,
    primary_key=True,
)

UUID: from sqlalchemy.dialects.postgresql import UUID
I've also tested this logic at dockerized PostgreSQL v10.14 with psycopg2 - all works as expected.

So somewhere it tries to make code like uuid.UUID(<uuid.UUID object>)

  File "<input>", line 1, in <module>
  File "/usr/local/lib/python3.9/uuid.py", line 174, in __init__
    hex = hex.replace('urn:', '').replace('uuid:', '')
AttributeError: 'UUID' object has no attribute 'replace'```

Thanks for the report, can you give a complete reproduction of the issue including the full code that triggers the issue?

I can't share all code, but I can point main steps that I did to get the error:

  1. Create Engine
engine = create_engine(
    url=f"postgresql+auroradataapi://:@/{DB_NAME}",  # noqa
    echo=True,
    connect_args={
        "aurora_cluster_arn": CLUSTER_ARN,
        "secret_arn": SECRET_ARN,
    },
)
  1. Initiate session maker:
session_factory = sessionmaker(bind=engine, future=True)
  1. Create session:
session = session_factory()
  1. Create declarative_mixin:
@declarative_mixin
class TableNameMixin:
    pattern = re.compile(r"(?<!^)(?=[A-Z])")

    @declared_attr
    def __tablename__(cls):
        return cls.pattern.sub("_", cls.__name__).lower()

@declarative_mixin
class UUIDMixin:
    id = Column(
        UUID(as_uuid=True),  # This option as_uuid=False by default
        default=uuid.uuid4,
        server_default=text("gen_random_uuid()"),
        primary_key=True,
    )
  1. Initiate declarative_base:
Base = declarative_base(cls=TableNameMixin)
  1. Create model like that:
class TestModel(Base, UUIDMixin):
    name = Column(VARCHAR(length=128), nullable=False)
  1. Create __repr__ for TestModel:
def __repr__(self):
    return f"{self.__class__.__name__}(id='{self.id}', name='{self.name}')"
  1. Create TestModel object and commit it:
obj_id = uuid.uuid4()  # need to create id at code side
obj = TestModel(id=obj_id, name="TEST)
session.add(obj)
session.commit()

print(obj)  # this will produce an error

P.S. Also if I try to get the object from DB with as_uuid=True I also get this issue
as I understand it try to convert UUID object to UUID object again

I suppose it also can be related to aurora-data-api,
https://github.com/cloud-utils/aurora-data-api/pull/32/files

Hi @Kostiantyn-Salnykov I am having the same problem. I downgraded aurora-data-api to a version released before the PR you linked, v0.2.5, and things started working again.

Thanks for bringing this up. I wasn't able to figure out how to configure the SQLAlchemy driver API to handle UUID objects correctly when the underlying DB-API driver has already converted them to native types, so I reverted the PR in question and made a new release that should fix the problem, and added a regression test. Pleas let me know if this does not fix the problem for you.