SQLAlchemy - обновить объект временным объектом - PullRequest
0 голосов
/ 05 февраля 2019

Я пытаюсь использовать flask-rest-api в качестве основы для простого примера веб-сервиса с SQLAlchemy в качестве ORM.Все работает отлично, за исключением случаев, когда дело касается обновлений.

Вот код, о котором идет речь:

    @blp.arguments(DogSchema)
    @blp.response(DogSchema)
    def put(self, data, dog_id):
        """Update existing dog"""
        try:
            dog = Dog.query.get(dog_id)
        except Exception as e:
            abort(404, message="Item not found - %s" % e)

        # update the dog here
        return dog

Когда это выполняется, переменная data является временным объектом модели Dog.flask-rest-api заботится о десериализации данных в запросе JSON, просмотре вашей схемы Marshmallow и создании объекта модели sqlalchemy правильного типа.

Вот моя модель и схемы для полноты:

class Dog(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String, nullable=False)


@api.definition("Dog")
class DogSchema(ma.ModelSchema):
    class Meta:
        model = Dog
        strict = True
        ordered = True

    id = field_for(Dog, 'id', dump_only=True)

То, что я хотел бы сделать, это просто взять временный объект и обновить объект в сеансе со всеми атрибутами.

Мне удалось сделать это двумя разными способами, нииз которых мне нравится, и я надеюсь, что есть лучший способ сделать это.

1) через слияние

    @blp.arguments(DogSchema)
    @blp.response(DogSchema)
    def put(self, data, dog_id):
        """Update existing dog"""
        try:
            dog = Dog.query.get(dog_id)
        except Exception as e:
            abort(404, message="Item not found - %s" % e)

        data.id = dog_id
        db.session.merge(data)
        db.session.commit()
        return dog

Это работает, но слияние, похоже, имеет другие побочные эффекты - этоизвлекает из базы данных, если записи нет в сеансе, и если ее нет в базе данных, добавляет ее.

2) С помощью сериализации зефира

    @blp.arguments(DogSchema)
    @blp.response(DogSchema)
    def put(self, data, dog_id):
        """Update existing dog"""
        try:
            dog = Dog.query.get(dog_id)
        except Exception as e:
            abort(404, message="Item not found - %s" % e)

        serializer = DogSchema()
        serializer.load(serializer.dump(data).data, instance=dog, session=db.session, partial=True)
        db.session.commit()
        return dog

ИспользуетсяMarshmallow, чтобы сериализовать переменную data обратно в JSON, а затем загрузить ее обратно, что кажется довольно неэффективным.

Мне бы очень хотелось, чтобы было что-то столь же простое, как

    @blp.arguments(DogSchema)
    @blp.response(DogSchema)
    def put(self, data, dog_id):
        """Update existing dog"""
        try:
            dog = Dog.query.get(dog_id)
        except Exception as e:
            abort(404, message="Item not found - %s" % e)

        dog.update(data)
        db.session.commit()
        return dog

но это не похоже нараунд, так что надеюсь, что кто-то может мне помочь.

Спасибо!

1 Ответ

0 голосов
/ 05 февраля 2019

Вы можете указать update метод для вас Dog класс:

class Dog(db.Model):
    ...
    def update(self, data):
        for k, v in data.items():
            settatr(self, k, v)
        return self

И затем использовать его, как вы хотели:

    try:
        dog = Dog.query.get(dog_id)
    except Exception as e:
        abort(404, message="Item not found - %s" % e)

    dog.update(data)
    db.session.commit()
...