2

我正在使用 Rails 2.3.14

更新 3我在更新 2 中发现的技巧仅适用于关联的第一次访问......所以,我将 set_table_name 行粘贴在我的应用程序控制器中。这太奇怪了。我希望这得到修复。=\

更新 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::Template::Contentthat belongs_to a时。Page::Templatehas_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"

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

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

4

1 回答 1

6

您的问题不是由于set_table_name而是由于 Rails 如何在关联中找到目标模型类以及您有一个顶级模型(Content)与嵌套在更深的命名空间(Page: :模板::内容)。奇怪的行为差异很可能是由于在检查关联时实际加载的类的差异(请记住,默认情况下,在开发模式下,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 控制台但这次导致模型类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)

如您所见,这次我获得了对Content的引用。

那么你能做些什么呢?

嗯,首先,你应该意识到在 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
    
于 2012-08-08T04:17:16.467 回答