如何在仅API的Rails中保存嵌套的多对多关系?

问题描述

在我的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

Controller#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

Controller#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,:release_date,:release_date_accuracy,:notes)
end

解决方法

通过查看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",},"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,roles_attributes: [:artist_id])
end

但是...我很希望自己完全错了,其他人会对此提供更好的解决方案。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...