我想知道如何构建我的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
答案 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的调用 - 但是更好,因为显然它更容易键入,但你也隐藏了实际的你的对象的结构。
因此,只处理值的例程可以在不知道其上方的层次结构的情况下了解模板,这意味着您将来可以更改它,而不会破坏现有代码。
在这种情况下,我总是把事情搞清楚 - 实际上你正在进行数据库设计,所以实体关系图虽然过时,但却是正确的工具。
我不完全确定3-way关系会给你什么 - 但它完全取决于值是什么 - 如果有很多重复值,那么数据库规范化规则声明你应该将它提取到一个单独的table(或Rails术语中的模型),这是3向关联带给你的。但这取决于价值是什么,有时候出于性能原因你必须进行非规范化。