Как я могу предварительно загрузить ассоциацию и вернуть ее в ecto?

#elixir #ecto

#elixir #ecto

Вопрос:

Допустим, у меня есть User модель, которая has_many Post .

Я выбрал пользователя:

 user = Repo.get(User, 1)
  

и теперь я хочу получить все сообщения для этого пользователя. Единственное решение, которое я нашел, это:

 posts = Repo.preload(user, :posts).posts
  

Но это некрасиво. Есть ли какое-либо сокращение для этого?

Ответ №1:

 user = Repo.get(User, 1) |> Repo.preload([:posts])
  

Комментарии:

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

Ответ №2:

Вы можете использовать Ecto.assoc/2 , чтобы получить запрос для всех сообщений, а затем передать Repo.all/1 его, чтобы фактически получить их:

 iex(1)> user = Repo.get(User, 1)
iex(2)> Ecto.assoc(user, :posts)
#Ecto.Query<from p in MyApp.Post, where: p.user_id == ^1>
iex(3)> Ecto.assoc(user, :posts) |> Repo.all
[debug] QUERY OK source="posts" db=2.4ms
SELECT p0."id", p0."title", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."user_id" = $1) [1]
...
  

Комментарии:

1. В чем разница между assoc и preload ?

2. assoc создает запрос для конкретной ассоциации без извлечения данных из БД, preload извлекает данные из БД для ассоциации, включая вложенные ассоциации.

3. Обратите внимание, что preload/2 возвращает как родительскую запись (в данном случае пользователя), так и связанные записи (сообщения) в одной структуре данных, в то время assoc/2 как piped to Repo.all возвращает только связанные записи (сообщения). Это означает, что если вам нужна родительская запись, вы можете get использовать их отдельно (как в примере выше), но у вас не будет этих данных, если вы запишете их полностью в виде конвейерных операторов.

4. В случае, если ассоциация уже была загружена, Repo.preload/2 не будет пытаться перезагрузить ее.

Ответ №3:

 user = Repo.get(User, 1) |> preload(:posts)
  

Это сокращение для вашего случая