#elixir #phoenix-framework #ecto
#elixir #phoenix-framework #ecto
Вопрос:
У нас есть довольно много тестов, в которых мы вручную устанавливаем отношения. Мы делаем что-то вроде:
defp build_relationships(relationship_map, user) do
relationship_map |> Map.put(:user, %{data: %{id: user.id}})
end
Это работает нормально, но я хочу сделать его гибким, чтобы принимать записи любого типа (а не только пользователя). Для справки, user
создается с ex-machina
помощью as insert(:user)
.
Есть ли способ получить type
запись of, которая передается? Если бы я мог получить строку с надписью "user"
, я мог бы использовать String.to_atom(param)
и передать ее, Map.put
но я не могу найти элегантный способ сделать это.
Реальный вопрос в том, как мне взять запись типа user
и вернуть атом :user
?
Любая помощь приветствуется.
Ответ №1:
Вы можете получить некоторую информацию о структуре Ecto, используя .__struct__.__schema__/1
, например:
iex(1)> %Post{}.__struct__.__schema__(:source)
"posts"
но поскольку нет сопоставления 1: 1 из схемы на фабрику ExMachina, я могу придумать 3 возможных способа:
- Переименуйте фабрики, чтобы использовать форму множественного числа, такую же, как имя таблицы, и используйте
.__struct__.__schema__(:source)
для получения имени. Это сделает ваш заводской код немного странным для чтения, поскольку затем вы будете использоватьinsert(:users)
его для вставки одного пользователя. - Продолжайте использовать имена в единственном числе, но используйте некоторую библиотеку для преобразования имен таблиц во множественном числе в единственные, например, что-то, что преобразует
"users"
в"user"
и"posts"
в"post"
. - Сохраняйте список сопоставлений имен схемы <->, например, так (или вы даже можете перенести сопоставление в отдельную функцию):
defp build_relationships(relationship_map, struct) do mapping = %{MyApp.User => :user, MyApp.Post => :post} # add more here relationship_map |> Map.put(mapping[struct.__struct__], %{data: %{id: struct.id}}) end
- Используйте
Module.split/1
, чтобы получить имя модели и использоватьMacro.underscore/1
для преобразования его в строчное подчеркнутое имя, а затем используйтеString.to_existing_atom
:defp build_relationships(relationship_map, struct) do name = struct.__struct__ |> Module.split |> List.last |> Macro.underscore |> String.to_existing_atom relationship_map |> Map.put(name, %{data: %{id: struct.id}}) end
Это не будет работать корректно, если вы используете вложенные модули в качестве модели, например
MyApp.Something.User
.
Я бы выбрал 4, если все ваши модели имеют одинаковый уровень вложенности, или 3, если вы хотите быть более явным.
Комментарии:
1. Спасибо за быстрый ответ! Я не решаюсь попробовать третий, поскольку в этом проекте так много участников, и я не хочу писать помощника, который необходимо обновить позже. Я попробовал это, и это отлично работает! Я могу выбрать вариант 2. Спасибо за помощь!
2. @sbatson5 Пожалуйста, посмотрите четвертый вариант, который я только что добавил. Я подумал о лучшем способе, который может быть лучше для вашего использования.
3. Спасибо! Это именно то, что мне было нужно. В случае, если вам было любопытно, вы можете увидеть PR, реализующий это здесь: github.com/code-corps/code-corps-api/pull/358/files