Каков наилучший способ отслеживания окончания месяца для удаления строк для определенного ресурса? - PullRequest
0 голосов
/ 28 апреля 2019

Я делал простой API для отдыха с Flask, Flask-Restful и Flask-sqlAlchemy, где у меня есть пользовательский ресурс и ресурс предмета. Пользовательский ресурс является родительским ресурсом ресурса Item.

То есть

GET https://example.com/users/1/items получить весь ресурс предмета

Я хочу автоматически удалить все элементы после каждого месяца. Проблема в том, как отследить конец месяца?

Я провел некоторое исследование и нашел python datetime и библиотеку календаря.

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

Вот так я могу вычислить дату начала и окончания

import datetime
import calendar

today = datetime.datetime.today()



days_in_current_month = calendar.monthrange(today.year, today.month)[1]

days_till_this_month_end = days_in_current_month -today.day

start_date = today + datetime.timedelta(days = days_till_this_month_end +1)
end_date = start_date

while some_condition:
    # details of the condision

days_in_current_month = calendar.monthrange(end_date.year, end_date.month)[1]

end_date  = end_date + datetime.timedelta(days = days_till_this_month_end +1)

Это мой UserModel:

class UserModel(db.Model):
    __tablename__ = "users"

    id = db.Column(db.Integer, primary_key = True)
    firstname = db.Column(db.String(80))
    lastname = db.Column(db.String(80))
    email = db.Column(db.String(255))
    password = db.Column(db.String())
    items = db.relationship('ItemModel', lazy = "dynamic")

Это мой ItemModel:

class ItemModel(db.Model):
    __tablename__ = "categories"

    id = db.Column(db.Integer, primary_key = True)
    name = db.Column(db.String(255))
    date = db.Column(db.DateTime, default=datetime.utcnow)

    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))

    user = db.relationship('UserModel')

и, наконец, удалить элементы, которые я могу сделать ниже:

@staticmethod
def delete_from_db():
    ItemModel.query.filter(start_date <= end_date).delete()
    db.session.commit()

1 Ответ

2 голосов
/ 28 апреля 2019

Ваш фильтр запросов ItemModel не указывает ни одного столбца;выражение start_date <= end_date всегда равно True (поскольку значение end_date всегда будет позже, чем ваше start_date).Вы также усложняете дело.

Все, что вам нужно сделать, это удалить все строки до начала текущего месяца.База данных возьмет это оттуда.Начало текущего месяца тривиально определяется следующим образом:

import datetime

today = datetime.date.today()
month_start = today.replace(day=1)  # set day of the month to 1

ItemModel.query.filter(ItemModel.date < month_start).delete()

Выполнение этого для каждого запроса достаточно дешево, особенно если индексирован столбец ItemModel.date.

Если вы нацеливаетесь наконкретной базы данных, то вы также можете использовать функции даты базы данных.Postgresql, например, имеет удобную date_trunc() функцию , которую можно использовать для указания, какие строки отбрасывать:

ItemModel.query.filter(
    ItemModel.date < func.date_trunc('month', func.current_date())
).delete()

При этом используется значение PostgreSQL CURRENT_DATE , усекает его до текущего месяца (который устанавливает день даты равным 1), затем удаляет все строки, предшествующие текущему месяцу.

Если вы считаете, что эти запросы на удаление слишком дороги для выполненияПри каждом отдельном запросе рассмотрите возможность создания табличного представления таблицы items и используйте это представление в своей модели SQLAlchemy.В запросе на просмотр отображаются только элементы текущего месяца:

CREATE OR REPLACE current_items WITH
SELECT * FROM items
WHERE items.date >= date_trunc(CURRENT_DATE, 'month')

и использование current_items в качестве имени таблицы для поддержки объектов ItemModel, а отдельный пакетный процесс удаляет все строки из элементовтаблица, которая старше, чтобы сохранить базу данных в чистоте.Таким образом, ваши запросы SQLAlchemy только когда-либо смогут увидеть элементы текущего месяца, и удаление будет выполняться один раз, пока пакетный процесс завершает удаление этих строк.

Вместо представления можно также применить SQLAlchemy.фильтр автоматически, через перехват событий.Это реализация фильтра с фильтром :

@event.listens_for(Query, "before_compile", retval=True)
def before_itemmodel_compile(query):
    """Limit all ItemModel queries to the current month"""
    if query._execution_options.get("include_all", False):
        return query

    for ent in query.column_descriptions:
        entity = ent['entity']
        if entity is None:
            continue
        insp = inspect(ent['entity'])
        mapper = getattr(insp, 'mapper', None)
        if mapper and mapper.class_ is ItemModel:
            today = datetime.date.today()
            month_start = today.replace(day=1)  # set day of the month to 1
            query = query.enable_assertions(False).filter(
                ent['entity'].date >= month_start)

    return query
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...