Ecto.Preloads

I am new to Elixir and hoping someone can help me with the Ecto library.

	def send_notification(notification) do
	with {:ok, message} <- get_message(notification),
	do
		user = Repo.preload(notification, :user).user
		actor = Repo.preload(notification, :actor).actor
		auth_tokens = Repo.preload(user, :auth_tokens).auth_tokens

What would be the best way to check if this works with the With syntax. Basically, I want to preload it and check if the user or actor is nil. However, I did that in the guard with the with statement and the compiler complains. Any way to make this code cleaner or is this the best it is going to get?

1 Like

Not sure what you want to achieve with the with keyword but you can preload everything in one go.

notification = Repo.preload(notification, :actor, user: :auth_tokens)

Does that help?

1 Like

I didn’t know you can do it all in one line. That is neat. However, what if the preload fails. So you get a nil object. I tried to put that in my with clause as well but it would not let me call a remote function.

I could go ahead and check if any of those are nill. is_nil(notification.user) but is there a clever way to do it in one or two lines.

1 Like

Perhaps you can use pattern matching?

case Repo.preload(notification, :actor, user: :auth_tokens) do
  %{user: nil, actor: nil} = notification ->
    IO.puts "No user or actor"
  %{actor: nil} = notification ->
    IO.puts "No actor"
  %{user: nil} = notification ->
    IO.puts "No user"
  notification ->
    IO.puts "Both user and actor"
end
6 Likes

That looks like a great solution. :+1:

1 Like

Linuus, that looks slick. Thank you. Let me give that a shot. Thank you for your help everyone!

1 Like

@Linuus this might not work. It is complaining about auth_tokens since it is part of the user schema. As a test I did this:

			{user, actor, auth_tokens} = case Repo.preload(notification, :actor, user: :auth_tokens) do
			notification ->
				{notification.user, notification.actor, notification.user.auth_tokens}
		end

and then get this error:

%Protocol.UndefinedError{description: "", protocol: Enumerable, value: #Ecto.Association.NotLoaded<association :auth_tokens is not loaded>}

Okay, seems to work with this:

{user, actor, auth_tokens} = case Repo.preload(notification, [:actor, {:user, :auth_tokens}]) do
2 Likes