stephskardal / rails_admin_import

Rails Admin Import functionality
http://www.endpoint.com/
MIT License
160 stars 122 forks source link

Importing record associations that are related :through another model #51

Closed zrisher closed 8 years ago

zrisher commented 8 years ago

I'm trying to import a Service that has_one :user, through: :pro, where :pro is a "role model" that holds information about that particular role a user may fulfill. I have the user's email to uniquely identify it, but I can't get the association to save.

I have:

A standard user model with an email field, which can have various role models attached:

class User < ActiveRecord::Base
  has_one :pro
  has_one :other_role_model
  has_one :some_other_role_model
  ...etc...
end

Various role models that exist for any user that fulfills that role, and holds data corresponding to that role:

class Pro < ActiveRecord::Base
  belongs_to :user
end

class OtherRoleModel < ActiveRecord::Base
  belongs_to :user
end

...etc...

And finally my Service model, which is only tied to users with a Pro role:

class Service < ActiveRecord::Base
  belongs_to :pro
  has_one :user, through: :pro
end

Now I want to import a service which belongs to a particular user via their pro role. The only unique identifier I have for that user/pro is their email, so I want to provide a csv with the format you listed above:

date,description,price,user
2016-02-12T03:21:55Z,Project sample,10000,test@example.com

This will go through fine, but each service's Pro never actually gets set, which means their User is null too.

Looking through the code, it seems like import_single_association_data will try to set service.user = user, given the service it's created and the user that it (successfully) finds. But that never sets the :through association, so service.pro is nil and of course after saving service.user becomes nil because it's accessed through service.pro.

I have a feeling I'm tripping over how ActiveRecord associations work here.

I tried setting the user's email field as a delegate in Pro:

class Pro < ActiveRecord::Base
  belongs_to :user
  delegate :email, to: :user
end

and then changing my import data to

date,description,price,pro
2016-02-12T03:21:55Z,Project sample,10000,test@example.com

But unfortunately I'm unable to select email as the mapping field for pro, even with the above delegation, probably because mapping fields are pulled directly from AR attributes.

I imagine I'm going to just work around this in the before_import_save hook. After changing the import column name to "user_email", I can write:

class Service < ActiveRecord::Base
  belongs_to :pro
  has_one :user, through: :pro

  def before_import_save(record)
    if (email = record[:user_email]) and (user = User.find_by_email(email))
      self.pro = user.pro
    end
  end
end

and it should work. But I'm wondering if there's a cleaner approach that I'm missing.

zrisher commented 8 years ago

Confirmed, my final suggested approach worked. Still curious if there's a more general approach.

monkbroc commented 8 years ago

You got it: for more complex cases you have to use the import hook. On Feb 12, 2016 12:30 AM, "zrisher" notifications@github.com wrote:

Confirmed, my final suggested approach worked. Still curious if there's a more general approach.

— Reply to this email directly or view it on GitHub https://github.com/stephskardal/rails_admin_import/issues/51#issuecomment-183185396 .

zrisher commented 8 years ago

Rgr that, will include in docs PR per #50