Confusion about Ecto has_many and belongs_to

Hi All,
I’m confused about Ecto.Schema has_many and belongs_to name parameter. Should this parameter refer to the module etc. Does it have a correlation with the foreign key name in the child table of the migration etc. What role does the pluralisation of the name plays like ruby on rails. Please kindly educate me. Thanks.

The name is whatever you want it to be. However, it may mean you need to specify options because the defaults won’t be correct.

For has_many

:foreign_key - Sets the foreign key, this should map to a field on the other schema, defaults to the underscored name of the current schema suffixed by _id
:references - Sets the key on the current schema to be used for the association, defaults to the primary key on the schema

For belongs_to

  • :foreign_key - Sets the foreign key field name, defaults to the name of the association suffixed by _id . For example, belongs_to :company will define foreign key of :company_id
  • :references - Sets the key on the other schema to be used for the association, defaults to: :id

Phoenix and Ecto don’t have an inflector like ruby does. There’s no automatic pluralization. When you generate a model in phoenix you specify the plural name for the schema and a singular name for the module. But that is only convention and the names can be arbitrary. That’ll give you:

defmodule MyApp.User do #singular
  use Ecto.Schema

  schema "users" do #plural
  end
end

Nothing in Ecto relies on converting between singular and plural. Some defaults use the schema name and others use the module name.

3 Likes

I am also confused; the documentation at:
https://hexdocs.pm/ecto/Ecto.Schema.html#has_many/3

Doesn’t explain anything about the name argument.
How is name supposed to be used?
Why does the has_many assoc even have a name?
The associated modules/schemas already have names right?

The documentation states that:
“Indicates a one-to-many association with another schema”.
So how is this indication supposed to be used?
How is it useful other than being indicative?
Is this what it’s all about?

comments = Repo.all assoc(post, :comments)

And is the :comments argument actually the name of the assoc indication?
(it looks to me like a schema name: create table comments …)

The documentation at:
https://hexdocs.pm/ecto/Ecto.html#assoc/2
doesn’t mention anything about the assocs argument to assocs/2.
The examples suggest the argument should be an atom or a list of atoms.
Should these be assoc names? schema names? module names? any of these?
To me :comments looks like a schema and :author looks like a module.

Also has_many is probably not about preloading; belongs_to works fine.

So it’s not clear to me why I would want to use has_many/3.

You don’t always want to name your association after the schema it points to. For example, you could have a people schema and a company schema. Where company has_many employees, which points to the people schema and company has_one owner, which also points at the people schema.

Yep, that’s all it’s about. It prevents you from needing to specify a longer statement for something you probably reference many times. For example, without it, you’d have to write:

from(c in Comment, where: c.post_id == ^(post.id))
|> Repo.all()

It’s the name. Think Repo.all assoc(company, :employees) returning a person schema.

If you think the docs can be improved, they love PRs. Given the name of the method is assoc and the argument name is assocs in the docs, you might infer that it’s the association name.

The docs do cover when to use belongs_to: https://hexdocs.pm/ecto/Ecto.Schema.html#belongs_to/3

You should use belongs_to in the table that contains the foreign key. Imagine a company <-> employee relationship. If the employee contains the company_id in the underlying database table, we say the employee belongs to company.

As for has_many, it could say more, but it does say:

The current schema has zero or more records of the other schema. The other schema often has a belongs_to field with the reverse association.

It’s not possible to define a 1 to many association with has_one or belongs_to.

You can preload any of the associations regardless of whether they are has_many, has_one, or belongs_to.

2 Likes