How to implement to_query(data) in Elixir Struct

2019-09-13 03:17发布

问题:

I am attempting to update a existing records in my database using Repo.update:

  def subscribe_email(conn, %{"email-address"=>email_address, "shop"=>shop}) do
    current_record = Repo.all(%Oauth.EmailAddress{email_address: email_address, active: false, shop: shop, verified: :true})
    current_record = Ecto.Changeset.change(current_record, active: :true)
    case Repo.update current_record do
      {:ok, struct} -> IO.puts "updated correctly."
      {:error, changeset} -> IO.puts "did not update"
    end
  end

I have a model for %Oauth.EmailAddress:

defmodule Oauth.EmailAddress do
  use Ecto.Model

  schema "email_address" do
    field :email_address, :string
    field :active, :boolean
    field :shop, :string
    field :verified, :boolean
    timestamps
  end
end

When I hit subscribe_email(), an exception is raised:

** (Protocol.UndefinedError) protocol Ecto.Queryable not implemented   for %Oauth.EmailAddress

I know that I need to implement to_query() from Ecto.Queryable. However, I do not know how to do this. I am not familiar with Protocols in Elixir, although I have read the official documentation, I have not used Protocols. Please explain how to implement to_query() for my struct.

回答1:

That error is a bit misleading. Repo.all doesn't accept a struct with fields filled in like that. You're most likely looking for this:

current_record =
  from(Oauth.EmailAddress)
  |> where(email_address: email_address, active: false, shop: shop, verified: :true)
  |> Repo.all

Also, since you're passing the result to Ecto.Changeset.change, you probably want just one record, not a list of all records:

current_record =
  from(Oauth.EmailAddress)
  |> where(email_address: email_address, active: false, shop: shop, verified: :true)
  |> Repo.one

Note that this will fail if there's more than one matching record for the query.



回答2:

Whilst @Dogbert's answer is the way to go, a couple of other points:

  1. Ecto.Model is deprecated and you should consider moving to Ecto.Schema

  2. If you're curious about protocol implementation look in ./deps/ecto/lib/ecto/repo/queryable.ex - you could, if you're feeling adventurous, quite easily implement what you wrote. But it would be a pain to maintain and difficult for others to read and understand.