Як оновити запис рядків SQLAlchemy?


138

Припустимо , таблиця має три колонки: username, passwordі no_of_logins.

Коли користувач намагається увійти, він перевіряється на запит типу запиту

user = User.query.filter_by(username=form.username.data).first()

Якщо пароль збігається, він продовжується далі. Що я хотів би зробити, це підрахувати, скільки разів користувач увійшов у систему. Таким чином, кожного разу, коли він успішно входить у систему, я хотів би збільшити no_of_loginsполе і зберегти його назад до таблиці користувачів. Я не впевнений, як запустити запит на оновлення за допомогою SqlAlchemy.

Відповіді:


132
user.no_of_logins += 1
session.commit()

12
stackoverflow.com/a/2334917/125507 каже, що це поганий спосіб зробити це. Також що робити, якщо рядок ще не існує?
ендоліт

6
Згідно з посиланням на ендоліт, user.no_of_logins += 1можна створити умови для перегонів. Замість використання user.no_of_logins = user.no_of_logins + 1. Перекладений в SQL останній правильний шлях буде виглядати так : SET no_of_logins = no_of_logins + 1.
ChaimG

5
@ChaimG Я думаю, ви мали на увазі user.no_of_logins = User.no_of_logins + 1, або іншими словами, використовуйте інструментальний атрибут моделі для створення вираження SQL. Оскільки ваш коментар відображає два способи створити однакові умови гонки.
Ilja Everilä

2
Вони не. Перший встановлює атрибут виразу SQL, останній виконує додавання на місці в Python і знову вводить гонку.
Ilja Everilä

1
@jayrizzo Я не такий знайомий із рівнем ізоляції транзакцій SERIALIZABLE, але, наскільки я розумію, це дозволить виконувати додавання в Python, використовуючи додаток на місці. Якщо буде гонка, одна з транзакцій буде тоді успішною, а інші вийдуть з ладу і повинні повторити спробу (з новим станом БД). Але я, можливо, неправильно зрозумів СЕРІАЛЬНІСТЬ.
Ilja Everilä

343

Існує кілька способів UPDATEвикористанняsqlalchemy

1) user.no_of_logins += 1
   session.commit()

2) session.query().\
       filter(User.username == form.username.data).\
       update({"no_of_logins": (User.no_of_logins +1)})
   session.commit()

3) conn = engine.connect()
   stmt = User.update().\
       values(no_of_logins=(User.no_of_logins + 1)).\
       where(User.username == form.username.data)
   conn.execute(stmt)

4) setattr(user, 'no_of_logins', user.no_of_logins+1)
   session.commit()

3
Я використовую setattr(query_result, key, value)для деяких оновлень у Flask SQLAlchemy з подальшим введенням. Будь-яка причина знизити цю модель?
Марк

2
@datamafia: Ви можете використовувати setattr(query_result, key, value), що абсолютно рівнозначно написаннюquery_result.key = value
Німа Соруш,

Thx @ NimaSoroush, це те, що я роблю, і так рівнозначно.
Марк

8
Чи можна пояснити відмінності між цими випадками? Дякую!
Хатшепсут

1
@Hatshepsut Одна різниця, яку я натрапив сьогодні між 4 та 1/2, - це за адресою: groups.google.com/forum/#!topic/sqlalchemy/wGUuAy27otM
Vikas Prasad

13

Приклади для з'ясування важливого питання в коментарях прийнятої відповіді

Я не розумів цього, доки сам з ним не розігрувався, тому зрозумів, що будуть і інші, хто також збентежений. Скажіть, ви працюєте над користувачем, чий id == 6і чий, no_of_logins == 30коли ви починаєте.

# 1 (bad)
user.no_of_logins += 1
# result: UPDATE user SET no_of_logins = 31 WHERE user.id = 6

# 2 (bad)
user.no_of_logins = user.no_of_logins + 1
# result: UPDATE user SET no_of_logins = 31 WHERE user.id = 6

# 3 (bad)
setattr(user, 'no_of_logins', user.no_of_logins + 1)
# result: UPDATE user SET no_of_logins = 31 WHERE user.id = 6

# 4 (ok)
user.no_of_logins = User.no_of_logins + 1
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6

# 5 (ok)
setattr(user, 'no_of_logins', User.no_of_logins + 1)
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6

Точка

Посилаючись на клас замість екземпляра, ви можете зробити SQLAlchemy розумнішою щодо збільшення, зробивши це на стороні бази даних, а не на стороні Python. Робити це в базі даних краще, оскільки вона менш вразлива для корупції даних (наприклад, два клієнти намагаються збільшити одночасно з чистим результатом лише один приріст замість двох). Я припускаю, що можна зробити приріст у Python, якщо встановити блокування або збільшити рівень ізоляції, але навіщо турбуватися, якщо цього не потрібно?

Застереження

Якщо ви збираєтесь збільшувати двічі за допомогою коду, що створює подібний SQL SET no_of_logins = no_of_logins + 1, тоді вам потрібно буде здійснити або принаймні прострочити між кроками, інакше ви отримаєте лише один приріст:

# 6 (bad)
user.no_of_logins = User.no_of_logins + 1
user.no_of_logins = User.no_of_logins + 1
session.commit()
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6

# 7 (ok)
user.no_of_logins = User.no_of_logins + 1
session.flush()
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6
user.no_of_logins = User.no_of_logins + 1
session.commit()
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6

Привіт, дякую за вашу відповідь, замість змінної int я намагаюсь оновити струнну змінну, як це зробити? Я використовую базу даних sqlite, і змінні, які я хочу змінити, знаходяться в current_user, надсилаючи форму
dani bilel

1
@danibilel Ознайомтеся з документацією, яка досить ретельно. Ця частина підручника проходить через оновлення рядкових полів: docs.sqlalchemy.org/en/13/orm/… . В іншому випадку я пропоную вам поставити нове запитання із зазначенням того, на чому ви конкретно застрягли.
MarredCheese

Дякую за посилання. Після того, як я провів цілий день на пошуках, я знаю, що моя проблема пов'язана з db.session.commit (), вона не зберігає зміни в консолі, як слід, я знайшла подібні запитання, але надала відповіді не працюй для мене, у фактичному веб-додатку це ще гірше! зміни взагалі не зберігаються, вибачте за довгий коментар, але я не можу додати питання через політику веб-сайту, будь-яка допомога буде вдячна.
dani bilel

4

За допомогою user=User.query.filter_by(username=form.username.data).first()оператора ви отримаєте вказаного користувача у userзмінній.

Тепер ви можете змінити значення нової змінної об'єкта на зразок user.no_of_logins += 1і зберегти зміни sessionметодом 'commit'.


2

Я написав бота телеграми і мав певну проблему з рядками оновлення. Скористайтеся цим прикладом, якщо у вас є Модель

def update_state(chat_id, state):
    try:
        value = Users.query.filter(Users.chat_id == str(chat_id)).first()
        value.state = str(state)
        db.session.flush()
        db.session.commit()
        #db.session.close()
    except:
        print('Error in def update_state')

Навіщо використовувати db.session.flush()? Ось чому >>> SQLAlchemy: Яка різниця між flush () та commit ()?


7
Рум'яна повністю надмірна перед фіксацією. Фіксація завжди неявно змивається. Як багато сказано в запитаннях, на які ви посилаєтесь: " flush()завжди називається частиною дзвінка commit()"
Ilja Everilä
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.