Mysql Python Sqlalchemy二进制列类型HEX()和UNHEX()

Mysql Python Sqlalchemy二进制列类型HEX()和UNHEX(),mysql,python-2.7,sqlalchemy,Mysql,Python 2.7,Sqlalchemy,我正在尝试学习Sqlalchemy并使用ORM。我的一列将文件哈希存储为二进制。在SQL中,select将是 从hash=UNHEX('somehash')的表中选择类型、列。 如何使用我的ORM实现这样的选择(理想情况下还有一个插入示例)?我已经开始阅读有关列覆盖的内容,但我不清楚/不确定这是否是我真正想要的 乙二醇 res=session.query.filter(Model.hash==\uuuuu something\uuuuuu?) 想法?仅适用于选择和插入 那么,对于select,您

我正在尝试学习Sqlalchemy并使用ORM。我的一列将文件哈希存储为二进制。在SQL中,select将是

从hash=UNHEX('somehash')的表中选择类型、列。

如何使用我的ORM实现这样的选择(理想情况下还有一个插入示例)?我已经开始阅读有关列覆盖的内容,但我不清楚/不确定这是否是我真正想要的

乙二醇
res=session.query.filter(Model.hash==\uuuuu something\uuuuuu?)

想法?

仅适用于选择和插入 那么,对于select,您可以使用:

>>> from sqlalchemy import func
>>> session = (...)
>>> (...)
>>> engine = create_engine('sqlite:///:memory:', echo=True)
>>> q = session.query(Model.id).filter(Model.some == func.HEX('asd'))
>>> print q.statement.compile(bind=engine)
SELECT model.id
FROM model
WHERE model.some = HEX(?)
插入:

>>> from sqlalchemy import func
>>> session = (...)
>>> (...)
>>> engine = create_engine('sqlite:///:memory:', echo=True)
>>> m = new Model(hash=func.HEX('asd'))
>>> session.add(m)
>>> session.commit()
INSERT INTO model (hash) VALUES (HEX(%s))
更好的方法:使用sql函数转换数据的自定义列 但是,我认为最适合你的是使用any,看看这个

检查下面的代码,它将创建一个我认为适合您需要的自定义列:

from sqlalchemy.types import VARCHAR
from sqlalchemy import func

class HashColumn(VARCHAR):

    def bind_expression(self, bindvalue):
        # convert the bind's type from String to HEX encoded 
        return func.HEX(bindvalue)

    def column_expression(self, col):
        # convert select value from HEX encoded to String
        return func.UNHEX(col)
您可以将表格建模为:

from sqlalchemy import Column, types
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Model(Base):
    __tablename__ = "model"
    id = Column(types.Integer, primary_key=True)
    col = Column(HashColumn(20))

    def __repr__(self):
        return "Model(col=%r)" % self.col
一些用法:

>>> (...)
>>> session = create_session(...)
>>> (...)
>>> model = Model(col='Iuri Diniz')
>>> session.add(model)
>>> session.commit()
这将发出以下查询:

INSERT INTO model (col) VALUES (HEX(?)); -- ('Iuri Diniz',)
SELECT 
    model.id AS model_id, UNHEX(model.col) AS model_col 
FROM model 
LIMIT ? ; -- (1,)
SELECT 
    model.id AS model_id, UNHEX(model.col) AS model_col 
FROM model 
WHERE model.col = HEX(?) 
LIMIT ? ; -- ('Iuri Diniz', 1)
更多用法:

>>> session.query(Model).first()
Model(col='Iuri Diniz')
这将发出以下查询:

INSERT INTO model (col) VALUES (HEX(?)); -- ('Iuri Diniz',)
SELECT 
    model.id AS model_id, UNHEX(model.col) AS model_col 
FROM model 
LIMIT ? ; -- (1,)
SELECT 
    model.id AS model_id, UNHEX(model.col) AS model_col 
FROM model 
WHERE model.col = HEX(?) 
LIMIT ? ; -- ('Iuri Diniz', 1)
还有一点:

>>> session.query(Model).filter(Model.col == "Iuri Diniz").first()
Model(col='Iuri Diniz')
这将发出以下查询:

INSERT INTO model (col) VALUES (HEX(?)); -- ('Iuri Diniz',)
SELECT 
    model.id AS model_id, UNHEX(model.col) AS model_col 
FROM model 
LIMIT ? ; -- (1,)
SELECT 
    model.id AS model_id, UNHEX(model.col) AS model_col 
