ruby-on-rails - 如果模型在深层命名空间中,set_table_name 不起作用 - Rails 2.3.14 中的错误?

标签 ruby-on-rails ruby activerecord

我正在使用 Rails 2.3.14

UPDATE 3 我在更新 2 中发现的技巧仅适用于关联的首次访问...因此,我将 set_table_name 行粘贴到我的应用程序 Controller 中。这太奇怪了。我希望这得到解决。 =\

更新 2 如果我在我的环境文件底部为麻烦的类手动/恶意/hackily 设置表格,我将不再收到错误。

应用程序/配置/环境.rb:

Page::Template::Content.set_table_name "template_page_contents"
Page::Template::Section.set_table_name "template_page_sections"

为什么我必须这样做?这个特定用例的 rails 是否损坏?

更新:最初在我的 Page::Template::Content 类中调用 set_table_name 时,它​​似乎不起作用。

但是如果我在使用关联之前调用它,它会起作用...

ap Page::Template::Content.table_name # prints "contents"
Page::Template::Content.set_table_name "template_page_contents"
ap Page::Template::Content.table_name # prints "template_page_contents"

return self.page_template_contents.first # doesn't error after the above is exec'd

原始问题:

TL; DR:我尝试访问一个 has_many 关系,但 Rails 认为 has_many 关系使用的表是不同的表

我遇到了这个错误,当我尝试通过 访问属于 Page::TemplatePage::Template::Content 时>has_many 关系。

Mysql2::Error: Unknown column 'contents.template_page_id' in 'where clause': SELECT * FROM `contents` WHERE (`contents`.template_page_id = 17)  LIMIT 1

查看错误日志,我想我需要开始使用一些打印语句来找出为什么 Rails 试图在错误的表中查找关联对象。

