0

Situation:

I have 4 models with relationships like these:

class A < ActiveRecord::Base
  belongs_to :b
  belongs_to :c
  belongs_to :d
end

class B < ActiveRecord::Base
  has_many :as, dependent: :destroy
end

class C < ActiveRecord::Base
  belongs_to :d
end

class D < ActiveRecord::Base
  has_many :cs, dependent: :destroy
end

Problem:

I need to relate model A object a to model B object b but I also need to set a position for object a where to appear in ActiveRecord CollectionProxy when calling b.as and it won't work. All attempts to add object a to a certain position in a.bs result end up with having newly added a at the end. Is it possible at all? I know there are possibilities for defining ordering in arrays and while querying, but is it possible to define the ordering directly when adding new relationship?

Example:

Model B object b has 3 model A objects.

b.as
  => #<ActiveRecord::Associations::CollectionProxy [
    #<A id: 39038, quantity: 3.0 created_at: "2016-02-01 15:51:26", updated_at: "2016-02-01 15:51:26", d_id: 4144, c_id: nil, b_id: 81218>, 
    #<A id: 39039, quantity: 2.0, created_at: "2016-02-01 15:51:26", updated_at: "2016-02-01 15:51:26", d_id: 4145, c_id: nil, b_id: 81218>,
    #<A id: 39040, quantity: 2.0, created_at: "2016-02-01 15:51:26", updated_at: "2016-02-01 15:51:26", d_id: 4145, c_id: 1590, b_id: 81218>]>

I create fourth model A object a and I need it to appear on second place when calling b.as like this:

a = A.create(quantity: 4.0, d: D.find(4144), c: C.find(1612), b: b)

Basically the ordering should be defined like this:

b.as
=>
  a1: d_id: 4144, c_id: nil
  a2: d_id: 4144, c_id: 1612
  a3: d_id: 4145, c_id: nil
  a4: d_id: 4145, c_id: 1590

...meaning that every a object with same d_id attribute should be grouped together meanwhile having those as that have c_id.nil? in first place.

Andres
  • 2,099
  • 3
  • 22
  • 39

2 Answers2

1

The add method will always add it to the end of the list. You need some sort of a position attribute to achieve what you want. If you don't want to implement that yourself, you can take a look at Acts as list gem. Another option is to add a default_scope { order: :key_to_order_by } on the As model.

Jonas Meinerz
  • 612
  • 3
  • 9
  • With a little modification `scope :in_order, -> { order("d_id, id") }` did what I needed exactly, thanks! – Andres Feb 02 '16 at 09:16
1
has_many :as, -> { order(d_id: :asc, c_id: :asc) }

Should be close, except nil is ordered at the end after the highest cd_id. That's postgres doing the sort, so I don't know if there is much you can do there.

Leonel Galán
  • 6,993
  • 2
  • 41
  • 60
  • This lead me to errors where I had declarations like `b.reload` while new `b` was not saved yet (error: `Couldn't find B without an ID`). I guess I can't order sub-objects if they don't know the parent-object ID? – Andres Feb 02 '16 at 09:11
  • Not using `order` that translates to a SQL statement. Why where you reloading `b` if it hasn't being saved. Reload is meant to reinitialize the record from the DB, the documentation says: "Reloads the record from the database. This method finds record by its primary key ..." – Leonel Galán Feb 02 '16 at 15:08