Запрос записи перед вставкой с помощью Ecto (аналогично обратному вызову AR) - PullRequest
1 голос
/ 08 марта 2019

Я новичок в Elixir и Phoenix (менее 10 дней), но очень взволнован этим, и, как и многие другие, я родом из Rails.

Я понимаю, что Ecto не является AR, и обратные вызовы устарели или удалены, но мне нужно добавить пользовательскую проверку, которая должна выполняться только при создании и должна выполнять запрос.

Вот как выглядит моя Reservation модель.

schema "reservations" do
  field :ends_at, :utc_datetime
  field :name, :string, null: false
  field :starts_at, :utc_datetime
  field :user_id, :id
end

и затем у меня есть другая схема Slot, которая выглядит следующим образом:

schema "slots" do
  field :ends_at, :utc_datetime
  field :name, :string, null: false
  field :starts_at, :utc_datetime
  field :admin_id, :id
end

Всякий раз, когда я добавляю новое резервирование, мне нужно запросить мою БД, чтобы проверить, есть ли какие-либо слоты с совпадающими ends_at и starts_at. Если есть, мне нужно предотвратить сохранение записи и добавить к ней ошибку (аналогично тому, что в Rails мы выполняем с throw :abort и errors.add).

Может кто-нибудь, пожалуйста, пролить свет на это? Как Экто это делает?

С уважением

1 Ответ

1 голос
/ 08 марта 2019

* edit: добавлены примеры использования отдельных наборов изменений для создания и обновления

Вы можете добавить пользовательскую функцию проверки в цепочку проверки набора изменений и выполнять в ней запросы к БД.

Не запущенэтот код, но что-то вроде этого должно работать

# separate changeset for creation
def create_changeset(struct, params) do
  struct
  |> cast(params, [...list of fields...])
  |> validate_unique([:name]) # lets say it has to be unique
  |> validate_slots # -- custom validation
end

# separate changeset for updation, no slot-check
def update_changeset(struct, params) do
  struct
  |> cast(params, [...list of fields...])
  |> validate_unique([:name]) # lets say it has to be unique
end


def validate_slots(changeset) do
    starts_at = get_field(changeset, :starts_at)
    ends_at = get_field(changeset, :ends_at)
    slots = Repo.all(from s in Slot, where: s.starts_at == ^starts_at and s.ends_at == ^ends_at)

    if Enum.empty?(slots) do
      changeset
    else
      add_error( changeset, :starts_at, "has slot with similar starts_at/ends_at")
    end
end

#---- using the changesets
# creation
%Reservation{} |> Reservation.create_changeset(params) |> Repo.insert()

# updation
%Reservation{} |> Reservation.update_changeset(params) |> Repo.update()

Хотя, по внешнему виду, вам, вероятно, следует нормализовать ваши launch_at и конец_at в отдельную таблицу с названием booking_time_frame или что-то в этом роде и добавить к ней уникальные индексы.

Или у вас может появиться больше типов бронирований, а затем вам придется проверять старты / конца_катов по 3 таблицам и т. Д.

...