SQLAlchemy PickleType для определенного пользователем объекта - PullRequest
0 голосов
/ 27 февраля 2020

Я пытаюсь использовать поле SQLAlchemy PickleType для пользовательского объекта, который у меня есть.

вот моя модель:

class RouteModelStore(db.Model):
    __tablename__ = 'route_model_store'

    id = db.Column(db.Integer, primary_key=True)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow)
    origin = db.Column(db.String(255), index=True, nullable=False)
    dest = db.Column(db.String(255), index=True, nullable=False)
    route_key = db.Column(db.String(255))
    model = db.Column(db.PickleType, nullable=False) # this field
    scores = db.Column(db.PickleType, nullable=False)
    resolution = db.Column(db.String(255), nullable=False)

    def __repr__(self):
        return f"<RouteModel {self.origin}-{self.dest}>"

У меня изначально было только что определено поле PickleType, но после сохранения моего пользовательского объекта и загрузки его из БД я получаю эту ошибку:

model = RouteModel.query.filter(RouteModel.origin=='MKE', RouteModel.dest=='ATL')
model.model
AttributeError: 'RouteModel' object has no attribute '_sa_instance_state'

Я подумал, что это может быть из-за того, что ORM не знает, как десериализовать этот объект поэтому я создал настраиваемое поле через TypeDecorator с настраиваемым расширителем:

class CustomUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if name == 'RouteModel':
            return RouteModel
        return super().find_class(module, name)


class RouteModelPickle(types.TypeDecorator):
    impl = types.PickleType

    def convert_bind_param(self, value, engine):
        result = self.impl.convert_bind_param(value, engine)
        return result

    def process_result_value(self, value, dialect):
        return CustomUnpickler(
            io.BytesIO(value.read())).load()

, затем я изменил поле модели для этого нового типа столбца:

model = db.Column (RouteModelPickle, nullable = False)

однако теперь, когда я запускаю миграцию, я получаю ошибку:

  File "<frozen importlib._bootstrap_external>", line 674, in exec_module
  File "<frozen importlib._bootstrap_external>", line 781, in get_code
  File "<frozen importlib._bootstrap_external>", line 741, in source_to_code
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/cyrusghazanfar/Desktop/pilota/pilota_project/pilota_ml/migrations/versions/f49bcb85c25f_.py", line 28
    sa.Column('model', app.models.RouteModelPickle(pickler=<module 'pickle' from '/Users/cyrusghazanfar/anaconda3/lib/python3.6/pickle.py'>), nullable=False),

вот миграция, созданная (python manage.py db migrate)

def upgrade():
  ...
    sa.Column('model', app.models.RouteModelPickle(pickler=<module 'pickle' from '/Users/cyrusghazanfar/anaconda3/lib/python3.6/pickle.py'>), nullable=False),
  ...

Ох, и вот фактический объект, который я пытаюсь выбрать, сохранить / загрузить из БД:

class RouteModel:
    def __init__(self, route, pred_type='crs_arr'):
        self.route = route
        self.pred_type = pred_type
        self.rm_key = route[2:5] + '_' + route[9:12] + '_' + pred_type
        self.f_select = None
        self.model = None

    def train_model(self, x_train, y_train, time_cols):
        self.model = fit_rf(x_train, y_train)
        self.f_select = get_f_select(self.model, x_train, time_cols, num_f=20)
        self.model = fit_rf_cv(x_train[self.f_select],
                               y_train).best_estimator_.fit(x_train[self.f_select], y_train)

    def retrain_model(self, x_train, y_train):
        self.model = self.model.fit(x_train[self.f_select], y_train)

    def predict(self, x_pred):
        y_pred = self.model.predict(x_pred[self.f_select])

        return y_pred

    def predict_prob(self, x_pred):
        y_pred_prob = self.model.predict_proba(x_pred[self.f_select])

        return y_pred_prob

Любая помощь, пожалуйста:)

...