Two fundamental questions regarding Ecto relationships

I can’t quite get a clear answer from Hex Docs or the Programming Ecto book, so I’m going to put these two questions out to the forum:

I’m going to pull the code right out of HexDocs and then reference that for the questions:

defmodule EctoAssoc.Post do
  use Ecto.Schema

  schema "posts" do
    field :header, :string
    field :body, :string
    belongs_to :user, EctoAssoc.User  # this was added
  end
end

defmodule EctoAssoc.User do
  use Ecto.Schema

  schema "users" do
    field :name, :string
    field :email, :string
    has_many :posts, EctoAssoc.Post  # this was added
  end
end

Question 1: For the Post Schema, there is a field :user added to “posts” and it belongs_to User. Can that field name be anything or does it have to be :user in order to follow some naming convention?

Question 2: The documentation says that has_many is used for one-to-many relationships. Can it be used for zero-to-many relationships? For example, a user many not have any posts. In that case, would user.posts just return nil? I understand that has_one is for a zero-or-one relationship. I just want to confirm that has_many is not just for one-to-many relationships but ALSO for zero-to-many relationships. I want to make sure I’m not setting myself up for issues down the road by allowing nil as an option.

It can be anything. But note that :user is how to refer to the association in functions (i.e. preloading) and not the actual name of the field in the database. There is an option for the database field name called :foreign_key that defaults to the name suffixed by _id. There are some explanations and examples here.

It can be zero to many as well, If there are no associations it will default to empty list.

3 Likes

Thank you! The default :foreign_key had me confused. So I need to be careful about how I name that association field (:user), because it will use that name to identify the table’s id field (:user_id). If I want to customize that association name (:user), then I must explicitly declare the :foreign_key.

Just to sanity check this … this is how I would customize an association name:

schema "posts" do
    field :header, :string
    field :body, :string
    **belongs_to :member, EctoAssoc.User,  foreign_key: :user_id** 
  end

Can I create two associations to the same table like the following (terrible example but you get the general idea–both associations point to the same table but are distinguished by different association names):

schema "posts" do
    field :header, :string
    field :body, :string
    **belongs_to :member, EctoAssoc.User,  foreign_key: :user_id**
    **belongs_to :moderator, EctoAssoc.User, foreign_key: :user_id**
  end

Thank you also for your answer to Question 2. It’s good to know that it will just treat it as an empty list. I was worried it would throw an error.

Not with the same foreign_key because what happens when you create a belongs_to associations is the Post struct gets 2 fields: one for the association and one for the foreign key. So there will be one field :user will contain the User struct if it’s preloaded and another field :user_id for the foreign key which is always there. So if 2 associations have the same foreign key it will not be allowed because you can’t make 2 user_id keys on a struct.

1 Like

Ahhhh … that makes sense. Thank you so much for explaining this. I’m getting a much better handle on Ecto relationships.

No worries. The association functionality in Ecto is very powerful but it takes some time for it to become completely intuitive. You’re asking the right questions :).