Дата-час за замовчуванням SQLAlchemy


174

Це моя декларативна модель:

import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Test(Base):
    __tablename__ = 'test'

    id = Column(Integer, primary_key=True)
    created_date = DateTime(default=datetime.datetime.utcnow)

Однак, коли я намагаюся імпортувати цей модуль, я отримую цю помилку:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "orm/models2.py", line 37, in <module>
    class Test(Base):
  File "orm/models2.py", line 41, in Test
    created_date = sqlalchemy.DateTime(default=datetime.datetime.utcnow)
TypeError: __init__() got an unexpected keyword argument 'default'

Якщо я використовую тип Integer, я можу встановити значення за замовчуванням. Що відбувається?


1
Це не слід використовувати. utcnow - це наївна часова марка в часовому поясі UTC, проте є ймовірність, що наївні часові позначки інтерпретуються натомість у локальному часовому поясі.
Антті Хаапала

1
Я знаю, що це питання було задано дуже давно, але я думаю, що вашу відповідь слід змінити на відповідну, яку надав @Jeff Widman, оскільки інша використовує часовий час "компілювати", коли клас таблиці визначається порівняно з тим, коли створюється запис. Якщо ви є АФК, то принаймні цей коментар буде попереджати інших уважно перевірити коментарі до кожного питання.
Девід

Відповіді:


186

DateTimeне містить ключ за замовчуванням як вхід. Клавіша за замовчуванням повинна бути входом до Columnфункції. Спробуйте це:

import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Test(Base):
    __tablename__ = 'test'

    id = Column(Integer, primary_key=True)
    created_date = Column(DateTime, default=datetime.datetime.utcnow)

13
Це неправильно. Часова позначка при завантаженні моделі буде використовуватися для всіх нових записів, а не часу додавання запису.
SkyLeach

8
@SkyLeach Цей код правильний, ви можете перевірити його тут: pastebin.com/VLyWktUn . datetime.datetime.utcnowвидається зворотним.
bux

1
Я використав цю відповідь, але позначка часу при завантаженні моделі використовується для всіх нових записів.
scottydelta

105
@scottdelta: Переконайтеся, що ви не використовуєте default=datetime.datetime.utcnow(); ви хочете передати utcnowфункцію, а не результат її оцінки при завантаженні модуля.
дисфункція

