docs elixir phoenix-framework ecto

docs - ecto elixir



Ámbitos de mezcla y asociaciones en Phoenix/Ecto (3)

En Rails, si tengo la siguiente configuración:

class Post < ActiveRecord::Base has_many :comments end class Comment < ActiveRecord::Base belongs_to :post def self.approved where(approved: true) end end

Entonces puedo hacer algo como esto:

post = Post.find(100) comments = post.comments.approved

para obtener rápidamente todos los comentarios aprobados para el Post dado.

¿Cómo puedo hacer algo similar en ecto?

defmodule MyApp.Post do use Ecto.Model schema "posts" do #columns omitted has_many :comments, MyApp.Comment end end defmodule MyApp.Comment do use Ecto.Model schema "comments" do #columns omitted belongs_to :post, MyApp.Post end end

Tengo el post con comments precargados:

post = MyApp.Post |> MyApp.Repo.get(100) |> MyApp.Repo.preload(:comments)

Ni siquiera estoy seguro de por dónde empezar con el alcance approved en MyApp.Comment .


Las precargas tienen permitido recibir consultas. Así que puedes filtrar comentarios asociados como este.

post = MyApp.Post |> Ecto.Query.preload(comments: ^MyApp.Comment.approved(MyApp.Comment)) |> MyApp.Repo.get(100)

Y en tu modelo de Comment

def approved(query) do from c in query, where: c.approved == true end


Llego tarde a esta fiesta, pero tengo tiempo libre y me parece que una respuesta podría ayudar a las personas nuevas en Elixir.

Si viene de Ruby / Rails, una cosa que debe recordar es que los datos no tienen estado en Elixir / Erlang porque los valores son inmutables. Así que no tenemos una forma de manipular la publicación y cargar el comentario en la estructura de datos. Podemos lograr el mismo resultado final de dos maneras:

# 1 Devuelve una nueva estructura / mapa con comentarios fusionados en ella

post_with_comments = %{post | comments: comments} # or Map.put(post, :comments, comments)

donde comentarios es algo como:

comments = MyApp.Repo.get_by(MyApp.Comment, where: post_id == ^post.id) .

# 2 Precargue los datos en la estructura de datos de la publicación mediante la creación de una consulta para capturarlos todos a la vez. Podemos hacer esto pasando las consultas a las consultas, ver más abajo.

defmodule MyApp.Post.Query do def approved_with_comments(id) do get_post(id) |> with_approval(true) |> with_comments() end def get_post(id) do from p in MyApp.Post, where: p.id == ^id end def with_approval(query, approval) do from q in query, where: approved == ^approval end def with_comments(query) do from q in query, preload: [:comments] end end

Por lo general, siempre querrá precargar las asociaciones ya que es más eficiente para la base de datos. Personalmente, me encanta este comportamiento en Ecto porque te obliga a no dispararte en el pie con consultas de N + 1 o los hace muy obvios para ver.

Puede hacer que la interfaz sea un poco más ergonómica para algo como el módulo de Query anterior utilizando el mismo nombre de función con la coincidencia de patrones:

def query(query, :by_id, id), do: from q in query, where: q.id == ^id def query(query, :by_approval, approval), do: # ....

Luego, asignará la reducción de lo que sean sus parámetros a un único objeto de consulta que finalmente cargue con Repo.one o lo que sea más adecuado para su Repo.one .


No creo que sea posible con la versión actual de Ecto. La precarga no permite el filtrado. Alternativa es obtener los comentarios con una consulta:

(from comment in MyApp.Comment, where: comment.post_id == ^post_id and comment.approved == true, select: comment) |> Repo.all