简体   繁体   English

Rails具有许多直通关系-新保存和创建之间的区别

[英]Rails Has Many Through Relationship - Difference Between New-Save and Create

I checked the Rails Guides about has_many through relationship but they have a poor documentation for has_many through's effects in controllers in views. 我检查了有关has_many through关系的Rails指南,但是对于has_many through在视图中的控制器中的效果,它们的文档很差。

The models: 型号:

class Project < ActiveRecord::Base
  has_many :twitter_memberships
  has_many :twitter_accounts, through: :twitter_memberships
end

class TwitterAccount < ActiveRecord::Base
  has_many :twitter_memberships
  has_many :projects, through: :twitter_memberships
end

class TwitterMembership < ActiveRecord::Base
  belongs_to :project
  belongs_to :twitter_account
end

Routes: 路线:

resources :projects do
  resources :twitter_accounts
end

The question is, when I use the create method , a TwitterMembership object is being created automatically but when I use new method and then the save method , TwitterMembership is not being created. 问题是,当我使用create方法时 ,会自动创建一个TwitterMembership对象,但是当我使用new方法然后再使用save方法时 ,不会创建TwitterMembership。 Many of the posts in Stackoverflow is saying that create = new & save but it seems that they are not the same. Stackoverflow中的许多帖子都说create = new&save,但似乎它们并不相同。

For example: 例如:

Project.first.twitter_accounts.create(name: "bar")

is creating both TwitterAccount and TwitterMembership: 正在创建TwitterAccount和TwitterMembership:

SQL (0.5ms)  INSERT INTO "twitter_accounts" ("created_at", "name", "updated_at") VALUES (?, ?, ?)  [["created_at", Thu, 20 Nov 2014 22:01:06 UTC +00:00], ["name", "test_record"], ["updated_at", Thu, 20 Nov 2014 22:01:06 UTC +00:00]]

SQL (0.3ms)  INSERT INTO "twitter_memberships" ("created_at", "project_id", "twitter_account_id", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Thu, 20 Nov 2014 22:01:06 UTC +00:00], ["project_id", 1], ["twitter_account_id", 8], ["updated_at", Thu, 20 Nov 2014 22:01:06 UTC +00:00]]

But when I do the same thing in the controllers/twitter_accounts_controller with new & create - it's not creating the TwitterMembership: 但是,当我使用new&createcontrollers / twitter_accounts_controller中执行相同的操作时,它不是在创建TwitterMembership:

def new
  @twitter_account = @project.twitter_accounts.new
end

def create
  @twitter_account = @project.twitter_accounts.new(twitter_account_params)
  @twitter_account.save
end

Can you explain me the difference between new-save and create? 您能解释一下新保存和创建之间的区别吗? And is it okay if I use create method directly in my controller like this? 这样直接在控制器中使用create方法可以吗?

def create
  @twitter_account = @project.twitter_accounts.create(twitter_account_params)
end

Thanks in advance. 提前致谢。

Edit. 编辑。 Here is the full controller => 这是完整的控制器=>

class TwitterAccountsController < ApplicationController
  before_action :set_project
  before_action :set_twitter_account, only: [:show, :edit, :update, :destroy]

  def new
    @twitter_account = @project.twitter_accounts.new
  end

  def create
    @twitter_account = @project.twitter_accounts.new(twitter_account_params)
    @twitter_account.save
  end

  private
    def set_project
      @project = Project.friendly.find(params[:project_id])
    end

    def set_twitter_account
      @twitter_account = TwitterAccount.friendly.find(params[:id])
    end

    def twitter_account_params
      params.require(:twitter_account).permit(:name, :account_id)
    end
end

The Fix 修复

You need to set your inverse relationships on your models to guarantee the build and new on associations will work consistently to setup the relationships, foreign keys, and intermediate associations: 您需要在模型上设置逆向关系,以确保buildnew的关联将一致地建立关系,外键和中间关联:

class Project < ActiveRecord::Base
  has_many :twitter_memberships, inverse_of: :project

  has_many :twitter_accounts, through: :twitter_memberships
end

class TwitterMembership < ActiveRecord::Base
  belongs_to :project,         inverse_of: :twitter_memberships
  belongs_to :twitter_account, inverse_of: :twitter_memberships
end

class TwitterAccount < ActiveRecord::Base
  has_many :twitter_memberships, inverse_of: :twitter_account

  has_many :projects, through: :twitter_memberships
end

Which should then make this work 那应该使这项工作

@twitter_account = Project.first.twitter_accounts.new(name: 'baz')
@twitter_account.save

And voila: 瞧:

SQL (0.3ms)  INSERT INTO "twitter_accounts" ("created_at", "name", "updated_at") VALUES (?, ?, ?)  [["created_at", "2014-11-21 19:19:06.323072"], ["name", "baz"], ["updated_at", "2014-11-21 19:19:06.323072"]]
SQL (0.1ms)  INSERT INTO "twitter_memberships" ("created_at", "project_id", "twitter_account_id", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", "2014-11-21 19:19:06.324779"], ["project_id", 1], ["twitter_account_id", 7], ["updated_at", "2014-11-21 19:19:06.324779"]]

Also, using create is perfectly acceptable assuming you've handled all your edge cases in the event of a failure. 此外,假设您已经处理了所有发生故障的极端情况,那么使用create是完全可以接受的。 For example: if it fails do you show an error to the end user? 例如:如果失败,您是否向最终用户显示错误? If it is not supposed to fail, you should raise an exception (see create! ) to generate a notification of some sort to inform you of the failure. 如果不应该失败,则应引发异常(请参阅create! ),以生成某种通知以通知您失败。

The Why 为什么

In short, without the inverse_of setup on has_many , has_one , and belongs_to Rails doesn't completely understand the chain of intermediate models that glue things together. 简而言之,如果没有在has_manyhas_onebelongs_to上的inverse_of设置,Rails不会完全理解将事物粘合在一起的中间模型链。 Rails will in certain cases (like create ) take a "best guess", and may get things right. 在某些情况下(例如create ),Rails会做出“最佳猜测”,并可能使事情变得正确。 Rails Guide doesn't make this clear, but the documentation on inverse_of spells this out. Rails指南并不清楚,但是有关inverse_of的文档对此进行了说明。

It has become a Rails idiom to set inverse_of on all has_many , has_one , and belongs_to relationships, and should be done to ensure guessing by Rails is not necessary. 在所有has_manyhas_onebelongs_to关系上设置inverse_of已成为Rails惯用语,应确保不要使用Rails进行猜测。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM