【问题标题】:How to save a nested many-to-many relationship in API-only Rails?如何在仅 API 的 Rails 中保存嵌套的多对多关系?
【发布时间】:2021-02-14 00:49:10
【问题描述】:

在我的 Rails(仅限 api)学习项目中,我有 2 个模型,Group 和 Artist,它们与加入模型 Role 具有多对多关系,其中包含有关该关系的附加信息。我之前可以通过自己保存连接模型来保存 m2m 关系,但在这里我试图将关系保存为嵌套关系。我正在使用jsonapi-serializer gem,但没有与之结婚,也没有绑定到 JSON api 规范。让它发挥作用比遵循最佳实践更重要。

使用此设置,我在尝试保存时收到 500 错误,并出现以下错误: Unpermitted parameters: :artists, :albumsActiveModel::UnknownAttributeError (unknown attribute 'relationships' for Group.)

我怀疑我的问题在于强大的参数和/或 json 有效负载。

模型

class Group < ApplicationRecord
  has_many :roles
  has_many :artists, through: :roles

  accepts_nested_attributes_for :artists, :roles
end


class Artist < ApplicationRecord
  has_many :groups, through: :roles
end


class Role < ApplicationRecord
  belongs_to :artist
  belongs_to :group
end

控制器#create

def create
  group = Group.new(group_params)

  if group.save
    render json: GroupSerializer.new(group).serializable_hash
  else
    render json: { error: group.errors.messages }, status: 422
  end
end

控制器#group_params

def group_params
  params.require(:data)
    .permit(attributes: [:name, :notes],
      relationships: [:artists])
end

序列化器

class GroupSerializer
  include JSONAPI::Serializer
  attributes :name, :notes

  has_many :artists
  has_many :roles
end


class ArtistSerializer
  include JSONAPI::Serializer
  attributes :first_name, :last_name, :notes
end


class RoleSerializer
  include JSONAPI::Serializer
  attributes :artist_id, :group_id, :instruments
end

示例 JSON 负载

{
  "data": {
    "attributes": {
      "name": "Pink Floyd",
      "notes": "",
    },
    "relationships": {
      "artists": [{ type: "artist", "id": 3445 }, { type: "artist", "id": 3447 }]
    }
}

其他信息

知道我能够使用以下 json 和强参数组合保存另一个模型可能会有所帮助。

# Example JSON

"data": {
  "attributes": {
    "title": "Wish You Were Here",
    "release_date": "1975-09-15",
    "release_date_accuracy": 1
    "notes": "",
    "group_id": 3455
  }
}


# in albums_controller.rb 

def album_params
  params.require(:data).require(:attributes)
    .permit(:title, :group_id, :release_date, :release_date_accuracy, :notes)
end

【问题讨论】:

    标签: ruby-on-rails json-api jsonapi-resources fastjsonapi jsonapi-serialize


    【解决方案1】:

    通过查看https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html,我认为 Rails 通常期望的数据格式如下所示:

    {
      "group": {
        "name": "Pink Floyd",
        "notes": "",
        "roles_attributes": [
          { "artist_id": 3445 },
          { "artist_id": 3447 }
        ]
      }
    }
    

    使用类似于以下内容的许可语句(注意 . 之前的许可已移动):

    params.require(:group).
        permit(:name, :notes, roles_attributes: [:artist_id])
    

    我认为您在这里有几个选择:

    1. 更改要执行的数据格式。
    2. 制作一个适用于您当前数据的许可语句(不确定这有多棘手),您可以在控制台中测试您的当前版本:
    params = ActionController::Parameters.new({
      "data": {
        "attributes": {
          "name": "Pink Floyd",
          "notes": "",
        },
        "relationships": {
          "artists": [{ type: "artist", "id": 3445 }, { type: "artist", "id": 3447 }]
        }
      }
    })
    
    group_params = params.require(:data).
        permit(attributes: [:name, :notes],
          relationships: [:artists])
    
    group_params.to_h.inspect
    

    然后将数据重组为模型可以接受的形式;或

    1. 在尝试允许之前重组数据,例如类似:
    def group_params
        params_hash = params.to_unsafe_h
    
        new_params_hash = {
          "group": params_hash["data"]["attributes"].merge({
            "roles_attributes": params_hash["data"]["relationships"]["artists"].
                map { |a| { "artist_id": a["id"] } }
          })
        }
    
        new_params = ActionController::Parameters.new(new_params_hash)
    
        new_params.require(:group).
            permit(:name, :notes, roles_attributes: [:artist_id])
    end
    

    但是......我有点希望我完全错了,其他人会提出更好的解决方案。

    【讨论】:

      猜你喜欢
      • 2021-02-14
      • 2018-01-09
      • 1970-01-01
      • 2019-04-25
      • 1970-01-01
      • 2015-02-27
      • 1970-01-01
      • 2019-01-24
      • 1970-01-01
      相关资源
      最近更新 更多