thiagomajesk

thiagomajesk

Hello everyone!

Something happened at work today and it kinda got me off-guard because I was expecting different behavior from Ecto. What happened is: I was trying to insert a record with a given association that already existed in the database without casting the foreign key directly.

For example:

schema "post" do
  field :title, :string
  field :body, :string
end
schema "category" do
  field :name, :string
  field :description, :string
end
schema "category_post" do
  belongs_to :post, Post
  belongs_to :category, Category
  field :remarks, :string
end

def changeset(category_post, attrs) do
  post
  |> cast(attrs, [:remarks])
  |> cast_assoc(:post)
  |> cast_assoc(:category)
  |> validate_if_posts_has_the_right_amount_of_comments() # needs the retrieved post
  |> validate_if_category_has_the_right_amount_of_followers() # needs the retrieved category
end

And then, trying to create a “category_post” with its relationship:

def create_category_post(attrs, post_id, category_id) do
  post = Posts.get_post!(post_id)
  category = Categories.get_category!(category_id)

  attrs = Map.merge(attrs, %{category: category, post: post})
   
  %CategoryPost{}
  |> CategoryPost.changeset(attrs)
  |> Repo.insert()
end

Is this even possible? I noticed that for this case Ecto always tries to insert a new “post”/ “category” if I use cast_assoc/3.

Showing Posts 1 to 10

kokolegorille

kokolegorille

If the record already exists, You should use put_assoc instead.

But for this use case, I would use build_assoc.

thiagomajesk

thiagomajesk OP

Hi @kokolegorille, thanks for the reply.
Could you elaborate on why would you choose one over the other?

kokolegorille

kokolegorille

Do You mean cast_assoc over put_assoc, or put_assoc over build_assoc?

kokolegorille

kokolegorille

I would use cast_assoc in case I want to use aggregate root.
I would use put_assoc, if the association already exists.

And the last case is to use build_assoc in case I want to set … a belongs to. For example if I need to set an owner, or in your case, a category.

thiagomajesk

thiagomajesk OP

Sorry about the confusion. I meant build_assoc over put_assoc.
If it’s not asking too much, could you exemplify?

kokolegorille

kokolegorille

Instead of using a normal post, You will use…

post = build_assoc(category, :posts)

and then, pass this post to the changeset, You don’t need to cast anything, category_id will be set for You.

thiagomajesk

thiagomajesk OP

I see. But in this specific case, I would have to have a has_many association on the other side, right?
In my scenario, I only have the belongs_to association on the record that is currently being inserted.

PS.: I’ve updated my original post examples to better represent the challenge I’m facing.

kokolegorille

kokolegorille

Yes, You need to have the has_many on the other side… or cast category_id on the post side, but it’s not something I like to do when I can do with build_assoc.

Maybe You can simply pass a post where You manually set category_id, like this %Post{category_id: category.id} before passing it to changeset, but I have never tried.

Sleepful

Sleepful

Hi!
You can add a field for your FK id and simply cast that in your changeset :slight_smile:

      schema "table_table" do
        belongs_to :post,
          Post,
          [foreign_key: :post_id]

        field :related_post, :integer,
          [source: :post_id]
      end

      def changeset(schema, attrs) do
        schema
        |> cast(attrs, [:related_post])
        |> cast_assoc(:post)
      end

:smiley:

Sleepful

Sleepful

trying to update my own code but I can’t so posting it again, this is clearer:

      schema "table_table" do
        belongs_to :related_post,
          Post,
          [foreign_key: :post_id, define_field: false]

        field :post_id, :integer
          # and you can add the following to opts if you want
          # the field and the column on the DB to have 
          # different names:
          # [source: :"table_column_id"]

      end

      def changeset(schema, attrs) do
        schema
        |> cast(attrs, [:post_id])
        |> cast_assoc(:related_post)
      end

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
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
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
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
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
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
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews