AttributeError:“UUID”对象在使用与后端无关的 GUID 类型时没有属性“replace”

Posted

技术标签:

【中文标题】AttributeError:“UUID”对象在使用与后端无关的 GUID 类型时没有属性“replace”【英文标题】:AttributeError: 'UUID' object has no attribute 'replace' when using backend-agnostic GUID type 【发布时间】:2018-05-05 21:50:46 【问题描述】:

我想在使用 SQLAlchemy 1.1.5 的 Postgresql 数据库中拥有一个类型为 uuid 的主键 id,并使用 pg8000 适配器连接到数据库。我使用了 SQLAlchemy 文档中的 Backend-agnostic GUID Type recipe。

当我想插入数据库时​​,出现以下错误

  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'

我的模型是这样的

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)

我的资源或控制器如下所示

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

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

【问题讨论】:

注意:为什么您需要使用后端不可知的自定义 GUID 类型,尽管您使用的是 Postgresql。我可能遗漏了一些东西,但 SQLAlchemy 也提供了postgresql.UUID @IljaEverilä:大概是因为他们的项目试图支持的不仅仅是 postgresql。 【参考方案1】:

这应该可以解决它:

id = Column(GUID(as_uuid=True), ...)

来自https://bitbucket.org/zzzeek/sqlalchemy/issues/3323/in-099-uuid-columns-are-broken-with:

“如果要传递UUID() 对象,则必须将as_uuid 标志设置为True。”

【讨论】:

db.Column(UUID, primary_key=True) 更改为db.Column(UUID(as_uuid=True), primary_key=True) 解决了我的问题。 感谢@DavidHariri db.Column(UUID(as_uuid=True) 也解决了我的问题 这被记录为 not working on pg8000,这是 OP 正在使用的驱动程序。 as_uuid 在使用 psycopg2 时是 great,但对于这个特定问题,这个答案是错误的。来自UUID documentation:可能并非所有 DBAPI 都支持 UUID 类型。众所周知,它适用于 psycopg2 而不是 pg8000。【参考方案2】:

pg8000 PostgreSQL 数据库适配器正在返回一个 uuid.UUID() 对象(请参阅他们的 type mapping documentation,并且 SQLAlchemy 已将其传递给 TypeDecorator.process_result_value() method。

文档中给出的实现需要一个字符串,但是,这失败了:

>>> 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'

快速的解决方法是强制该值是一个字符串:

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

或者您可以先测试类型:

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

我已提交 pull request #403 以在文档中解决此问题(自合并以来)。

【讨论】:

【参考方案3】:

在跨系统使用 UUID 时,这可能会让人非常沮丧。在某些情况下,可能难以控制 UUID 是作为字符串还是作为原始 UUID 出现。要解决这个问题,这样的解决方案可能会奏效。我附上了文档的示例,以确保其他所有内容仍然适用。

# TODO: Set this up such that the normal uuid interface is available as a pass through
import uuid

class UUID(uuid.UUID):

    def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,
                       int=None, version=None):

        if hex and (issubclass(type(hex), uuid.UUID) or isinstance(hex, uuid.UUID)):
            hex = str(hex)

        super(UUID, self).__init__(hex=hex, bytes=bytes, bytes_le=bytes_le, fields=fields, int=int, version=version)

print(UUID(uuid4())) # Now this works!

print(UUID('12345678-1234-5678-1234-567812345678'))
print(UUID('12345678123456781234567812345678'))
print(UUID('urn:uuid:12345678-1234-5678-1234-567812345678'))
print(UUID(bytes=b'\x12\x34\x56\x78' * 4)) # Python 3 requires this to be prefixed with b''. Docs appear to be mainly for Python 2
print(UUID(bytes_le=b'\x78\x56\x34\x12\x34\x12\x78\x56' +
              b'\x12\x34\x56\x78\x12\x34\x56\x78'))
print(UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678)))
print(UUID(int=0x12345678123456781234567812345678))

请自行判断,这只是一个例子。

【讨论】:

