Выберите диапазон даты и времени из текущей даты, а не из прошлой даты и времени

Я работаю над изучением RoR. У меня есть одно требование к вводу дат от пользователя, но это всегда должна быть текущая дата или будущее. Во входных данных пользователь вводит дату начала и дату окончания. В настоящее время я использую приведенный ниже синтаксис и вспомогательный класс для выбора даты и времени.

<div class = "field">
    <%= form.label :start_time %>
    <%= form.datetime_select :start_time %>
  </div>

Согласно текущей форме, это то, что пользователь может выбрать из формы:

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

Здесь я хочу получить информацию о будущих событиях от пользователя и сохранить в базе данных.

Ниже представлена ​​модель событий:

class Event < ApplicationRecord
  validates :start_time, presence: true
  validates :end_time, presence: true
  validate :valid_start_and_end_time

  def start_time
    super || end_time
  end

  private

  def valid_start_and_end_time
    start_time <= end_time
  end
end

и EventController:

class EventsController < ApplicationController
 before_action :set_event only: [:show, :edit, :update, :destroy]

  def index
    @event = Event.all.where('start_time > ?', Time.now).sort_by(&:start_time).sort_by(&:title)
  end

  def create 
      @event = Event.new(event_params)
      @event.save
  end
  
  private
  
  def event_params 
    params.require(:event).permit(:title, :start_time, :end_time)
  end 
end

По сути, что я хочу здесь сделать:
1. Мы хотим, чтобы пользователь ввел дату начала и время окончания, все это должно быть будущими событиями или, по крайней мере, добавить проверку.
2. В модели мы хотим отсортировать индекс событий по времени начала, а затем по заголовку.

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

🤔 А знаете ли вы, что...
Ruby on Rails позволяет использовать компоненты фронтенда, такие как AngularJS или React, с помощью API-режима.


88
1

Ответ:

Решено

1. Валидации

Вы можете проверить, что start_time не может находиться в прошлом, с помощью валидатора сравнения (добавленного в Rails 7):

validates :start_time, comparison: { greater_than_or_equal_to: Time.current }

Вы также можете проверить, что end_time должен быть после start_time или равен ему, с помощью валидатора сравнения:

validates :end_time, comparison: { greater_than_or_equal_to: :start_time }

Эти две проверки также проверяют наличие атрибута по умолчанию, поэтому с их помощью вы гарантируете, что start_time и end_time оба присутствуют, а не в прошлом, и что end_time находится после start_time или равен ему.

Это означает, что вы можете удалить validates :start_time, presence: true, validates :end_time, presence: true и свой собственный validate :valid_start_and_end_time.

2. Области применения

Вы можете отсортировать события по start_time, а затем по названию, например:

Event.order(start_time: :asc, title: :asc)

что преобразуется в этот SQL-запрос:

SELECT
  events.*
FROM
  events
ORDER BY
  events.start_time ASC,
  events.title ASC

и дает вам события, упорядоченные по start_date (от самых старых до самых новых), а затем по title (в алфавитном порядке).

В Rails эти часто используемые запросы очень часто встречаются в областях . Вы бы написали это так:

scope :sorted, -> { order(start_time: :asc, title: :asc) }

После определения этой области вместо Event.order(start_time: :asc, title: :asc) вы можете использовать Event.sorted.

Поскольку вы используете where('start_time > ?', Time.now) в контроллере событий, я предполагаю, что вам также нужна область для этого, которую вы можете определить следующим образом:

scope :upcoming, -> { where('start_time > ?', Time.current) }

Ваше индексное действие теперь можно переписать так:

def index
  @events = Event.upcoming.sorted
end

который выполняет этот SQL-запрос:

SELECT
  events.*
FROM
  events
WHERE
  (start_time > '2024-05-10 21:03:50.413480') -- the current time
ORDER BY
  events.start_time ASC,
  events.title ASC