虚拟列计数记录

时间:2014-08-25 06:40:49

标签: ruby-on-rails

首先,对不起我的英语,即使是非常基本的东西,我也很喜欢ruby on rails,所以我希望你们都能帮助我。

我有表Role和RoleUser table具有与RoleUser的has_many关系,其中role_id为外键 在表中,RoleUser包含user_id,所以我可以称之为1个角色有很多用户

我希望在每个名为total_users,

的记录中显示Role中的所有记录和附加字段

total_users在每条记录中都有role_id并计算每个角色的user_id,并将其放在total_users中,

我知道这必须使用连接表,但在rails中我绝对不知道这一点,你能给我一个简单的例子来做这件事。

还有一个,与上面的情况相同,我可以做一些例如Role.all,然后在包含的total_users中没有将它添加到数据库中吗?那是用虚拟列吗? 任何人都有很好的链接来了解

我在模型中有以下代码

def with_filtering(params, holding_company_id)
  order = []
  if params[:sort].present?
    JSON.parse(params[:sort]).each do |data|
      order << "#{data['property']} #{data['direction']}"
    end
  end
  order = 'id ASC' if order.blank?

  if self.column_names.include? "holding_company_id"
    string_conditions = ["holding_company_id = :holding_company_id"]
    placeholder_conditions = { holding_company_id: holding_company_id.id }
  else
    string_conditions = []
    placeholder_conditions = {}
  end

  if params[:filter].present?
    JSON.parse(params[:filter]).each do |filter|
      if filter['operation'] == 'between'
        string_conditions << "#{filter['property']} >= :start_#{filter['property']} AND #{filter['property']} <= :end_#{filter['property']}"
        placeholder_conditions["start_#{filter['property']}".to_sym] = filter['value1']
        placeholder_conditions["end_#{filter['property']}".to_sym] = filter['value2']
      elsif filter['operation'] == 'like'
        string_conditions << "#{filter['property']} ilike :#{filter['property']}"
        placeholder_conditions["#{filter['property']}".to_sym] = "%#{filter['value1']}%"
      else
        string_conditions << "#{filter['property']} = :#{filter['property']}"
        placeholder_conditions["#{filter['property']}".to_sym] = filter['value1']
      end
    end
  end

  conditions = [string_conditions.join(' AND '), placeholder_conditions]

  total_count = where(conditions).count

  if params[:limit].blank? && params[:offset].blank?
    data = where(conditions).order(order)
  else
    data = where(conditions).limit(params[:limit].to_i).offset(params[:offset].to_i).order(order)
  end

  return data, total_count.to_s
end

我在控制器中有以下代码

def crud_index(model)
  data, total = Role.with_filtering(params, current_holding_company)
  respond_to do |format|
    format.json { render json: { data: data, total_count: total }.to_json, status: 200 }
  end
end

我唯一的目的是添加名为total_users的虚拟字段,但我想在模型中添加它并将其与方法with_filtering中的数据相结合

3 个答案:

答案 0 :(得分:4)

如果你有这样的模型:

Class Role < ActiveRecord::Base
  has_many :role_users
end

Class RoleUser < ActiveRecord::Base
  belong_to :role
end

您可以使用select和join来生成摘要列,但所有Role的属性都应包含在组中。

roles = Role.select("roles.*, count(role_users.id) as total_users")
            .joins(:role_users)
            .group("roles.id")

在Rails控制台中键入这些脚本,Rails将生成一个类似的SQL:

SELECT roles.id, count(role_users.id) as total_users
FROM roles
INNER JOIN role_users
ON  roles.id = role_users.role_id
GROUP BY roles.id

然后您可以使用roles.to_json查看结果。可以在每个角色成员中访问摘要列total_users

还有很多其他方式可以满足您的要求。例如this。引用了counter cache

我的建议是在搜索之后,你可以通过rails console测试这些方法,这是一个很有用的工具。

<强>更新

根据OP的更新和评论,您似乎还有更多工作要做。

第1步:将with_filtering类方法移动到控制器

with_filtering处理很多参数事情来获取条件,它应该在控制器而不是模型中处理。因此,我们可以将with_filtering转移到控制器中的conditionsorders

