pythonpostgresqlsqlalchemyuuidpg8000

AttributeError: 'UUID' object has no attribute 'replace' when using backend-agnostic GUID type


I want to have a primary key id with type uuid in a Postgresql database using SQLAlchemy 1.1.5, connecting to the database with the pg8000 adapter. I used the Backend-agnostic GUID Type recipe from the SQLAlchemy documentation.

When I want to insert into the database, I get the following error

  File ".../guid.py", line ???, in process_result_value
    return uuid.UUID(value)
  File "/usr/lib/python2.7/uuid.py", line 131, in __init__
    hex = hex.replace('urn:', '').replace('uuid:', '')
AttributeError: 'UUID' object has no attribute 'replace'

my model looks like this

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from guid import GUID
import uuid

base = declarative_base()

class Item(base):
    __tablename__ = 'item'

    id = Column(GUID(), default=uuid.uuid4, nullable=False, unique=True, primary_key=True)
    name = Column(String)
    description = Column(String)

    def __repr__(self):
        return "<Item(name='%s', description='%s')>" % (self.name, self.description)

My resource or controller looks like this

data = req.params
item = Item(name=data['name'], description=data['description'])

self.session.add(item)
self.session.commit()

Solution

  • The pg8000 PostgreSQL database adapter is returning a uuid.UUID() object (see their type mapping documentation, and SQLAlchemy has passed that to the TypeDecorator.process_result_value() method.

    The implementation given in the documentation expected a string, however, so this fails:

    >>> import uuid
    >>> value = uuid.uuid4()
    >>> uuid.UUID(value)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/Users/mjpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/uuid.py", line 133, in __init__
        hex = hex.replace('urn:', '').replace('uuid:', '')
    AttributeError: 'UUID' object has no attribute 'replace'
    

    The quick work-around is to force the value to be a string anyway:

    def process_result_value(self, value, dialect):
        if value is None:
            return value
        else:
            return uuid.UUID(str(value))
    

    or you can test for the type first:

    def process_result_value(self, value, dialect):
        if value is None:
            return value
        else:
            if not isinstance(value, uuid.UUID):
                value = uuid.UUID(value)
            return value
    

    I've submited pull request #403 to fix this in the documentation (since merged).