gems_path/activerecord-2.3.14/lib/active_record/associations/association_collection.rb:63:in `find'

我刚刚决定打印 @reflection 对象,因为一切似乎都围绕着它发生。这是我的做法:

  require "awesome_print" # best console printer (colors, pretty print, etc)
  def find(*args) # preexisting method header
     ap "-------" # separator so I know when one reflection begins / ends, etc
     ap @reflection # object in question
     ... # rest of the method

在错误之前打印的最后一个“@reflection”:

"-------"
#<ActiveRecord::Reflection::AssociationReflection:0x108d028a8
    @collection = true,
    attr_reader :active_record = class Page::Template < LibraryItem {...},
    attr_reader :class_name = "Page::Template::Content",
    attr_reader :klass = class Content < LibraryItem {...},
    attr_reader :macro = :has_many,
    attr_reader :name = :page_template_contents,
    attr_reader :options = {
        :foreign_key => "template_page_id",
         :class_name => "Page::Template::Content",
             :extend => []
    },
    attr_reader :primary_key_name = "template_page_id",
    attr_reader :quoted_table_name = "`contents`"
>

上面的代码块中有几处错误。

:klass should be Page::Template::Content
:name should be :contents  
:quoted_table_name should be `contents`

我的模型是如何设置的:

应用/模型/page.rb:

class Page < LibrayItem
  belongs_to :template_page, :class_name => "Page::Template"

app/models/page/template.rb

class Page::Template < Library Item
  set_table_name "template_pages"
  has_many :page_template_contents,
    :class_name => "Page::Template::Content",
    :foreign_key => "template_page_id"

app/models/page/template/content.rb

class Page::Template::Content
  set_table_name "template_page_contents"
  belongs_to :template_page,
    :class_name => "Page::Template",
    :foreign_key => "template_page_id"



class Page::Template
...
    return self.page_template_contents.first

协会选择的类(与我上面的页面/模板结构无关):

class Content < LibraryItem
  set_table_name "contents"
# no associations to above classes

那么...是什么原因造成的,我该如何解决?

最佳答案

您的问题不是由于 set_table_name 而是由于 Rails 如何在关联中找到目标模型类以及您拥有顶级模型这一事实(内容>) 与嵌套在更深命名空间 (Page::Template::Content) 中的模型共享其名称。奇怪的行为差异很可能是由于在检查关联时实际加载的类存在差异(请记住,默认情况下,在开发模式下,Rails 在首次引用时按需加载模型类)。

当你定义一个关联时(无论你是像你所做的那样明确指定关联的目标模型的类名还是接受默认值),Rails 都必须将目标模型类的名称转换为 Ruby 常量,以便它可以引用该目标类。为此,它在定义关联的模型类上调用 protected 类方法 compute_type

例如,你有

class Page::Template < LibraryItem
  set_table_name "template_pages"
  has_many :page_template_contents,
    :class_name => "Page::Template::Content",
    :foreign_key => "template_page_id"
 end

您可以在 Rails 控制台中测试 compute_type 如何为此类工作:

$ ./script/console 
Loading development environment (Rails 2.3.14)
> Page::Template.send(:compute_type, "Page::Template::Content")
=> Page::Template::Content(id: integer, template_page_id: integer)

正如我所料,我看到结果是对类 Page::Template::Content 的引用。

但是,如果我重新启动 Rails 控制台,但这次导致模型类 Content 首先加载(通过引用它)然后再试一次,我看到这个(我没有费心创建Content 模型的表格,但这不会改变重要的行为):

$ ./script/console 
Loading development environment (Rails 2.3.14)
>> Content # Reference Content class so that it gets loaded
=> Content(Table doesn't exist)
>> Page::Template.send(:compute_type, "Page::Template::Content")
=> Content(Table doesn't exist)

如您所见,这次我得到的是对内容的引用。

那么你能做些什么呢?

嗯,首先,您应该意识到在 Rails 中(当然是在 2.3 中)使用命名空间模型类是一件非常痛苦的事情。在层次结构中组织模型类很好,但它确实会让生活变得更加困难。正如您在上面看到的,如果您在一个命名空间中有一个类与另一个命名空间中的类具有相同的名称,这将变得更加棘手。

如果您仍然想忍受这种情况,我可以提出一些建议。以下其中一项可能会有所帮助:

  1. 在开发模式下开启类缓存。这将导致预加载所有模型类,而不是默认情况下按需加载它们。它会使您上面的代码更可预测,但可能会使开发有些不愉快(因为类将不再随每个请求重新加载)。

  2. 明确要求关联有问题的类中的依赖类。例如:

    class Page::Template < LibraryItem        
      require 'page/template/content'
      set_table_name "template_pages"
      has_many :page_template_contents, ...
    end
    
  3. 在您知道引用关联类可能出错的类中覆盖 compute_type 方法,以便它无论如何都返回命名空间类。在不更详细地了解您的类层次结构的情况下,我无法就如何执行此操作给出完整的建议,但下面是一个快速而肮脏的示例:

    class Page::Template < LibraryItem
      set_table_name "template_pages"
      has_many :page_template_contents,
        :class_name => "Page::Template::Content",
        :foreign_key => "template_page_id"
    
      def self.compute_type(type_name)
        return Page::Template::Content if type_name == "Page::Template::Content"
        super
      end
    end
    

关于ruby-on-rails - 如果模型在深层命名空间中,set_table_name 不起作用 - Rails 2.3.14 中的错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11752151/

相关文章:

ruby-on-rails - Ruby 2.2 在 Heroku 上有内存问题吗?

ruby-on-rails - 如何在 Windows 上的一个地方运行 Ruby 和 GIT 命令

ruby - 在 Ruby 中如何将参数传递给 class << self ?

ruby-on-rails - 将原始 SQL 查询的结果转换为 JSON(和 CSV)

mysql - Rails 4 应用程序进入 Bluehost 的问题

ruby-on-rails - Rspec request.env 参数数量错误

ruby - 如何在 Ruby 中将字符串中的第一个字母大写

ruby-on-rails - 如何在回形针 ruby​​ on rails 中设置默认图像

ruby-on-rails-3 - rails i18n accepts_nested_attributes_for how-to translate?

ruby-on-rails - 不使用主键选项的belongs_to