Flask Коммит () сеансов SQLAlchemy не работает "иногда" - PullRequest
0 голосов
/ 07 мая 2020

Я работаю в веб-приложении какое-то время, и впервые осознаю эту проблему, я думаю, что это может быть связано с тем, как обрабатываются сеансы SQLAlchemy, поэтому некоторые пояснения в простом виде были бы полезны. Моя конфигурация для работы с flask sqlAlchemy:

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)

Моя проблема: db.session.commit () иногда не сохраняет изменения. Я написал несколько flask конечных точек, которые достигаются через запросы внешнего интерфейса в браузере пользователя. В этом конкретном случае я редактирую объект «Бронирование» отеля, изменяя столбцы «Номера», которые являются текстовым полем.

функция выполняет следующие действия:

1 - запрашивает объект Booking по датам в запросе

2 - Редактирует столбец Rooms этого объекта Booking

3- Зафиксируйте изменения "db.session.commit ()"

4- Если у пользователя активна функция X, я провожу несколько проверок, вызывая вторую функцию:

· 4.1- Эти функции выполняют некоторые проверки, запрашивают и редактируют другой объект в базе данных, отличный от объекта «Бронирование», который я редактировал ранее.

· 4.2- В конце этой вторичной функции я вызываю db.session.commit () «Обратите внимание, что эти изменения всегда правильно сохраняются в базе данных»

· 4.3 - Вернуть результаты предыдущей функции