FROM model 
WHERE model.col = HEX(?) 
LIMIT ? ; -- ('Iuri Diniz', 1)
额外:使用python类型转换数据的自定义列 也许您想使用一些漂亮的自定义类型,并希望在python和数据库之间进行转换

在下面的示例中,我在python和数据库之间转换UUID(代码基于此):


由于以下错误,我无法使@iuridiniz的自定义列解决方案正常工作:

sqlalchemy.exc.StatementError: (builtins.TypeError) encoding without a string argument
对于以下表达式:

m = Model(col='FFFF')
session.add(m)
session.commit()
我通过重写处理参数的
process\u bind\u param
解决了这个问题 在将其传递给
bind_expression
以插入查询语言之前

from sqlalchemy.types import VARCHAR
from sqlalchemy import func

class HashColumn(VARCHAR):

    def process_bind_param(self, value, dialect):
        # encode value as a binary
        if value:
            return bytes(value, 'utf-8')

    def bind_expression(self, bindvalue):
        # convert the bind's type from String to HEX encoded
        return func.HEX(bindvalue)

    def column_expression(self, col):
        # convert select value from HEX encoded to String
        return func.UNHEX(col)
然后定义表是一样的:

from sqlalchemy import Column, types
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Model(Base):
    __tablename__ = "model"
    id = Column(types.Integer, primary_key=True)
    col = Column(HashColumn(20))

    def __repr__(self):
        return "Model(col=%r)" % self.col
我真的很喜欢一种更好的方法:使用sql函数转换数据的自定义列,但在MySQL 5.7中使用BINARY和VARBINARY存储十六进制字符串时,我遇到了一些问题。我尝试了不同的方法,但SQLAlchemy一直在抱怨编码,和/或在无法使用它们的上下文中使用
func.HEX
func.UNHEX
。使用python3和SQLAlchemy 1.2.8,我成功地扩展了基类并替换了它的处理器,因此SQLAlchemy不需要数据库中的函数来绑定数据和计算结果,而是在python中完成,如下所示:

import codecs
from sqlalchemy.types import VARBINARY

class VarBinaryHex(VARBINARY):
    """Extend VARBINARY to handle hex strings."""

    impl = VARBINARY

    def bind_processor(self, dialect):
        """Return a processor that decodes hex values."""
        def process(value):
            return codecs.decode(value, 'hex')
        return process

    def result_processor(self, dialect, coltype):
        """Return a processor that encodes hex values."""
        def process(value):
            return codecs.encode(value, 'hex')
        return process

    def adapt(self, impltype):
        """Produce an adapted form of this type, given an impl class."""
        return VarBinaryHex()
我们的想法是用python函数代替需要DBMS干预的
HEX
UNHEX
,python函数的作用与HEX和UNHEX一样,对十六进制字符串进行编码和解码。如果您直接连接到数据库,则可以使用HEX和UNHEX,但从SQLAlchemy开始,
codecs.enconde
codecs.decode
函数可以为您工作

我敢打赌,如果有人对编写适当的处理器感兴趣,甚至可以从python的角度将十六进制值作为整数进行管理,从而允许存储大于BIGINT的整数

一些考虑:

  • 如果十六进制字符串的长度已知,则可以使用
    BINARY
    代替
    VARBINARY
  • 根据您将要执行的操作,在将要使用此类列的类的构造函数上取消/大写字符串可能是值得的,这样您就可以在对象初始化时使用一致的大写。i、 例如,
    'aa'!='AA'
    0xaa==0xaa
  • 如前所述,您可以考虑将DB二进制十六进制值转换为PRYthon整数的处理器。
  • 使用
    VARBINARY
    时要小心,因为
    'aa'!='00aa'
  • 如果使用
    BINARY
    ,假设您的列是
    col=column(BinaryHex(length=4))
    ,请考虑您提供的小于
    length
    字节的任何值都将以零完成。我是说,如果你这样做了
    obj.col='aabb'
    并提交它,当您稍后从Database中检索它时,您将得到的是
    obj.col=='aabb0000'
    ,这是完全不同的

您正在使用varchar而不是binary作为guid字段。@Ricardo,这只是一个示例,只是为了演示如何在python数据类型和db数据类型之间转换。func.HEX和func.UNHEX是否已弃用?代码不起作用后,无论输入如何,都会返回相同的对象,并且该值似乎隐藏在对象中。另外,我找不到任何关于这两个功能的文档,这里发生了什么事??@Clocker和@MatthewTrevor。我现在明白了。(at)Clocker,根据:“可以为func指定任何名称。如果SQLAlchemy不知道函数名,它将按原样呈现。”