Ще не працювали для мене. Підхід Джеффа-Відмена працював на мене:from sqlalchemy.sql import func; created_date = Column(DateTime(timezone=True), server_default=func.now()
tharndt

396

Обчисліть часові позначки у вашій БД, а не у клієнта

Для розуму ви, мабуть, хочете мати всіх datetimes обчислено вашим сервером БД, а не сервером додатків. Обчислення часової позначки в додатку може призвести до проблем, оскільки затримка в мережі мінлива, клієнти відчувають дещо інший рух годинника, а різні мови програмування час від часу обчислюють час дещо інакше.

SQLAlchemy дозволяє зробити це шляхом передачі func.now()або func.current_timestamp()(вони є псевдонімами один одного), що повідомляє БД обчислити саму часову марку.

Використовуйте SQLALchemy server_default

Крім того, для дефолту, де ви вже говорите БД для обчислення значення, його краще використовувати server_defaultзамість цього default. Це вказує SQLAlchemy передавати значення за замовчуванням як частину CREATE TABLEоператора.

Наприклад, якщо ви пишете спеціальний сценарій проти цієї таблиці, використовуючи server_default засоби, вам не потрібно буде турбуватися про те, що вручну додавати виклик часової мітки до вашого сценарію - база даних встановить його автоматично.

Розуміння SQLAlchemy's onupdate/server_onupdate

SQLAlchemy також підтримує onupdateтак, що коли б оновлення рядка було вставлено нову часову позначку. Знову ж таки, краще сказати БД для розрахунку самої часової позначки:

from sqlalchemy.sql import func

time_created = Column(DateTime(timezone=True), server_default=func.now())
time_updated = Column(DateTime(timezone=True), onupdate=func.now())

Є server_onupdateпараметр, але на відміну від цього server_default, він насправді не встановлює нічого серверного. Він просто говорить SQLalchemy, що ваша база даних змінить стовпчик, коли відбудеться оновлення (можливо, ви створили тригер на стовпчику ), тому SQLAlchemy попросить повернути значення, щоб він міг оновити відповідний об'єкт.

Ще один потенційний готч:

Ви можете бути здивовані, помітивши, що якщо ви внесете купу змін в рамках однієї транзакції, всі вони мають однакову мітку часу. Це тому, що стандарт SQL вказує, що CURRENT_TIMESTAMPповертає значення на основі початку транзакції.

PostgreSQL надає не-SQL стандарту statement_timestamp()і clock_timestamp()які роблять зміни в рамках транзакції. Документи тут: https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT

UTC часова мітка

Якщо ви хочете використовувати часові позначки UTC, в документаціїfunc.utcnow() на SQLAlchemy міститься заголовок реалізації . Вам потрібно забезпечити відповідні функції, характерні для драйвера, самостійно.


1
Я бачив, що зараз є спосіб сказати Postgres використовувати поточний час, а не лише час початку транзакції ... це в документах postgres
Джефф Відман

2
є func.utcnow () чи щось подібне?
yerassyl

1
@JeffWidman: Чи є у нас реалізація mysql для utcnow? Я в документації тільки mssql і postreg
JavaSa

1
Це, справді, чудова відповідь!
Джон Мутума

3
server_default=func.now()працював на мене, але onupdate=func.now()не став. Спробував onupdate=datetime.datetime.utcnow(без дужок), що теж не допомогло
Вікас Прасад

55

Ви також можете використовувати функцію вбудованої sqlalchemy за замовчуванням DateTime

from sqlalchemy.sql import func

DT = Column(DateTime(timezone=True), default=func.now())

9
Ви також можете використовувати server_defaultзамість defaultso value will, обробляючись самою базою даних.
rgtk

2
Чи не func.now () виконується, коли дескриптор визначений, тому всі моделі / діти (якщо це базовий клас) матимуть однакове значення DT. При передачі посилання на функцію типу "datetime.datetime.utcnow" вона виконується окремо для кожного. Це має вирішальне значення для "створених" або "оновлених" властивостей.
Metalstorm

10
@Metalstorm Ні, це правильно. sqlalchemy.sql.func - це особливий випадок, який повертає екземпляр sqlalchemy.sql.functions.now, а не поточний час. docs.sqlalchemy.org/uk/latest/core/…
Кріс Харді

Що робить timezone=True?
ChaimG

1
@self, З документації : "Якщо True, і підтримується бекенд, створить" TIMESTAMP WITH TIMEZONE ". Для програм, які не підтримують часові позначки, відомі часовому поясу, це не впливає .
ChaimG

7

Ви, ймовірно, хочете використовувати onupdate=datetime.nowтак, щоб оновлення також змінювали last_updatedполе.

SQLAlchemy має два параметри за замовчуванням для виконуваних функцій python.

  • default встановлює значення на INSERT лише один раз
  • onupdateвстановлює значення для результату дзвінка також у UPDATE.

Я не знаю чому, але чомусь onupdateнічого не робить для мене.
Вікас Прасад

1
Нарешті я отримав це, працюючи над Postgres db, зробивши це: created_at = db.Column(db.DateTime, server_default=UtcNow())а updated_at = db.Column(db.DateTime, server_default=UtcNow(), onupdate=UtcNow())де UtcNowклас, як у class UtcNow(expression.FunctionElement): type = DateTime()нас,@compiles(UtcNow, 'postgresql') def pg_utc_now(element, compiler, **kw): return "TIMEZONE('utc', CURRENT_TIMESTAMP)"
Вікас Прасад

6

Параметр defaultключового слова повинен бути заданий об'єкту "Стовпець".

Приклад:

Column(u'timestamp', TIMESTAMP(timezone=True), primary_key=False, nullable=False, default=time_now),

Значенням за замовчуванням може бути дзвінок, який тут я визначив так.

from pytz import timezone
from datetime import datetime

UTC = timezone('UTC')

def time_now():
    return datetime.now(UTC)

Звідки береться time_now?
Кей - SE є зло

Дякую! Я припускав, що є вбудована функція з таким ім'ям, яку я якось не помітив.
Кей - SE є зло

абоdatetime.datetime.utcnow
serkef

1

Відповідно до документації PostgreSQL, https://www.postgresql.org/docs/9.6/static/functions-datetime.html

now, CURRENT_TIMESTAMP, LOCALTIMESTAMP return the time of transaction.

Це вважається особливістю: намір дозволити одній транзакції мати послідовне поняття "поточного" часу, так що кілька модифікацій в межах однієї транзакції мають однакову позначку часу.

Якщо ви не хочете позначити часові позначки транзакцій, ви можете використовувати команду statement_timestamp або clock_timestamp .

statement_timestamp ()

повертає час початку поточного оператора (точніше, час отримання останнього повідомлення команди від клієнта). statement_timestamp

clock_timestamp ()

повертає фактичний поточний час, і тому його значення змінюється навіть у межах однієї команди SQL.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.