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
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
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New

Other Trending Topics Top

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
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
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
Aludel - LLM Evaluation Workbench Aludel is an embeddable Phoenix LiveView dashboard for evaluating and comparing LLM prompts across mult...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews