从模板对象创建深层嵌套对象

时间:2015-07-18 05:45:05

标签: ruby-on-rails

我想知道如何构建我的rails关系,以便这样的事情成为可能(注意报告和值之间的遥远关系):

class Report
has_and_belongs_to_many :templates
has_many :values

class Template
has_and_belongs_to_many :reports
has_many :sections

class Section
belongs_to :template
has_many :columns

class Column
belongs_to :section
has_many :fields

class Field
belongs_to :column
has_many :values

class Value
belongs_to :field
belongs_to :report

有办法解决这个问题吗?目标是从模板创建报告,避免在数据库中复制Template.sections.columns.fields对象树,并且报告仍包含唯一值。

像rails方法'委托'之类的东西会以某种方式帮助我吗?

例如,这样的事情可以吗?

class Report
has_and_belongs_to_many :templates
has_many :sections, through: :templates
has_many :columns, through: :sections
has_many :fields, through: :columns
has_many :values
delegate :value, to: :fields

我还不完全了解委托如何运作。

另一种解决方案可能是使用一种3路连接表,如on this ruby-forum post

所述
class Report
has_many :field_values

class Field
has_many :field_values
has_many :values, through: :field_values

class Value
has_many :field_values
has_many :fields, through: :field_values

class FieldValue
belongs_to :report
belongs_to :field
belongs_to :value

2 个答案:

答案 0 :(得分:1)

这绝对不是一个简单,直接的解决方案,特别是使用AngularJS作为前端。这确实是一个具体问题,但也许可以学到很多东西。

使用以下模型和关系:

public synchronized void setDate(Calendar date) {
    // What to do.... 
    Calendar anotherCalendar = Calendar.getInstance();
    anotherCalendar.setTimeInMillis(date.getTimeInMillis());
}

将以下内容添加到reports_controller.rb的顶部(对于包含class Report < ActiveRecord::Base has_and_belongs_to_many :templates has_many :values accepts_nested_attributes_for :values end class Template < ActiveRecord::Base has_and_belongs_to_many :reports has_many :sections, dependent: :destroy accepts_nested_attributes_for :sections has_many :fields, through: :sections end class Section < ActiveRecord::Base belongs_to :template, inverse_of: :sections has_many :columns, dependent: :destroy accepts_nested_attributes_for :columns has_many :fields, through: :columns end class Column < ActiveRecord::Base belongs_to :section, inverse_of: :columns has_many :fields, dependent: :destroy accepts_nested_attributes_for :fields end class Field < ActiveRecord::Base belongs_to :column, inverse_of: :fields has_many :values has_many :options, dependent: :destroy accepts_nested_attributes_for :values accepts_nested_attributes_for :options end class Value < ActiveRecord::Base belongs_to :field belongs_to :report end 模型的angular-rails-templates用户,已讨论here):

accepts_nested_attributes_for

使用模板字段创建报告...模板...用于报告值:

class ModelController < ApplicationController
  nested_attributes_names = Model.nested_attributes_options.keys.map do |key| 
    key.to_s.concat('_attributes').to_sym
  end

  wrap_parameters include: Model.attribute_names + nested_attributes_names

  # ...
end

上面的参数:

def create
  template = current_user.templates.find(params[:template_id])
  @report = current_user.reports.new(allowed_params)
  @report.save
  @report.templates << template
  current_user.reports << @report
  template.fields.each do |field|
    @report.values.where({:field_id => field.id, :report_id => @report.id, :input => field.values.first.input }).first_or_create
  end
  render 'show', status: 201
end

显示AngularJS的报告:

private
  def allowed_params
    params.require(:report).permit(
      :title, :submission, :response, :active, :location,
      values_attributes: [
        :id, :report_id, :field_id, :input
      ]      
    )
  end

在AngularJS中,将报告值附加到适当的模板位置以供显示:

def show
  report = current_user.reports.find(params[:id])
  render json: report.as_json(
    :include => [:values, :templates => {
      :include => { :sections => {
        :include => { :columns => {
          :include => { :fields => {
            :include => :options
          }}
        }}
      }}
    }]
  )
end

与Angular中的值混淆:

ClassFactory.get({class: 'reports', id: $routeParams.reportId}, (res)->
    jQuery.extend $scope.report, res
    $scope.report.templates.forEach((template)->
        template.sections.forEach((section)->
            section.columns.forEach((column)->
                column.fields.forEach((field)->
                    field.values = $scope.report.values.filter((obj)->
                        return obj.field_id == field.id
                    )
                )
            )
        )
    )
)

更新报告。注意:values_attributes声明适用于angular-rails-templates用户:

<div ng-repeat="template in report.templates">
  <div ng-repeat="section in template.sections">
    <div ng-repeat="column in section.columns">
      <div ng-repeat="field in column.fields">
        <input type="text" ng-model="field.values[0].input" value="{{field.values[0].input}}>
      </div>
    </div>
  </div>
</div>

典型的铁轨更新:

$scope.saveReport = ->
    $scope.report.values_attributes = $scope.form.values
    $scope.report.$update({class: 'reports', id: $scope.form.id}, (res)->
        $location.path("/reports/#{$scope.form.id}")
        $rootScope.$broadcast('clearreports')
    )

完成

就像我说的,我知道这是非常具体的。希望有人可以学到一些东西和/或告诉我我很糟糕(以及为什么)!

修改 刚刚意识到我可以通过在Create方法中添加类似的东西来略微减少笨拙:

def update
  report = current_user.reports.find(params[:id])
  report.update_attributes(allowed_params)
  head :no_content
end

然后在Angular中引用$ scope.report.fields,以减少循环。学习行动。

答案 1 :(得分:0)

你的has_many没问题:我能看到的。但是在这种情况下你不能使用委托 - 因为你有一个Report(最终)有很多Fields,当你委托你委托给一个集合而不是一个对象。

委托只是一种快捷方法 - 例如,你可以委托另一个方向:

class Report
  has_and_belongs_to_many :templates
  has_many :values

class Template
  has_and_belongs_to_many :reports
  has_many :sections
  has_many :columns, through: :sections
  has_many :fields, through: :columns

class Section
  belongs_to :template
  has_many :columns

class Column
  belongs_to :section
  has_many :fields

class Field
  belongs_to :column
  has_many :values

class Value
  belongs_to :field
  belongs_to :report
  delegates :column, to: :field
  delegates :section, to: :column
  delegates :template, to: :section

然后调用@ value.template实际上是对@ value.field.column.section.template的调用 - 但是更好,因为显然它更容易键入,但你也隐藏了实际的你的对象的结构。

因此,只处理值的例程可以在不知道其上方的层次结构的情况下了解模板,这意味着您将来可以更改它,而不会破坏现有代码。

在这种情况下,我总是把事情搞清楚 - 实际上你正在进行数据库设计,所以实体关系图虽然过时,但却是正确的工具。

ERD of the data models

我不完全确定3-way关系会给你什么 - 但它完全取决于值是什么 - 如果有很多重复值,那么数据库规范化规则声明你应该将它提取到一个单独的table(或Rails术语中的模型),这是3向关联带给你的。但这取决于价值是什么,有时候出于性能原因你必须进行非规范化。