shijith.k

shijith.k

I have a posts table which has many tags. The 2 are linked by a many to many association through an post_tags table. There is no schema for post_tags table. So, I use put_assoc/4 to insert data to post_tags table. Now, how can I add a unique constraint error to the Changeset when creating/editing a post?

Showing Posts 1 to 10

andreaseriksson

andreaseriksson

I would set up an has many - through relation ship instead

post has_many post_tags
post has_many tags through
(Ecto.Schema — Ecto v3.14.0)

Then you can have a changeset on PostTag that validates unique constraint

andreaseriksson

andreaseriksson

I wrote a short tutorial about how I prefer to design this:

shijith.k

shijith.k OP

In this tutorial, can we link multiple tags with a product at once? also, by adding cast_assoc in the taggings Changeset, it will try to create a new tag every time we try to link a tag with a product, right?

andreaseriksson

andreaseriksson

There is no batch insert of tags.

I see now that I forgot to add the unique constraint in Tag-module.

  def changeset(tag, attrs) do
    tag
    |> cast(attrs, [:name])
    |> validate_required([:name])
    |> unique_constraint(:name, name: :tags_name_index)
  end

That will result in an error Changeset that you need to chose how handle (ignore or validation error).

Specs look like this:

defmodule Tutorial.TaggableTest do
  use Tutorial.DataCase

  alias Tutorial.Taggable
  alias Tutorial.Taggable.Tag
  alias Tutorial.Taggable.Tagging
  alias Tutorial.Repo
  alias Tutorial.Products

  def product_fixture(attrs \\ %{}) do
    {:ok, product} =
      attrs
      |> Enum.into(%{description: "some description", name: "some name", price: 120.5, properties: %{}})
      |> Products.create_product()

    product
  end

  def tag_fixture() do
    product = product_fixture()

    {:ok, %Tagging{tag: tag}} =
      Taggable.tag_product(product, %{tag: %{name: "Stout"}})

    tag
  end

  describe "tags and taggings" do
    test "list_tags/0 returns all tags" do
      tag = tag_fixture()
      assert Taggable.list_tags() == [tag]
    end

    test "tag_product/2 with valid data creates a tag" do
      product = product_fixture()
      assert {:ok, %Tagging{} = tagging} = Taggable.tag_product(product, %{tag: %{name: "Stout"}})
      assert tagging.tag.name == "Stout"
    end

    test "tag_product/2 with invalid data returns error changeset" do
      product = product_fixture()
      assert {:error, %Ecto.Changeset{}} = Taggable.tag_product(product, %{tag: %{name: nil}})
    end

    test "tag_product/2 with duplicate tag returns error changeset" do
      product = product_fixture()
      Taggable.tag_product(product, %{tag: %{name: "Stout"}})
      assert {:error, %Ecto.Changeset{}} = Taggable.tag_product(product, %{tag: %{name: "Stout"}})
    end

    test "delete_tag_from_product/2 deletes the tagging from product but not the tag" do
      product = product_fixture()
      {:ok, %Tagging{tag: %Tag{} = tag}} = Taggable.tag_product(product, %{tag: %{name: "Lager"}})

      assert %{tags: [^tag]} = product |> Repo.preload(:tags)
      assert {:ok, %Tagging{}} = Taggable.delete_tag_from_product(product, tag)
      assert %{tags: []} = product |> Repo.preload(:tags)
      assert [%Tag{name: "Lager"}] = Taggable.list_tags()
    end
  end
end
shijith.k

shijith.k OP

Then how do you create product with multiple tags? :roll_eyes: :grimacing:

andreaseriksson

andreaseriksson

I would loop through the tags and insert them one by one (can’t be that many). Otherwise I risk getting an error if I batch insert and one of them is a duplicate.

shijith.k

shijith.k OP

this is why I tried doing.

And why did you use that cast_assoc in the taggings Changeset?

andreaseriksson

andreaseriksson

Its actually through the Tagging I insert the tag.

  def tag_product(product, attrs \\ %{}) do
    product
    |> Ecto.build_assoc(:taggings)
    |> Tagging.changeset(attrs)
    |> Repo.insert()
  end

But I think there is an issue that only allows me to create and assign new tags. Will see if I can fix that.

shijith.k

shijith.k OP

exactly… so every time I tag something, it tries to create a new one, and that causes the unique constraint in tag table to raise

shijith.k

shijith.k OP

also, i keep getting this error,

* (Ecto.ConstraintError) constraint error when attempting to insert struct:

    * taggings_product_id_fkey (foreign_key_constraint)

Why is that?

I am using Ecto.Multi to create the Product and then associate it with the tags

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews