2017-07-14 59 views
2

我有兩個表。表topics其中has_manytweets。我的桌子tweetsbelongs_to a topic用Ecto一次插入多行。 「協議枚舉未執行#Ecto.Changeset」

主題模式:

defmodule Sentiment.Topic do 
    use Sentiment.Web, :model 

    schema "topics" do 
    field :title, :string 

    has_many :tweets, Sentiment.Tweet 
    end 

    def changeset(struct, params \\ %{}) do 
    struct 
    |> cast(params, [:title]) 
    |> validate_required([:title]) 
    end 
end 

分享Tweet架構:

defmodule Sentiment.Tweet do 
    use Sentiment.Web, :model 

    schema "tweets" do 
    field :message, :string 
    belongs_to :topic, Sentiment.Topic 

    end 

    @doc """ 
    Builds a changeset based on the `struct` and `params`. 
    """ 
    def changeset(struct, params \\ %{}) do 
    struct 
    |> cast(params, [:message]) 
    |> validate_required([:message]) 
    end 
end 

我試圖一個topic插入我的表,其次是500 tweets我運行該主題Twitter的搜索後。

在我的控制,我用Ecto.Multi到組我的正回購操作,但是,每次我跑我的操作我得到一個錯誤protocol Enumerable not implemented for #Ecto.Changeset<action: nil, changes: %{message: "\"aloh....

這是我在嘗試第一次插入我的話題,獲得它的ID,然後用一個交易插入帶有關聯ID的tweet消息。

def create(conn, %{"topic" => topic}) do 
    # create a topic changeset 
    topic_changeset = Topic.changeset(%Topic{}, topic) 

    # obtain a list of tweet messages: ["hello", "a tweet", "sup!"] 
    %{"title" => title} = topic 
    all_tweets = title 
    |> Twitter.search 

# create an Ecto.Multi struct. 
multi = 
    Ecto.Multi.new 
    |> Ecto.Multi.insert(:topics, topic_changeset) #insert topic 
    |> Ecto.Multi.run(:tweets, fn %{topics: topic} -> 
    changeset_tweets = all_tweets 
    |> Enum.map(fn(tweet) -> 
     %{topic_id: topic.id, message: tweet} 
    end) 

    Repo.insert_all(Tweet, changeset_tweets) 

    end) 

     # Run the transaction 
     case Repo.transaction(multi) do # ERROR HERE! 
     {:ok, result} -> 
      conn 
      |> put_flash(:info, "Success!") 
      |> redirect(to: topic_path(conn, :index)) 
     {:error, :topics, topic_changeset, %{}} -> 
      conn 
      |> put_flash(:error, "Uh oh...") 
      |> render("new.html", changeset: topic_changeset) 
     {:error, :tweets, topic_changeset, %{}} -> 
      conn 
      |> put_flash(:error, "Something really bad happened...") 
      |>render("new.html", changeset: topic_changeset) 
     end 
    end 

insert_all約500排在一個事務中如何使用Ecto.Multi?

更新 我已將變更列表轉換爲地圖列表,並且我的錯誤已更改爲更令人困惑的內容。

error what I am trying to insert

+0

您可以發佈包括堆棧跟蹤完整的錯誤消息?錯誤應該在包含'Repo.insert_all'的行中,因爲它只支持關鍵字列表的映射和列表列表,而不支持變更集列表。 – Dogbert

+0

@Dogbert我爲你發佈了一個錯誤圖片。 –

+0

正如@Dogbert所說的,你正試圖將一組變更集傳遞給'Repo.insert_all'。但是,該函數不接受變更集列表。它只需要一張地圖列表或關鍵字列表列表。 –

回答

5

對於Ecto.Multi正確使用工序進度,他們每個人都必須返回要麼{:ok, value}{:error, reason}元組。

insert ING,ING updatedelete荷蘭國際集團變更集,它會自動返回這樣一個元組,但對於run,你需要明確地返回。

請考慮以下因素:

Ecto.Multi.new 
|> Ecto.Multi.insert(:topics, topic_changeset) #insert topic 
|> Ecto.Multi.run(:tweets, fn %{topics: topic} -> 
    maps = 
    Enum.map(all_tweets, fn(tweet) -> 
     %{topic_id: topic.id, message: tweet} 
    end) 

    {count, _} = Repo.insert_all(Tweet, maps) 
    {:ok, count} # <---- 
end) 
+0

謝謝你的明確解釋!這幫了我很多。我是鳳凰新手:) –

+2

Ecto.Multi可用於任何Elixir項目。這不一定是鳳凰。只是說!很高興你發現它很有用! –

+0

下次我一定會更仔細地閱讀文檔。謝謝! –

2

亞歷克斯,這不是一個直接的答案與你的Ecto.Multi問題,而是一個建議,這可能是更容易使用cast_assoc(:tweets)您的主題變更內。

這應該是這樣的:

# Topic.ex 
... 
def changeset(struct, params \\ %{}) do 
    struct 
    |> cast(params, [:message]) 
    |> cast_assoc(:tweets) 
    |> validate_required([:message]) 
end 

# create_topic.. 
... 
tweets = [%{message: "Tweet 1"}, %{message: "Tweet 2"}] 

{:ok, topic} = 
    %Topic{} 
    |> Topic.changeset(Map.put(topic, :tweets, tweets)) 
    |> Repo.insert() 
+0

我試過這個,但得到一個變更集錯誤,說我錯過了foreign_key ie。 topic_id。 – Cyzanfar