class RolesController < ApplicationController

  def conditions(params, holding_company_id)
    if self.column_names.include? "holding_company_id"
      string_conditions = ["holding_company_id = :holding_company_id"]
      placeholder_conditions = { holding_company_id: holding_company_id.id }
    else
      string_conditions = []
      placeholder_conditions = {}
    end

    if params[:filter].present?
      JSON.parse(params[:filter]).each do |filter|
        if filter['operation'] == 'between'
          string_conditions << "#{filter['property']} >= :start_#{filter['property']} AND #{filter['property']} <= :end_#{filter['property']}"
          placeholder_conditions["start_#{filter['property']}".to_sym] = filter['value1']
          placeholder_conditions["end_#{filter['property']}".to_sym] = filter['value2']
        elsif filter['operation'] == 'like'
          string_conditions << "#{filter['property']} ilike :#{filter['property']}"
          placeholder_conditions["#{filter['property']}".to_sym] = "%#{filter['value1']}%"
        else
          string_conditions << "#{filter['property']} = :#{filter['property']}"
          placeholder_conditions["#{filter['property']}".to_sym] = filter['value1']
        end
      end
    end

    return [string_conditions.join(' AND '), placeholder_conditions]
  end

  def orders(params)
    ord = []
    if params[:sort].present?
      JSON.parse(params[:sort]).each do |data|
        ord << "#{data['property']} #{data['direction']}"
      end
    end
    ord = 'id ASC' if ord.blank?    
    return ord
  end
end

第2步:使用crud_indexconditions更新操作orders以获取角色的total_count。

class AnswersController < ApplicationController
  def crud_index(model)
    total = Role.where(conditions(params, current_holding_company)).count

    if params[:limit].blank? && params[:offset].blank?
      data = Role.where(conditions(params, current_holding_company)).order(orders(params))
    else
      data = Role.where(conditions(params, current_holding_company)).limit(params[:limit].to_i).offset(params[:offset].to_i).order(orders(params))
    end
    respond_to do |format|
      format.json { render json: { data: data, total_count: total }.to_json, status: 200 }
    end
  end
end

第3步:更新操作crud_index以获得每个角色的total_users。

确保前两个步骤通过测试。

class AnswersController < ApplicationController
  def crud_index(model)
    total = Role.where(conditions(params, current_holding_company)).count

    if params[:limit].blank? && params[:offset].blank?
      data = 
        Role.select(Role.column_names.map{|x| "Roles.#{x}"}.join(",") + " ,count(role_users.id) as total_users")
            .joins(:role_users)
            .group(Role.column_names.map{|x| "Roles.#{x}"}.join(","))
            .where(conditions(params, current_holding_company))
            .order(orders(params))
    else
      data =
        Role.select(Role.column_names.map{|x| "Roles.#{x}"}.join(",") + " ,count(role_users.id) as total_users")
            .joins(:role_users)
            .group(Role.column_names.map{|x| "Roles.#{x}"}.join(","))
            .where(conditions(params, current_holding_company))
            .order(orders(params))
            .limit(params[:limit].to_i)
            .offset(params[:offset].to_i).order(orders(params))
    end
    respond_to do |format|
      format.json { render json: { data: data, total_count: total }.to_json, status: 200 }
    end
  end
end

注意:步骤3可能需要您修改conditionsorders方法以生成带有table_name前缀的column_name,以避免列名模糊错误

如果您可以执行这些步骤,建议您尝试使用will_paginate来简化代码中有关total_countlimitoffset的部分内容。

答案 1 :(得分:0)

根据你的解释,你可以这样做:

class Role < ActiveRecord::Base
  has_many :role_users
  has_many :users

  def total_users
    self.users.count
  end
end

所以你只需要在角色对象上调用total_users方法,它可以让你得到你想要的东西。像这样:

Role.first.total_users 
# this will give you the total users for the first role found in your database. 

希望有所帮助

答案 2 :(得分:0)

您可能也希望watch this Railscast

#app/models/role.rb
Class Role < ActiveRecord::Base
   has_many :role_users
   has_many :users, -> { select "users.*", "role_users.*", "count(role_users.user_id) as total_users" }, through: :role_users
end

这将允许您致电:

@roles = Role.find params[:id]
@roles.users.each do |role|
   role.total_users
end

您可以通过我前一段时间写的问题 - Using Delegate With has_many In Rails?

了解更多相关信息

-

这是我了解Alias columns的地方,Ryan Bates用它来count某些值:

enter image description here