Converting an array of objects to ActiveRecord::Relation

后端 未结 5 1371
醉酒成梦
醉酒成梦 2020-12-04 07:00

I have an array of objects, let\'s call it an Indicator. I want to run Indicator class methods (those of the def self.subjects variety, scopes, etc

相关标签:
5条回答
  • 2020-12-04 07:41

    You can convert an array of objects arr to an ActiveRecord::Relation like this (assuming you know which class the objects are, which you probably do)

    MyModel.where(id: arr.map(&:id))
    

    You have to use where though, it's a useful tool which you shouldn't be reluctant to use. And now you have a one-liner converting an array to a relation.

    map(&:id) will turn your array of objects to an array containing only their id's. And passing an array to a where clause will generate a SQL statement with IN that looks something like:

    SELECT .... WHERE `my_models`.id IN (2, 3, 4, 6, ....
    

    Keep in mind that the ordering of the array will be lost - But since your objective is only to run a class method on the collection of these objects, I assume it won't be a problem.

    0 讨论(0)
  • 2020-12-04 07:46

    ActiveRecord::Relation binds database query which retrieves data from database.

    Suppose to make sense, We have array with objects of same class, then with which query we suppose to bind them?

    When I run,

    users = User.where(id: [1,3,4,5])
      User Load (0.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc
    

    Here in above, usersreturn Relation object but binds database query behind it and you can view it,

    users.to_sql
     => "SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc"
    

    So it is not possible to return ActiveRecord::Relation from array of objects which is independent of sql query.

    0 讨论(0)
  • 2020-12-04 07:48

    How can I convert an Array of objects to an ActiveRecord::Relation? Preferably without doing a where each time.

    You cannot convert an Array to an ActiveRecord::Relation since a Relation is just a builder for a SQL query and its methods do not operate on actual data.

    However, if what you want is a relation then:

    • for ActiveRecord 3.x, don’t call all and instead call scoped, which will give back a Relation which represents the same records that all would give you in an Array.

    • for ActiveRecord 4.x, simply call all, which returns a Relation.

    When running a def self.subjects type method on an ActiveRecord::Relation, how do I access that ActiveRecord::Relation object itself?

    When the method is called on a Relation object, self is the relation (as opposed to the model class it’s defined in).

    0 讨论(0)
  • 2020-12-04 07:53

    Well, in my case, I need to converting an array of objects to ActiveRecord::Relation as well as sorting them with a specific column(id for instance). Since I'm using MySQL, the field function could be helpful.

    MyModel.where('id in (?)',ids).order("field(id,#{ids.join(",")})") 
    

    The SQL looks like:

    SELECT ... FROM ... WHERE (id in (11,5,6,7,8,9,10))  
    ORDER BY field(id,11,5,6,7,8,9,10)
    

    MySQL field function

    0 讨论(0)
  • 2020-12-04 07:56

    First of all, this is NOT a silver bullet. Out of my experience, I found that converting to relation is sometimes easier than alternatives. I try to use this approach very sparingly and only in cases where the alternative would be more complex.

    That being said here is my solution, I've extended Array class

    # lib/core_ext/array.rb
    
    class Array
    
      def to_activerecord_relation
        return ApplicationRecord.none if self.empty?
    
        clazzes = self.collect(&:class).uniq
        raise 'Array cannot be converted to ActiveRecord::Relation since it does not have same elements' if clazzes.size > 1
    
        clazz = clazzes.first
        raise 'Element class is not ApplicationRecord and as such cannot be converted' unless clazz.ancestors.include? ApplicationRecord
    
        clazz.where(id: self.collect(&:id))
      end
    end
    

    A usage example would be array.to_activerecord_relation.update_all(status: 'finished'). Now where do I use it?

    Sometimes you need to filter out ActiveRecord::Relation for example take out not completed elements. In those cases best is to use scope elements.not_finished and you would still keep ActiveRecord::Relation.

    But sometimes that condition is more complex. Take out all elements that are not finished, and that has been produced in the last 4 weeks and have been inspected. To avoid creating new scopes you can filter to an array and then convert back. Keep in mind that you still do a query to DB, quick since it searches by id but still a query.

    0 讨论(0)
提交回复
热议问题