Rails嵌套表单插入数据库时字段为nil但created_at/updated_at正常的问题
嘿,我来帮你捋捋这个问题——你说提交表单后,documentations的字段都是nil,但created_at和updated_at正常,这说明模型实例确实被创建了,只是表单里的内容没传对地方。结合你用了嵌套控制器、friendly_id和haml的情况,咱们一步步排查:
先看表单的参数结构(HAML代码)
首先,嵌套资源的表单得正确关联父子模型,不然参数会传错位置。比如你的路由应该是这样的对吧?
resources :categories do resources :documentations end
那表单得用form_with(model: [@category, @documentation])来生成,这样输入框的name属性才会是documentation[title]这种嵌套结构,参数才能被控制器正确识别。
要是你写HAML的时候图省事,直接手动写了input的name,比如:
%input{type: "text", name: "title"}
那参数里的title是顶级键,不在documentation下面,后面强参数过滤的时候就会被丢掉,自然存成nil。正确的写法应该是用表单辅助方法:
= form_with(model: [@category, @documentation]) do |f| .field = f.label :title = f.text_field :title .field = f.label :content = f.text_area :content .actions = f.submit "Create Documentation"
检查控制器的强参数(Strong Params)
这是最常见的坑!你得确保DocumentationsController里的documentation_params允许了表单提交的所有字段。比如:
def create @category = Category.friendly.find(params[:category_id]) # 用friendly_id找分类是对的 @documentation = @category.documentations.build(documentation_params) if @documentation.save redirect_to [@category, @documentation], notice: 'Documentation created!' else render :new end end private def documentation_params # 这里要把你表单里的所有字段都列出来,比如title、content这些 params.require(:documentation).permit(:title, :content) end
要是你漏写了某个字段,或者字段名写错了,那这个字段的参数就会被过滤掉,存到数据库就是nil。另外,你可以看服务器日志里的POST请求,要是有Unpermitted parameter: xxx的提示,直接把这个字段加到permit里就行。
确认数据库表的字段是否存在
别笑,有时候真会忘!你得检查documentations的迁移文件,确保你需要的字段(比如title、content)都已经创建了,比如:
class CreateDocumentations < ActiveRecord::Migration[6.1] def change create_table :documentations do |t| t.string :title t.text :content t.references :category, null: false, foreign_key: true t.timestamps end end end
要是迁移里没加某个字段,那即使参数传对了,数据库也没地方存,自然是nil。
最后看看服务器的POST日志
你之前给的是GET请求的日志,得找POST请求的那段,比如:
Started POST "/categories/ruby_on_rails/documentations" at 2024-05-20 12:34:56 +0800
Processing by DocumentationsController#create as HTML
Parameters: {"authenticity_token"=>"[FILTERED]", "documentation"=>{"title"=>"My Guide", "content"=>"Some text"}, "category_id"=>"ruby_on_rails"}
Category Load (0.1ms) SELECT "categories".* FROM "categories" WHERE "categories"."slug" = ? LIMIT ? [["slug", "ruby_on_rails"], ["LIMIT", 1]]
这里能直接看到参数是不是正确嵌套在documentation下面,有没有你需要的字段值。如果参数里的字段都是nil,那就是表单生成的输入框有问题;如果参数有值但存成nil,那就是强参数或者模型的问题。
几个容易忽略的小坑
- 字段名不一致:比如模型里是
heading,但表单里用了title,参数传过来模型认不出,直接忽略; - 不小心加了
attr_accessor:要是你在Documentation模型里写了attr_accessor :title,那ActiveRecord会用这个内存属性,不会存到数据库里; - 路由不对:跑一遍
rails routes,确认有category_documentations POST /categories/:category_id/documentations(.:format) documentations#create这条路由,别让表单提交到错误的地址。
内容的提问来源于stack exchange,提问作者Cédric Seyer

