I have an Ruby on Rails api where the data is handled in JSON. When I want to update an entity all the attributes are getting updated persistently but changed relationships arent' getting handled correctly, the entity stays the same as before.
JSON data before and after the PATCH:
{"data":{"id":"26","type":"candidate","attributes":
{"place":"Ort","zip_code":"PLZ","address":"Adresse",
"date_of_birth":"2019-01-01T00:00:00.000Z","title":"Frau",
"first_name":"Vorname","last_name":"Nachname",
"email_address":"email@example.ch",
"confirm_terms_and_conditions":true},"relationships":
{"occupational_fields":{"data":[]}}}}
PATCH input:
Started PATCH "/candidates/26" for 127.0.0.1 at 2019-01-22
19:40:53 +0100
Processing by CandidatesController#update as JSON
Parameters: {"data"=>{"id"=>"26", "attributes"=>{"place"=>"Ort",
"zip_code"=>"PLZ", "address"=>"Adresse", "title"=>"Frau",
"first_name"=>"Vorname", "last_name"=>"Nachname",
"email_address"=>"email@example.ch",
"confirm_terms_and_conditions"=>true, "date_of_birth"=>"2019-01-
01T00:00:00.000Z"}, "relationships"=>{"occupational_fields"=>
{"data"=>[{"type"=>"occupational-fields", "id"=>“4“}]}},
"type"=>"candidates"}, "id"=>"26", "candidate"=>{}}
This are my models, Candidates and OccupationalFields are related via a has_many belongs_to_many relationship through one CandidatesOccupationalField:
class Candidate < ApplicationRecord
has_many :candidates_occupational_fields, dependent: :destroy
has_many :occupational_fields, through:
:candidates_occupational_fields, dependent: :nullify
end
class CandidatesOccupationalField < ApplicationRecord
belongs_to :candidate
belongs_to :occupational_field
end
class OccupationalField < ApplicationRecord
has_many :candidates_occupational_fields, dependent: :destroy
has_many :candidates, through: :candidates_occupational_fields,
dependent: :nullify
end
This is the used controller:
class CandidatesController < ApplicationController
before_action :set_candidate, only: %i[show update destroy]
# GET /candidates
def index
@candidates = Candidate.all
render json: CandidateSerializer.new(@candidates).serialized_json
end
# GET /candidates/1
def show
@candidate = Candidate.find(params[:id])
render json: CandidateSerializer.new(@candidate).serialized_json
end
# POST /candidates
def create
@candidate = Candidate.new(candidate_params)
if @candidate.save
render json: CandidateSerializer.new(@candidate), status: :created
else
render json: @candidate.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /candidates/1
def update
@candidate = Candidate.find(params[:id])
if @candidate.update(candidate_params)
render json: CandidateSerializer.new(@candidate)
else
render status: :unprocessable_entity
end
end
# DELETE /candidates/1
def destroy
@candidate.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_candidate
@candidate = Candidate.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def candidate_params
params.require(:data)[:attributes]
.permit(:place, :zip_code, :address,
:date_of_birth, :title, :first_name,
:last_name, :email_address,
:confirm_terms_and_conditions,
occupational_field_ids: [])
end
end
The JSON formatting is handled by fastjsonapi, this are the used serializers:
class CandidateSerializer
include FastJsonapi::ObjectSerializer
attributes :place, :zip_code, :address, :date_of_birth,
:title, :first_name, :last_name, :email_address,
:confirm_terms_and_conditions
has_many :occupational_fields
end
class OccupationalFieldSerializer
include FastJsonapi::ObjectSerializer
attributes :field
has_many :candidates
end
Thank you for your help.
The problem was, that the used serializer fast_jsonapi can't be used as deserializer and the Rail's strong parameters can't handle the json input. It works with the gem restful-jsonapi and modified params as shown in the example of the readme of restful-jsonapi.