How to add a has_many migration without a belongs_to?

Disclaimer: Really new to Phoenix Elixir, just starting to learn/love it

I’m trying to create an Accounts table, where each account has a has_many relationship to an Attributes table, where the Attributes table holds a list of attribute name - value pairs

I want each account to be able to have a list of attributes, but the attribute table to not have to point back into the Accounts table.

Here’s my schema for the account table

defmodule AccountMaker.Account do
  use Ecto.Schema
  import Ecto.Changeset
  alias AccountMaker.{Account, Attribute}

  schema "accounts" do
    field :auth_id, :string
    field :password, :string
    field :username, :string
    has_many :attributes, Attribute

    timestamps()
  end

  @doc false
  def changeset(%Account{} = account, attrs) do
    account
    |> cast(attrs, [:auth_id, :username, :password])
    |> validate_required([:auth_id, :username, :password])
  end
end

However, I’m not quite sure how to write the migration for creating the account table, and was hoping to get some help with that.

Here’s my current migration

defmodule AccountMaker.Repo.Migrations.CreateAccounts do
  use Ecto.Migration

  def change do
    create table(:accounts) do
      add :auth_id, :string
      add :username, :string
      add :password, :string
      # Need something for many attributes
      timestamps()
    end

  end
end

Also, not sure if this helps clarify my question, but the Attributes schema looks like this, which is a tree hierarchy

defmodule AccountMaker.Attribute do
  use Ecto.Schema
  import Ecto.Changeset

  schema "attributes" do
    field :name, :string
    field :value, :string
    belongs_to :parent, AccountMaker.Attribute
  end
end

I don’t think you need anything in the accounts migration for has_many. has_many works by scanning the attributes table for some particular account_id.

1 Like

What You probably want is a many to many relationship with an intermediary table.

If You don’t want the attributes to point back to accounts, it is maybe because You can say…

An account can have many attributes.
An attribute can have multiple accounts

2 Likes

Thanks for your answer! Can you help clarify what’s you mean by an intermediary table?

Like would i then have 3 tables to manage? And if so, what would the middle table be? Would it be the aggregate/array of attributes?

Thanks for your answer!
What do you mean by I wouldn’t need anything in the accounts migration for has_many? Do you mean that I can leave it as is in the migration?

There are some examples here.

Taken from the doc… Note primary_key: false) and no timestamp(). The join table is just storing ids.

create table(:posts_tags, primary_key: false) do
  add :post_id, references(:posts)
  add :tag_id, references(:tags)
end

But sometime You want to add attributes to join table.

For exemple, consider

User many_to_many Club
Club many_to_many User

joined by a memberships table… You might want to store date of membership in the join table. So that later You might get the membership created_at as the date of registration.

create table(:memberships) do
  add :user_id, references(:clubs)
  add :club_id, references(:users)
  timestamps
end

Another example would be a shopping cart. You would want to join Cart and Product with …

create table(:cart_products) do
  add :cart_id, references(:carts)
  add :product_id, references(:products)
  add :quantity, :integer, default: 1
  timestamps
end

That way, for each items in your cart, You could get the price with product.price * cart_product.quantity

4 Likes