chrisdel101

chrisdel101

When adding two foreign keys using has_many only one is getting added

I have a Role like this in DB. It is attached to both a organization and a employee. But I cannot get both fields organization_id && employee_id to populate. This what it looks like after inserting an employee and an organization.


 id | name  | value | organization_id | employee_id |    
----+-------+-------+-----------------+-------------+-
 21 | owner | 1     |              |          24     |

Modeling


  schema "employees" do
    has_many :roles, Role

  schema "organizations" do
    has_many :roles, Role

  schema "roles" do
    belongs_to :employee, Employee, foreign_key: :employee_id
    belongs_to :organization, Organization, foreign_key: :organization_id

 create table(:roles) do
      add :organization_id, references(:organizations)
      add :employee_id, references(:employees)

The problem is not with the modelling but with the actual insertion.
Here what I’ve tried but it’s not working. I always get the table at the top w/ a missing FK.

I try to

  • associate the employee with the role then update the employee,
  • associate the role itself with an organization and then update the org.
  • Missing: associating the org with the role and upate the org.

Note: this is a bunch of random stuff trying to make the associations take. I know it’s not pretty.


def register_and_preload_employee(attrs) do
  # build a role instance
    role = Role{
      name: "owner",
      value: "1"
    }
    
    # build employee changeset
    emp_changeset = Employee.registration_changeset(%Employee{}, attrs)
    # assoc role with employee
    emp_changeset = Ecto.Changeset.put_assoc(emp_changeset, :roles, [role])
         #role is inserted
  # Ecto.Changeset<
  #   action: nil,
  #   changes: %{
  #     email: "...
  #   ...
  #     roles: [
  #       #Ecto.Changeset<action: :insert, changes: %{}, errors: [],
  #        data: #Role<>, valid?: true>
  #     ]
  #   },
  #   errors: [],
  #   data: #Employee<>,
  #   valid?: true
  # >
    # get org and build_assoc foreign key
    organization_id = 1
    # get org and preload roles
    organization = Company.get_organization(organization_id) |> Repo.preload(:roles)
    # ATTEMPT to build assoc with org and role
    role_w_org = Ecto.build_assoc(organization, :roles, role)
    #  org is loaded into role
    # %Role{
    #   __meta__: #Ecto.Schema.Metadata<:built, "roles">,
    #   id: nil,
    #   name: "owner",
    #   value: "1",
    #   employee_id: nil,
    #   employee: #Ecto.Association.NotLoaded<association :employee is not loaded>,
    #   organization_id: 1,
    #   organization: #Ecto.Association.NotLoaded<association :organization is not loaded>,
    #   inserted_at: nil,
    #   updated_at: nil
    # }
    
    # insert employee
    case Repo.insert(emp_changeset) do
      {:ok, new_emp} ->
     #preload in case required
        emp_preload =
          Repo.preload(new_emp, :organizations)
          |> Repo.preload(:roles)
        # ATTEMPT  build assoc with org and role
        role_loaded =  Ecto.build_assoc(emp_preload, :roles, role_w_org)
        # both foreign keys here
        # %TurnStile.Role{
        #   __meta__: #Ecto.Schema.Metadata<:built, "roles">,
        #   id: nil,
        #   name: "owner",
        #   value: "1",
        #   employee_id: 24,
        #   employee: #Ecto.Association.NotLoaded<association :employee is not loaded>,
        #   organization_id: 1,
        #   organization: #Ecto.Association.NotLoaded<association :organization is not loaded>,
        #   inserted_at: nil,
        #   updated_at: nil
        # }
        # try to update role via employee update - role_loaded is not holding
        update = update_employee(emp_preload)  #-> Repo.update(...)
        #AND/OR
        # try to update role via organization update since it's preloaded
       update_organization(organization) #-> Repo.update(...)
    
    end
  end

I guess maybe my issue is not understanding where the “child” actually gets inserted (not just in elixir, but in any FK relationship). Since the child Role is not actually inserted by me.

Marked As Solved

al2o3cr

al2o3cr

There’s no need to give up, things aren’t THAT bad! :slight_smile:

You will always get better results with questions about Ecto if you are specific about what you are trying to do. In particular, this is important:

This specific task is alternatively phrased as “I want to insert an employee and also create a role associated to an existing organization”, which then translates to Ecto operations:

organization = get_the_org_somehow()

role = Ecto.build_assoc(organization, :roles, %{"name" => "owner", etc})
# role is unsaved and has an organization_id but no employee_id

employee_changeset =
  %Employee{}
  |> Employee.registration_changeset(attrs)
  |> Ecto.Changeset.put_assoc(:roles, [role])
# changeset contains an unsaved Employee with one unsaved Role

result = Repo.insert(employee_changeset)

You might also refactor this to make Employee.registration_changeset take an Organization and do the put_assoc there, to keep things tidy.

Also Liked

al2o3cr

al2o3cr

What do you mean by “role_loaded is not holding” here? There’s no connection between emp_preload and role_loaded.

It’s hard to say with certainty without the code for these, but neither one takes role_loaded as an argument so it’s not going to have an effect on them.

What happens if you do Repo.insert(role_loaded)?

Where Next?

Popular in Questions Top

JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New

We're in Beta

About us Mission Statement