Should I add belongs_to to my schema? What do I gain?

I have added users to my Phoenix application through phx.gen.auth and I have the following schema defined in lib/myapp/accounts/user.ex:

  schema "users" do
    field :email, :string
    field :password, :string, virtual: true, redact: true
    field :hashed_password, :string, redact: true
    field :confirmed_at, :naive_datetime

    timestamps()
  end

Now I’m expanding my app and adding a catalog of products – and I want to make sure that each product has a maker in my system, which should correspond to a user from the accounts context. So I ran the following Phoenix generator:

mix phx.gen.html Catalog Product products url:string:unique product:map maker_id:references:users

This is the migration that was generated:

def change do
  create table(:products) do
    add :url, :string
    add :product, :map
    add :maker_id, references(:users, on_delete: :nothing)

    timestamps()
  end

  create unique_index(:products, [:url])
  create index(:products, [:maker_id])
end

and this was the schema that was generated in lib/myapp/catalog/product.ex:

schema "products" do
  field :product, :map
  field :url, :string
  field :maker_id, :id

  timestamps()
end

I wanted to make sure I could not add a product to my system without associating it with a user, and that deleting a user would delete all their associated products, so I adjusted the migration to add null: false and on_delete: :delete_all to the maker_id.

    add :maker_id, references(:users, on_delete: :delete_all), null: false

So I should now have a solid foreign key for every product that will point to the users table.

Here are a few questions I have:

  1. Why didn’t the generator automatically add a belongs_to macro to my products schema? It should know that the maker_id is a foreign key pointing to users, so it ought to have known how to do this.

  2. How will my app benefit if I adjust the products schema by adding the belongs_to macro? The relationship is already constrained in Postgres, after all.

  3. Do I need to add a corresponding has_many macro to my users schema? What do I lose if I don’t?

  4. Is this the correct way to specify belongs_to for the situation I have described:

schema "products" do
  field :product, :map
  field :url, :string
  belongs_to :user, Myapp.Accounts.User, foreign_key: :maker_id

  timestamps()
end
  1. Should I set the :source option, which is mentioned in the docs? Ecto.Schema — Ecto v3.7.1

Thanks!

I can’t answer all your questions well so I’ll respond to the title and a part of them:

Add those macros only if you need a back-reference to the owning object in your code, and if the owning object needs to know all the object it owns / points to. In the past I’ve commented out various belongs_to and has_many statements if after analysis I found I don’t use them.

However, with time I preferred to leave them in because they are not introducing a noticeable load on the DB and their presence serves as good DB design documentation.

50/50, nowadays I lean to leaving them in even if I don’t use them but I can see why people would comment them out (or skip them altogether) as well.

1 Like

I often find myself using belongs_to back refs during debug console sessions, so I like to leave them around since it impacts little else.

1 Like