如果model在深名称空间中,set_table_name不起作用 - 在rails 2.3.14中的错误?

问题描述:

我使用Rails 2.3.14如果model在深名称空间中,set_table_name不起作用 - 在rails 2.3.14中的错误?

更新3我在更新2中招只适用于该协会的第一次访问......所以,我坚持我的应用程序控制器的set_table_name线。这太奇怪了。我希望这得到修复。 = \

UPDATE 2如果我手动/ nastily/hackily为环境文件底部的麻烦类别设置表格,我不会收到错误。

应用程序/配置/ environment.rb中:

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

为什么我必须这样做?对于这个特殊的用例,轨道是否被打破?

UPDATE:看起来set_table_name在我的Page :: Template :: Content类中最初被调用时不起作用。

,但如果我把它之前,我使用的关联,它的工作原理...

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::Template::Content通过has_many关系属于Page::Template

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

望着错误日志,我想我需要使用一些打印语句,找出原因轨试图找到错误的表关联的对象开始。

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" 

应用程序/模型/页/ 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" 

应用/模型/网页/模板/内容。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常量,它可以指向那个目标类。为此,它调用正在定义关联的模型类上的受保护类方法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控制台,但这次导致模型类内容先被加载(通过引用它),然后再试一次,我看到了这一点(我没有打扰创建一个表为内容模式,但这并不改变显著行为):

$ ./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 
    
+0

这是很透彻!谢谢! = d – NullVoxPopuli 2012-08-12 18:13:21