简体   繁体   中英

Rails the best way to scope vars

i have a 'Course' model that has the following attributes;

Course
  Price - float 
  Featured - boolean

My question would be the following, I need 4 lists in my controller, recent courses, paid courses, free courses and featured courses.

It would be good practice to write my controller as follows?

def index
  @courses = Course.order(created_at: :desc)

  @free_courses = []
  @courses.map {|c| @free_courses << c if c.price == 0}

  @premium_courses = []
  @courses.map {|c| @premium_courses << c if c.price> 0}

  @featured_courses = []
  @courses.map {|c| @featured_courses << c if c.featured}
end

Or do the consultations separately?

def index
  @courses = Course.order(created_at: :desc)
  @free_courses = Course.where("price == 0")
  @premium_courses = Course.where("price > 0")
  @featured_courses = Course.where(featured: true)
end

I checked through the logs that the first option is more performance but I am in doubt if it is an anti partner.

Thanks for all!

The second approach will become faster than the first as the size of the Course table increases. The first approach has to iterate over every record in the table 4 times. The second approach creates a Relation of only the records that match the where clause, so it does less work.

Also, the second approach has the advantage of laziness. Each query is only run at the time it is used, so it can be changed further along the code path. It's more flexible.

Note that it would be an improvement to the second approach to create scopes on the Course model that handles the logic. For example, one each for courses, free_courses, premium_courses and featured courses. This has the advantage of putting database logic in the model instead of the controller, where it can more easily be reused and maintained.

第二种方法更好,因为当您使用.where()方法时,您是将查询安排在数据库本身而不是控制器中。

It is generally bad practice to iterate over all records in the database in Rails (ie Course.map or Course.all ) both for performance and memory usage. As your database grows this becomes exponentially problematic. It's much better to use Course.where() methods. You'll probably want a default sort order so you can add with one line in your model.

default_scope { order(created_at: :desc) }

Then you can just do this in controller and they'll have the sort by default:

@courses = Course.all

I would also suggest adding scopes to your model for easier access. So in your course.rb file

scope :free -> { where("price == 0") }
scope :premium -> { where("price > 0") }
scope :featured -> { where(featured: true) }

Then in your controller you can just do:

@courses = Course.all
@free_courses = Course.free
@premium_courses = Course.premium
@featured_courses = Course.featured

These scopes can also be chained if you need to combine those so you could do things like:

@mixed_courses = Course.premium.featured

As others have explained, Model.where() executes the selection of data by passing sql inside where("Write Pure SQL QUERIES HERE") where as regular ruby enumerable methods ( .map ) iterate over array which must be instantiated as ruby objects. That's where the memory / performance issues take the hit. It's ok if you're working with small data sets, but anything with data volume will get ugly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM