如何创建更新 Child 属性的 before_action Rails
How to create a before_action that updates a Child's attributes Rails
对于这个应用,一个App
有很多Elements
,每个Elements
有很多Features
。我想在每次 AppsController#show
运行 时更新 Elements
属性。但是,我 运行 遇到了一个问题,因为我必须深入研究每个元素的 Features
。我似乎无法从我的应用程序控制器调用 @app.elements.features
(或根本无法调用 features
。)
我在 App's Show View
上嵌入了表格,其中列出了应用程序的 Elements
和 Features
,所以我不能将其称为 ElementsController#show
或其他简单的名称就像那样,因为那样它就永远不会更新了。我必须找到一些方法来从 AppsController
调用 ElementsController
中的此操作,但这对我来说听起来不太 Rails-ey。
这是我到目前为止所做的,现在我不知道下一步该做什么。完成这项工作的最佳方法是什么?
我的应用程序控制器 (parent):
before_action :set_completion, only: :show
def set_completion
@app = App.find(params[:id])
@app.update_attribute(:completion_status,
(((@app.elements.where(completion: true).count) / (@app.elements.count).to_f) * 100 )
)
end
元素控制器(child):
def update_feature_completion
@app = App.find(params[:app_id])
@element.update_attribute(:element_feature_completion,
(@element.features.where(completion: true).count)
)
end
编辑:
到目前为止我所拥有的(无论如何都不简单):
def set_completion
@parent = Parent.find(params[:id])
@parent.childs.each do |child|
if (child.childs_childs.where(completion: true).count) == (child.childs_childs.count) #if all childs_childs of the child are completed...
then child.update_attribute(:completion, true) #...mark the child as completed.
else child.update_attribute(:completion, false) #if all childs_childs of the child are not completed, then mark the child as not completed
end
#changes child completion status to % of childs_childs completed
child.update_attribute(:child_completion_status,
child.childs_childs.where(completion: true).count / child.childs_childs.count.to_f
)
@parent.update_attribute(:parent_completion_status,
@parent.childs.child_completion_status.sum
)
end
end
Parent 型号
class Parent < ActiveRecord::Base
belongs_to :user
has_many :children, dependent: :destroy
has_many :childs_children, dependent: :destroy
accepts_nested_attributes_for :children
accepts_nested_attributes_for :childs_childs
end
Child 型号
class Child < ActiveRecord::Base
belongs_to :parent
has_many :childs_children, dependent: :destroy
accepts_nested_attributes_for :childs_children
end
Child的Child型号
class childs_child < ActiveRecord::Base
belongs_to :child
belongs_to :parent
end
最后一段代码如下所示:
@parent.update_attribute(:parent_completion_status,
(@parent.children.sum(:child_completion_status) / @parent.children.count) * 100
)
这基本上是通过找出所有 children
的完成百分比来找到 :parent_completion_status
的完成百分比。
我知道这很令人困惑,但我相信外面的人能够理解它。当我问这个问题时,我无法按照我最初想要的方式解决这个问题。
建议如果您尝试做类似的事情,请尝试按照我所做的步骤进行操作,除非有更简单的方法适合您。
这可能会在以后的路上咬到你的后方。每次 GET 请求到达显示页面时数据库中的数据都会发生变化,这可能会给您的服务器带来不必要的压力。想一想所有访问您页面的网络爬虫。他们会无意中触发这些数据库事务的发生。
如果您要更改任何持久化数据,应该通过 POST 方法来完成。动作回调通常不用于此类工作,通常应仅用于身份验证和缓存之类的事情。那么我可以提供替代解决方案吗?
所以我们从一个很好的深层嵌套关系开始。但是,当我们向 apps#show 发出 GET 请求时,我们不必不断地进行这些计算,而是必须首先查看导致这些变化的原因。看来您的数据只会随着功能更新为完整而真正改变。那就是您应该强制进行这些更改的地方。如果你做了这样的事情怎么办
让我们尝试采用 RESTful 方法来解决这个问题。由于功能已标记为完整,因此它们各自的元素和应用程序也应更新。我们还假设如果每个功能都未标记为完整,则无法将元素标记为完整。
在您的 apps#show 页面上,我假设您正在显示应用程序的所有元素和元素从属功能。我想您会希望您的用户单击某个功能上的按钮,将该功能标记为已完成。因此,让我们创建一个允许我们这样做的 RESTful 路由。
config/routes.rb
resources :features do
member do
post 'complete' # features#complete
end
end
所以现在我们有一个 RESTful 路由,它将在您的 FeaturesController 中查找完整的操作。
features_controller.rb
def complete
if @feature.mark_as_complete
# show some message that says "You successfully completed 'Feature A', Good job!"
else
# Respond with nasty error message of why this request couldn't process
end
end
现在您有了一个与该功能配合使用的操作。现在您所要做的就是在您的 apps#show 页面中创建一个 link 并遍历每个元素的功能
app/views/apps/show.html.haml
- @app.elements.each do |element|
- element.features.each do |feature|
= feature.name
= link_to complete_feature_path(feature), method: :post
当用户完成每个功能时,他们可以单击 link,这将向功能发送 post 方法#complete action
class Feature < ActiveRecord::Base
def mark_as_complete
self.update_attributes(completion: true)
self.element.update_completion_status
end
end
class Element < ActiveRecord::Base
def update_completion_status
if (self.feautres.where(completion: true).count) == (self.features.count) # if all features are complete
self.update_attributes(completion: true)
self.update_attributes(feature_completion_status: calculate_percent_complete)
self.app.update_completion_status
else
self.update_attributes(feature_completion_status: calculate_percent_complete)
end
end
private
def calculate_percent_complete
self.features.where(completion: true).count / child.childs_childs.count.to_f
end
end
class Feature < ActiveRecord::Base
def mark_as_complete
self.update_attributes(completion: true)
self.element.update_completion_status
end
end
class App < ActiveRecord::Base
def update_completion_status
# similar to that of Element I assume
end
end
这样,仅当 POST 请求将功能标记为完成时,才会更新相应的数据。您在 apps#show 操作中应该做的就是提供要显示的适当对象。
对于这个应用,一个App
有很多Elements
,每个Elements
有很多Features
。我想在每次 AppsController#show
运行 时更新 Elements
属性。但是,我 运行 遇到了一个问题,因为我必须深入研究每个元素的 Features
。我似乎无法从我的应用程序控制器调用 @app.elements.features
(或根本无法调用 features
。)
我在 App's Show View
上嵌入了表格,其中列出了应用程序的 Elements
和 Features
,所以我不能将其称为 ElementsController#show
或其他简单的名称就像那样,因为那样它就永远不会更新了。我必须找到一些方法来从 AppsController
调用 ElementsController
中的此操作,但这对我来说听起来不太 Rails-ey。
这是我到目前为止所做的,现在我不知道下一步该做什么。完成这项工作的最佳方法是什么?
我的应用程序控制器 (parent):
before_action :set_completion, only: :show
def set_completion
@app = App.find(params[:id])
@app.update_attribute(:completion_status,
(((@app.elements.where(completion: true).count) / (@app.elements.count).to_f) * 100 )
)
end
元素控制器(child):
def update_feature_completion
@app = App.find(params[:app_id])
@element.update_attribute(:element_feature_completion,
(@element.features.where(completion: true).count)
)
end
编辑: 到目前为止我所拥有的(无论如何都不简单):
def set_completion
@parent = Parent.find(params[:id])
@parent.childs.each do |child|
if (child.childs_childs.where(completion: true).count) == (child.childs_childs.count) #if all childs_childs of the child are completed...
then child.update_attribute(:completion, true) #...mark the child as completed.
else child.update_attribute(:completion, false) #if all childs_childs of the child are not completed, then mark the child as not completed
end
#changes child completion status to % of childs_childs completed
child.update_attribute(:child_completion_status,
child.childs_childs.where(completion: true).count / child.childs_childs.count.to_f
)
@parent.update_attribute(:parent_completion_status,
@parent.childs.child_completion_status.sum
)
end
end
Parent 型号
class Parent < ActiveRecord::Base
belongs_to :user
has_many :children, dependent: :destroy
has_many :childs_children, dependent: :destroy
accepts_nested_attributes_for :children
accepts_nested_attributes_for :childs_childs
end
Child 型号
class Child < ActiveRecord::Base
belongs_to :parent
has_many :childs_children, dependent: :destroy
accepts_nested_attributes_for :childs_children
end
Child的Child型号
class childs_child < ActiveRecord::Base
belongs_to :child
belongs_to :parent
end
最后一段代码如下所示:
@parent.update_attribute(:parent_completion_status,
(@parent.children.sum(:child_completion_status) / @parent.children.count) * 100
)
这基本上是通过找出所有 children
的完成百分比来找到 :parent_completion_status
的完成百分比。
我知道这很令人困惑,但我相信外面的人能够理解它。当我问这个问题时,我无法按照我最初想要的方式解决这个问题。
建议如果您尝试做类似的事情,请尝试按照我所做的步骤进行操作,除非有更简单的方法适合您。
这可能会在以后的路上咬到你的后方。每次 GET 请求到达显示页面时数据库中的数据都会发生变化,这可能会给您的服务器带来不必要的压力。想一想所有访问您页面的网络爬虫。他们会无意中触发这些数据库事务的发生。
如果您要更改任何持久化数据,应该通过 POST 方法来完成。动作回调通常不用于此类工作,通常应仅用于身份验证和缓存之类的事情。那么我可以提供替代解决方案吗?
所以我们从一个很好的深层嵌套关系开始。但是,当我们向 apps#show 发出 GET 请求时,我们不必不断地进行这些计算,而是必须首先查看导致这些变化的原因。看来您的数据只会随着功能更新为完整而真正改变。那就是您应该强制进行这些更改的地方。如果你做了这样的事情怎么办
让我们尝试采用 RESTful 方法来解决这个问题。由于功能已标记为完整,因此它们各自的元素和应用程序也应更新。我们还假设如果每个功能都未标记为完整,则无法将元素标记为完整。
在您的 apps#show 页面上,我假设您正在显示应用程序的所有元素和元素从属功能。我想您会希望您的用户单击某个功能上的按钮,将该功能标记为已完成。因此,让我们创建一个允许我们这样做的 RESTful 路由。
config/routes.rb
resources :features do
member do
post 'complete' # features#complete
end
end
所以现在我们有一个 RESTful 路由,它将在您的 FeaturesController 中查找完整的操作。
features_controller.rb
def complete
if @feature.mark_as_complete
# show some message that says "You successfully completed 'Feature A', Good job!"
else
# Respond with nasty error message of why this request couldn't process
end
end
现在您有了一个与该功能配合使用的操作。现在您所要做的就是在您的 apps#show 页面中创建一个 link 并遍历每个元素的功能
app/views/apps/show.html.haml
- @app.elements.each do |element|
- element.features.each do |feature|
= feature.name
= link_to complete_feature_path(feature), method: :post
当用户完成每个功能时,他们可以单击 link,这将向功能发送 post 方法#complete action
class Feature < ActiveRecord::Base
def mark_as_complete
self.update_attributes(completion: true)
self.element.update_completion_status
end
end
class Element < ActiveRecord::Base
def update_completion_status
if (self.feautres.where(completion: true).count) == (self.features.count) # if all features are complete
self.update_attributes(completion: true)
self.update_attributes(feature_completion_status: calculate_percent_complete)
self.app.update_completion_status
else
self.update_attributes(feature_completion_status: calculate_percent_complete)
end
end
private
def calculate_percent_complete
self.features.where(completion: true).count / child.childs_childs.count.to_f
end
end
class Feature < ActiveRecord::Base
def mark_as_complete
self.update_attributes(completion: true)
self.element.update_completion_status
end
end
class App < ActiveRecord::Base
def update_completion_status
# similar to that of Element I assume
end
end
这样,仅当 POST 请求将功能标记为完成时,才会更新相应的数据。您在 apps#show 操作中应该做的就是提供要显示的适当对象。