5 - Вернуть результаты во внешний интерфейс («непосредственно перед этим возвратом я распечатайте Booking.Rooms, чтобы убедиться, что он выглядит так, как должен, и это так ... Я даже пытался сделать вторую фиксацию после печати, но до возврата ... Но после этого иногда Booking.Rooms обновляются, как ожидалось но некоторые другие Иногда это не так ... Я заметил, что если повторить действие много раз, оно, наконец, сработает, но, учитывая, что промежуточная функция, "описанная в пункте 4", правильно сохраняет все его изменения, это вызывает несогласованность данных и сводит меня с ума, потому что если я повторяю действие и процедуру в функции пункта 4, я не могу повторить действие мода Rooms ...

Итак, теперь я действительно запутался, если это то, что я не понимаю из flask сеансов, насколько я понимаю, всякий раз, когда я делаю новый запрос к flask, это изолированный сеанс, верно? Я имею в виду, что если 2 одновременных пользователя сохраняют некоторые изменения в базе данных, db.session.commit () от одного из пользователей не фиксирует изменения от другого, верно?

То же самое, если я вызываю db.session.commit () в одном запросе, эти изменения сохраняются в базе данных, и если после этого «в том же запросе» я продолжаю изменять вещи, это похоже на другой сеанс, верно? И зафиксированные изменения там уже надежно сохранены? И я все еще могу использовать предыдущие объекты для дальнейших модификаций.

В любом случае, все это не должно быть проблемой, потому что после commit () я распечатываю Booking.Rooms и выглядит так, как ожидалось ... А иногда это работает правильно, а иногда нет ...

Также обратите внимание: когда я возвращаю этот результат клиенту, клиент мгновенно делает второй запрос к серверу, чтобы запросить обновленные данные бронирования, а затем данные возвращаются без фиксации ожидаемых изменений ... Я полагаю, что flask обработал все commit () до того, как получит второй запрос, «иначе он бы не вернул результат ранее ...»

Может ли это быть ограничением сервера разработки flask, который не может правильно обрабатывать множество запросов и что при развертывании с помощью Gunicorn этого не происходит?

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

И как запрос ed вот код, я знаю, что его невозможно воспроизвести, у него много настроек и потребуется много данных для работы, как задумано, при тех же обстоятельствах, что и в моем случае, но это должно дать обзор того, как выглядят функции как и где коммиты, о которых я упоминал выше. Любые идеи о том, где может быть проблема, очень полезны.

#Main function hit by the frontend
@users.route('/url_endpoint1', methods=['POST'], strict_slashes=False)
@login_required
def add_room_to_booking_Api():
    try:
        bookingData = request.get_json()
        roomURL=bookingData["roomSafeURL"]
        targetBooking = bookingData["bookingURL"]
        startDate = bookingData["checkInDate"]
        endDate = bookingData["checkOutDate"]
        roomPrices=bookingData["roomPrices"]

        booking = Bookings.query.filter_by(SafeURL=targetBooking).first() 
        alojamiento = Alojamientos.query.filter_by(id=reserva.CodigoAlojamiento).first() #owner of the booking
        room=Rooms.query.filter_by(SafeURL=roomURL).first()
        roomsInBooking=ast.literal_eval(reserva.Habitaciones) #I know, should be json.loads() and json.dumps() for better performance probably...

        #if room is available for given days add it to the booking
        if CheckIfRoomIsAvailableForBooking(alojamiento.id, room, startDate, endDate, booking) == "OK":

            roomsInBooking.append({"id": room.id, "Prices": roomPrices, "Guests":[]}) #add the new room the Rooms column of the booking
            booking.Habitaciones = str(roomsInBooking)#save the new rooms data
            print(booking.Habitaciones) # check changes applied
            room.ReservaAsociada = booking.id  # associate booking and room
            for ocupante in room.Ocupantes: #associate people in the room with the booking
                ocupante.Reserva = reserva.id

            #db.session.refresh(reserva) # test I made to check if something changes but didn't worked
            if some_X_function() == True: #if user have some functionality enabled
                #db.session.begin() #another test which didn't worked
                RType = WuBook_Rooms.query.filter_by(ParentType=room.Tipo).first()
                RType=[RType] #convert to list because I resuse the function in cases with multiple types
                resultAdd = function4(RType, booking.Entrada.replace(hour=0, minute=0, second=0), booking.Salida.replace(hour=0, minute=0, second=0))
                if resultAdd["resultado"] == True:  # "resultado":error, "casos":casos
                    return (jsonify({"resultado": "Error", "mensaje": resultAdd["casos"]}))

            print(booking.Habitaciones) #here I still get expected result
            db.session.commit()
            #I get this return of averything correct in my frontend but not really stored in the database
            return jsonify({"resultado": "Ok", "mensaje": "Room " + str(room.Identificador) + " added to the booking"})

        else:
            return (jsonify({"resultado": "Error", "mensaje": "Room " + str(room.Identificador) + " not available to book in target dates"}))

    except Exception as e:
        #some error handling which is not getting hit now
        db.session.rollback()
        print(e, ": en linea", lineno())
        excepcion = str((''.join(traceback.TracebackException.from_exception(e).format()).replace("\n","</br>"), "</br>Excepcion emitida ne la línea: ", lineno()))
        sendExceptionEmail(excepcion, current_user)
        return (jsonify({"resultado":"Error","mensaje":"Error"}))

#function from point 4
def function4(RType, startDate, endDate): 
    delta = endDate - startDate
    print(startDate, endDate)
    print(delta)
    for ind_type in RType: 
        calendarUpdated=json.loads(ind_type.updated_availability_by_date)
        calendarUpdatedBackup=calendarUpdated
        casos={}
        actualizar=False
        error=False
        for i in range(delta.days):
            day = (startDate + timedelta(days=i))
            print(day, i)
            diaString=day.strftime("%d-%m-%Y")
            if day>=datetime.now() or diaString==datetime.now().strftime("%d-%m-%Y"): #only care about present and future dates
                disponibilidadLocal=calendarUpdated[diaString]["local"]
                yaReservadas=calendarUpdated[diaString]["local_booked"]
                disponiblesChannel=calendarUpdated[diaString]["avail"]
                #adjust availability data
                if somecondition==True:
                    actualizar=True
                    casos.update({diaString:"Happened X"})
                else:
                    actualizar=False
                    casos.update({diaString:"Happened Y"})
                    error="Error"
        if actualizar==True: #this part of the code is hit normally and changes stored correctly
            ind_type.updated_availability_by_date=json.dumps(calendarUpdated)
            wubookproperty=WuBook_Properties.query.filter_by(id=ind_type.PropertyCode).first()
            wubookproperty.SyncPending=True
            ind_type.AvailUpdatePending=True
        elif actualizar==False: #some error occured, revert changes
            ind_type.updated_availability_by_date = json.dumps(calendarUpdatedBackup)

    db.session.commit()#this commit persists 
    return({"resultado":error, "casos":casos}) #return to main function with all this chnages stored

1 Ответ

0 голосов
/ 07 мая 2020

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

...