Hi Friends,
I have been struggling with this one.
My question is closely related to adding tags to a post section of put_assoc docs.
But, the section assumes that all tags already exist in the DB, while in my case, some or all tags may be new tags, which I want to create afresh in the DB while storing posts (articles in my schema).
Here is the query that I currently have:
def create_article(%{"tagList" => tagList} = attrs \\ %{}, current_user) do
Ecto.build_assoc(current_user, :articles)
|> IO.inspect()
|> Article.changeset(attrs)
|> IO.inspect()
# |> Repo.preload([:tags])
|> IO.inspect()
|> Ecto.Changeset.put_assoc(:tags, tags)
|> Repo.insert()
end
The above query works fine if all the tags in the tagList
are new, but it does not if a tag is re-used.
Also, if I comment out Repo.preload([:tags])
line it throws the error below:
(UndefinedFunctionError) function Ecto.Changeset.__schema__/2 is undefined or private
(ecto 3.7.1) Ecto.Changeset.__schema__(:association, :tags)
(elixir 1.12.0) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
Any help appreciated.
Here’s my schema:
Article Schema
defmodule ConduitElixir.Articles.Article do
use Ecto.Schema
import Ecto.Changeset
alias ConduitElixir.Tags.Tag
alias ConduitElixir.Tags.ArticleTag
alias ConduitElixir.Auth.User
schema "articles" do
field :body, :string
field :description, :string
field :title, :string
many_to_many :tags, Tag, join_through: ArticleTag
belongs_to :user, User
timestamps()
end
@doc false
def changeset(article, attrs) do
article
|> cast(attrs, [:title, :body, :description])
|> validate_required([:title, :body])
end
end
User Schema
defmodule ConduitElixir.Auth.User do
use Ecto.Schema
import Ecto.Changeset
alias ConduitElixir.Articles.Article
schema "users" do
field :email, :string
field :password, :string, virtual: true
field :hashed_password, :string
field :username, :string
field :bio, :string
has_many :articles, Article
timestamps()
end
Tags schema
defmodule ConduitElixir.Tags.Tag do
use Ecto.Schema
import Ecto.Changeset
alias ConduitElixir.Tags.ArticleTag
alias ConduitElixir.Articles.Article
schema "tags" do
field :title, :string
many_to_many :articles, Article, join_through: ArticleTag
timestamps()
end
@doc false
def changeset(tag, attrs) do
tag
|> cast(attrs, [:title])
|> validate_required([:title])
end
end
defmodule ConduitElixir.Tags.ArticleTag do
use Ecto.Schema
import Ecto.Changeset
alias ConduitElixir.Tags.Tag
alias ConduitElixir.Articles.Article
schema "article_tags" do
belongs_to :tag, Tag
belongs_to :article, Article
end
@doc false
def changeset(tag, attrs) do
tag
|> cast(attrs, [:article_id, :tag_id])
|> validate_required([:article_id, :tag_id])
|> unique_constraint([:article_id, :tag_id])
end
end