Не удается исправить ошибку объекта int, когда объект приходит из SQLAlchemy? - PullRequest
5 голосов
/ 21 октября 2011

Я использую YAML и SQLAlchemy. Я определил свой объект, и я могу использовать YAML, чтобы напечатать это просто отлично. Однако когда я пытаюсь использовать YAML для объекта, возвращенного из запроса SQLAlchemy, происходит сбой с ошибкой can't pickle int objects. Я распечатал экземпляр, возвращенный из SQLAlchemy, и он показывает правильный тип. Я позволю коду говорить:

class HashPointer(Base):
    __tablename__ = 'hash_pointers'

    id = Column(Integer, primary_key=True)
    hash_code = Column(VARBINARY(64), unique=True)
    file_pointer = Column(Text)

    def __init__(self, hash_code, file_pointer):
        self.hash_code = hash_code
        self.file_pointer = file_pointer

    def __repr__(self):
        return "<HashPointer('%s', '%s')>" % (self.hash_code, self.file_pointer)

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Engine = create_engine("mysql://user:pass@localhost/db", echo=True)
Session = sessionmaker(bind=Engine)
session = Session()
fhash = HashPointer(0x661623708235, "c:\\test\\001.txt")

# PRINTS FINE
print(yaml.dump(fhash))

for instance in session.query(HashPointer).all():
    # PRINTS FINE AS __repr__
    print instance

    # THROWS ERROR, 'CAN'T PICKLE INT OBJECTS'
    print(yaml.dump(instance))

Ответы [ 2 ]

2 голосов
/ 21 октября 2011

Попробуйте добавить в свой класс следующее:

def __reduce__(self):
    'Return state information for pickling'
    return self.__class__, (int(self.hash_code), str(self.file_pointer))
1 голос
/ 17 октября 2014

Оказывается, что метод по умолчанию redu_ex (я уверен, что это тот, который в object (), но это не обязательно.), Который идет по линии, когда у вас активна sqlalchemy, добавляет член _sa_instance_state к состоянию, возвращаемому в API redu_ex , который PyYAML использует для выполнения сериализации.

При сериализации объекта, полученного из запроса SqlAlchemy, это по существускрытая часть метаданных объекта, доступная для дальнейших операций.

Именно в этом объекте происходит сбой сериализатора PyYAML.Вы можете убедиться в этом, запустив сериализацию в PDB и увидев два вызова представления_объекта в стеке вызовов, даже для относительно простых результатов объекта запроса SQLAlchemy.

Эта ссылка на экземпляр запроса используется,Насколько я понимаю, использование методов с помощью позволяет вам вернуться к запросу, который генерирует данный объект в течение времени жизни того же интерпретатора Python.

Если вам нужна эта функциональность (например, session.new & session.грязный), вам нужно будет реализовать поддержку для этого в сериализаторе PyYAML.

Если вам все равно, и вы просто хотите, чтобы ваши объявленные члены, вы можете использовать базовый класс, который «скрывает» эту связь от вызовов к уменьшение * - обратите внимание, что это также нарушит расширение сериализатора SQLAlchemy, поэтому внимательно рассмотрите свои планы.

Пример базового класса для реализации этого изменения:

DeclBase = declarative_base()

class Base(DeclBase):
    __abstract__ = True

    def __reduce_ex__(self, proto):
        ret = super(Base, self).__reduce_ex__(proto)
        ret = ( ret[0], ret[1], dict(ret[2]) ) + ret[3:]
        ret[2].pop('_sa_instance_state', None) # remove bad yamly from reduce state
        return ret

Это позволит вам отправлять туда и обратно ваши объекты в / из yaml, хотя и туда и обратноотменит их от любых ожидающих транзакций или запросов.Это также может иметь взаимодействие, если вы используете ленивые загруженные элементы, например.Убедитесь, что вы сериализуете все, что ожидаете.

ПРИМЕЧАНИЕ / РЕДАКТИРОВАТЬ: Я решил использовать здесь redu_ex , чтобы быть совместимым с возможными другими базовыми классами или миксинами.Согласно https://docs.python.org/2/library/pickle.html#object.reduce_ex, это приведет к правильному поведению для любых базовых классов, а также определит, было ли объявлено только lower ().

Redux ... lower возвращает действительное значение dict объекта экземпляра - мы не хотим удалять оттуда, поэтому для __reduce * мы должны фактически сделать мелкую копию этого диктанта.

...