我在Ruby,Rails和SQL(以及一般的编程)中仍然非常环保,但我知道基础知识。这是我的小谜题涉及酒窖组织应用程序:
我有模特。葡萄酒 - 具有属性:名称和:葡萄酒,葡萄酒 - 葡萄酒所属,用户 - 拥有许多葡萄酒,位置 - 葡萄酒的酒窖位置,这是一个字符串属性:名称和瓶子 - 每个瓶子都有用户记录,葡萄酒和位置ID。这是我对模型,模式,控制器和我想要使用的视图的要点: https://gist.github.com/mpron/8725840
这就是我想要做的事情:
到目前为止,这是我粗略的,稍微伪代码的解决方案:
- if @bottles.count > 0
You have
= @bottles.count
bottles
%table.table
%tr
%th Location
%th Vintage
%th Name
%th Winery
%th Quantity
%th Actions
- @wines.each do |wine|
- @locations each do |loc|
- if wine.@bottles.each exists in loc
%tr
%td
= "#{loc}"
%td
= "#{wine.vintage}"
%td
= "#{wine.name}"
%td
= "#{wine.winery.name}"
%td
= "#{loc.bottles.where(name: wine.name).count}"
%td
= link_to "Delete", bottle_path(bottle), :method => "delete", :data => {:confirm => "Are you sure?"}, :class => "btn btn-danger btn-sm"
这也在gist以及其他相关文件中 我的问题是: 1.有效查询数据库的最佳方法是什么? 2.这属于视图,还是应该存在于其他地方,也许是部分?
如果有更有效的方式对此进行建模,我愿意对此进行大幅改进。我只是不知道足够的SQL来弄清楚如何使用GROUP BY或JOIN之类的东西来使这个工作得很好。一位朋友提出类似Location.joins(:bottles).where(name: wine.name)
之类的内容,但后来又认定可能导致表现不佳。
(这是个人项目,不是家庭作业或任何东西。我只是难倒。)
答案 0 :(得分:1)
我对你的问题的初步答案是与更好的关联。重构您的关联结构。例如,删除belongs_to :user
与瓶子的关联:
class Bottle < ActiveRecord::Base
belongs_to :wine
# belongs_to :user
belongs_to :location
end
在葡萄酒模型中添加belongs_to
:
class Wine < ActiveRecord::Base
belongs_to :user
belongs_to :winery
has_many :bottles
#has_many :locations
end
并向用户添加has_many through:
:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :wines
has_many :bottles, through :wines
end
您必须相应地调整列,以摆脱不需要的外键,并添加您需要的任何新键。您不需要has_many through
的显式键,因为Rails会为您完成工作。 user -> wine -> bottles
将此视为一个不错的例子:Getting count by drilling down through relations
这将使您能够拉动用户:
@user = User.first
您将自动获得可以通过以下方法访问的联接:
my_wines = @user.wines # wines in a user's cellar
my_bottles = @user.bottles # bottles in a user's cellar
这些都返回表示AR对象数组的ActiveRecord关系。然后,您可以访问这些对象,如:
my_bottles.each do |bottle|
puts bottle.name
puts bottle.location
end
我实际上需要了解有关使用.joins
方法的更多信息,因为我很早就学会了has_many through
我甚至没有意识到它正在为我做加入。至少在has_many through
上做一些教程,你可以在模型和辅助方法上找到任何东西。我在评论中发布的CodeSchool.com链接是一个巨大的帮助。一个月25美元但值得每分钱。
.joins
适用于需要将信息放在不具有您定义的显式关联的单独表中的情况。或者当您想从两个表中提取信息时,只需要提取与某个用户相关的信息。为了性能,我会注意在循环内做任何类型的SQL请求。我没有在这里看到任何东西,但只是一个抬头。
通过删除所有嵌套循环并在关联和辅助方法中布置逻辑,您的视图将更加清晰。可以这样想,视图中的三重嵌套循环基本上是一个分层对象。您应该利用您的关联并构建一些封装该逻辑的方法,这样您在视图中只有一个.each
语句。重构您的关联,如果您仍然没有看到更简单的方法来封装您的视图逻辑,请告诉我,我可以给出更多指导。
答案 1 :(得分:0)
wine.rb
class Wine < ActiveRecord::Base
belongs_to :winery
has_many :bottles
has_many :locations, through: :bottles
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :wines, through: :bottles
has_many :bottles
end
location.rb
class Location < ActiveRecord::Base
has_many :bottles
has_many :wines, through: :bottles
end
bottle.rb
class Bottle < ActiveRecord::Base
belongs_to :wine
belongs_to :user
belongs_to :location
end
users_controller.rb的部分
def show
@user_wines = @user.wines.order('wines.name')
@bottles = @user.bottles
end
show.html.haml
%table.table
%tr
%th Location
%th Vintage
%th Name
%th Winery
%th Quantity
%th Actions
- @user_wines.each do |wine|
- wine.locations.each do |loc|
%tr
%td= loc
%td= wine.vintage
%td= wine.name
%td= wine.winery.name
%td= loc.bottles.where(:wine_id => wine.id).count
这将贯穿数据库中葡萄酒的一个更窄的子集,以及数据库中更小的位置子集,这样我的嵌套循环就不会查询那么多。