issubclass(type(hex), uuid.UUID) 测试完全是多余的。 isinstance(hex, uuid.UUID)uuid.UUID 的子类的实例是正确的,无需显式测试。请注意,None 也不是UUID 的实例,因此也可以删除hex and 组件:if isinstance(hex, uuid.UUID): 请注意Python core developers have rejected the idea of accepting UUID instances as input for UUID(). 最后但同样重要的是,考虑使用if isinstance(hex, uuid.UUID): hex, int = None, hex.int,而不是使用hex = str(hex),作为一种更快的替代方法,不需要将.int属性格式化为十六进制,然后将其拆分成更大的字符串,然后再被解析成一个整数..【参考方案4】:

我也遇到了同样的问题,搜索了两天才发现错误之前的代码有错误:

它得到了以下错误,指的是python和sqlalchemy中的错误

offertemodule_1  |   File "/opt/packages/database/models.py", line 79, in process_bind_param
offertemodule_1  |     return "%.32x" % uuid.UUID(value).int
offertemodule_1  |   File "/usr/local/lib/python3.7/uuid.py", line 157, in __init__
offertemodule_1  |     hex = hex.replace('urn:', '').replace('uuid:', '')
offertemodule_1  | sqlalchemy.exc.StatementError: (builtins.AttributeError) 'builtin_function_or_method' object has no attribute 'replace'
offertemodule_1  | [SQL: SELECT product.id AS product_id, product.supplier_id AS product_supplier_id, product.supplier_product_url AS product_supplier_product_url, product.supplier_product_id AS product_supplier_product_id, product.title AS product_title, product.description AS product_description, product.brand AS product_brand, product.product_line AS product_product_line, product.buying_price_ex_vat AS product_buying_price_ex_vat, product.buying_vat AS product_buying_vat, product.vat_pct AS product_vat_pct, product.advise_price AS product_advise_price, product.estimated_days_leadtime AS product_estimated_days_leadtime, product.product_category AS product_product_category, product.nestedproducts AS product_nestedproducts, product.atttibutes_meta AS product_atttibutes_meta, product.statistics_meta AS product_statistics_meta, product.active AS product_active, product.created AS product_created, product.created_by AS product_created_by, product.modified AS product_modified, product.modified_by AS product_modified_by
offertemodule_1  | FROM product
offertemodule_1  | WHERE product.id = %(id_1)s]
offertemodule_1  | [parameters: [immutabledict()]]

但事实证明,在此之前的一个进程向我的数据库函数发送了错误的对象

@ns_products.route('/<string:product_id>')
@api.response(404, 'product not found.')
class Details(Resource):
    @api.marshal_with(product)
    @api.doc(security='jwt')
    @jwt_required
    def get(self, product_id):
        '''Returns a single product instance'''
        return Product.get(id)`

应该是(注意'product_id')

@ns_products.route('/<string:product_id>')
@api.response(404, 'product not found.')
class Details(Resource):
    @api.marshal_with(product)
    @api.doc(security='jwt')
    @jwt_required
    def get(self, product_id):
        '''Returns a single product instance'''
        return Product.get(product_id)

因此存储在 product_id 中的 uuid 字符串实际上是一个原生 python 对象“id”。因此,它尝试将字符串处理为 uuid,但失败了。

【讨论】:

【参考方案5】:

这里的实际问题是您有 default=uuid.uuid4,其中 postgres 需要 UUID 格式的字符串。你会想要类似default=lambda: str(uuid.uuid4())

【讨论】:

【参考方案6】:

我在不使用表单的情况下遇到了影响我的 ORM 的问题。我正在运行 psycopg2。我的解决方法是:

sudo pip install psycopg2-binary

重启 apache 后,从 psycopg2-binary 版本 2.7.5+ 开始,我再也没有看到该错误

【讨论】:

不过,这是一个完全不同的问题。 我收到了上面列出的错误,这个解决方案适用于我的代码。这有什么不同?

以上是关于AttributeError:“UUID”对象在使用与后端无关的 GUID 类型时没有属性“replace”的主要内容,如果未能解决你的问题,请参考以下文章

从 PySpark 中的数据框中删除重复项

AttributeError: 'RDD' 对象没有属性 'show'

AttributeError:“NumpyArrayIterator”对象没有属性“类”

AttributeError:“模块”对象没有属性“WebSocketApp”

AttributeError:“模块”对象没有属性“作者”

初学者 Python:AttributeError:'list' 对象没有属性