Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving ActiveRecord's associations which are pointing to each other, at once

I have the following models

class Cargo < ApplicationRecord
  has_many :destinations
  has_many :assignments
  accepts_nested_attributes_for :destinations
  accepts_nested_attributes_for :assignments
end

class Destination < ApplicationRecord
  has_one :assignment_coming_to_me, class_name: 'Assignment', foreign_key: 'arrive_destination_id'
  has_one :assignment_leaving_me, class_name: 'Assignment', foreign_key: 'start_destination_id'
end

class Assignment < ApplicationRecord
  belongs_to :start_from, class_name: 'Destination', foreign_key: 'start_destination_id'
  belongs_to :arrive_at, class_name: 'Destination', foreign_key: 'arrive_destination_id'
end

Giving a visual image, it's something like this

                +-------------+
            +---| Destination |   
            |   +-------------+---+ <= start_from
            |                     |   +------------+ 
            |                     +---| Assignment |
            |                     |   +------------+ 
+-------+   |   +-------------+---+ <= arrive_at
| Cargo | --+---| Destination |   
+-------+   |   +-------------+---+ <= start_from
            |                     |   +------------+ 
            |                     +---| Assignment |
            |                     |   +------------+ 
            |   +-------------+---+ <= arrive_at 
            +---| Destination |   
            |   +-------------+---+
            |                     |
            .                     .
            .                     .

Now, is there a way to create the whole records at once, given the the parameters like this? (Suppose the destinations and assignments are associated to each other just as the order of the parameter array)

{
  cargo: [
    destinations_attributes: [{place_id: ..}, {place_id: ...}, ...],
    assignments_attributes: [{assignee_id: ..}, {assignee_id: ...}, ...],
  ]
}

I know saving Destinations first then iterating through them to set Assignment's destination_id can do the job, but wondering if there's a smarter way.

like image 937
Ryo Avatar asked Aug 24 '17 06:08

Ryo


People also ask

What is the difference between Has_one and Belongs_to?

They essentially do the same thing, the only difference is what side of the relationship you are on. If a User has a Profile , then in the User class you'd have has_one :profile and in the Profile class you'd have belongs_to :user . To determine who "has" the other object, look at where the foreign key is.

What is ActiveRecord naming convention?

ActiveRecord expects applications to follow certain naming conventions. These conventions extend from file naming, class naming, table naming and more. By default classes are singular, tables are plural, primary keys are id and foreign keys are table_id.

What is ActiveRecord relation?

Whereas an instance of ActiveRecord::Relation is a representation of a query that can be run against your database (but wasn't run yet). Once you run that query by calling to_a , each , first etc. on that Relation a single instance or an array of ActiveRecord::Base instances will be returned.


2 Answers

Well, not knowing the details of your problem I will answer what I think it would be a "smater" way to solve.

Use a has_many through association where you would have:

Cargo has many Destinations through Assignments.

Destinations has many Cargo through Assignments.

Assignments holds both Cargo and Destinations foreign key.

If you need information about the order of destinations, you could just query the records based on the Assignment's create timestamp.

Here has a example-of-has-many-through-using-nested-attributes. I believe that way you can have rails saving all the records "automagicly". Furthermore, you can have the queries offered by the association to handle all the three table's records!

Let me know what you think! Good luck!

Link to guide rails has_many_through assoaciton

like image 124
Pedro Gabriel Lima Avatar answered Sep 19 '22 00:09

Pedro Gabriel Lima


Hierarchical structure is too rich for architectural purposes: I could reproduce all your workflow with it:

In the end of the code I left comments

You can run the code in terminal just copy it to test.rb file and run ruby test.rb in terminal

And just before, create db in postgresql (or mysql)

require "active_record"

class Cargo < ActiveRecord::Base
  establish_connection adapter: 'postgresql', database: 'test_destination'

  connection.create_table table_name, force: true do |t|
    t.string :name
  end

  has_many :delivery_places

  accepts_nested_attributes_for :delivery_places
end

class DeliveryPlace < ActiveRecord::Base
  establish_connection adapter: 'postgresql', database: 'test_destination'

  connection.create_table table_name, force: true do |t|
    t.string     :name
    t.integer    :order, default: 0
    t.datetime   :arrived_at
    t.belongs_to :cargo
  end

  belongs_to :cargo
  has_one :assigment

  accepts_nested_attributes_for :assigment
end

class Assignee < ActiveRecord::Base
  establish_connection adapter: 'postgresql', database: 'test_destination'

  connection.create_table table_name, force: true do |t|
    t.string :name
  end

  has_many :assigments
  has_many :delivery_places, through: :assigments
  has_many :cargos, through: :delivery_places
end

class Assigment < ActiveRecord::Base
  establish_connection adapter: 'postgresql', database: 'test_destination'

  connection.create_table table_name, force: true do |t|
    t.belongs_to :delivery_place
    t.belongs_to :assignee
  end

  belongs_to :delivery_place
  belongs_to :assignee
  accepts_nested_attributes_for :assignee
end

nikolay = Assignee.create! name: 'Nikolay'
dec12 = DateTime.new(2017, 12, 12)
dec13 = DateTime.new(2017, 12, 13)
dec14 = DateTime.new(2017, 12, 14)

Cargo.create! \
  name: 'candies',
  delivery_places_attributes: [
    {
      name: 'Moscow',
      order: 0,
      arrived_at: dec12,
      assigment_attributes: {
        assignee_id: nikolay.id
      }
    },
    {
      name: 'Tokio',
      order: 1,
      arrived_at: dec13,
      assigment_attributes: {
        assignee_attributes: {
          name: 'Ryo'
        }
      }
    },
    {
      name: 'Ny York',
      order: 2,
      arrived_at: dec14,
      assigment_attributes: {
        assignee_attributes: {
          name: 'John'
        }
      }
    }
  ]


# Let's find all my cargos
puts nikolay.cargos
# => [#<Cargo:0x007fae3e4475e8 id: 1, name: "candies">]

# Do I have any cargos since 13 december till 14 december?
puts nikolay.cargos.joins(:delivery_places).where(delivery_places: { arrived_at: dec13...dec14 }).distinct
# []

# Do I have any cargos since 12 december till 14 december?
puts nikolay.cargos.joins(:delivery_places).where(delivery_places: { arrived_at: dec12...dec14 }).distinct
# => [#<Cargo:0x007fefac16a460 id: 1, name: "candies">]

# Do I have cargo which I sent?
nikolay.cargos.joins(:delivery_places).where(delivery_places: { order: 0 }).distinct
# => [#<Cargo:0x007fefac16a460 id: 1, name: "candies">]
like image 32
itsnikolay Avatar answered Sep 19 '22 00:09

itsnikolay