0

I'm figuring a problem with a nested_attribute.

team.rb:

class Team < ApplicationRecord
  has_many :players, dependent: :destroy
  accepts_nested_attributes_for :players, allow_destroy: true
end

console output:

Processing by TeamsController#create as JSON
  Parameters: {"team"=>{"id"=>nil, "name"=>"testes",
  "players_attributes"=>[{"id"=>nil, "name"=>"dadada", "_destroy"=>false, "team_id"=>nil}]}}
Unpermitted parameter: id

So, i'm ignoring team_id in controller for create and sending it as null same to player_id. What rails is getting in controller after permit is:

team: {name:'testes team', players_attributes: [{ name: 'testes'}]}

In my opinion (prob my mistake) rails should feed this relation in exactly this way. I tested it removing the nested attribute id and team_id but doesn't works.

Rails return:

bodyText: "{"players.team":["must exist"]}

controller:

def create
  @team = Team.create(team_params)

  @team.players.each do |player|
    player.team_id = 1
  end

  respond_to do |format|
    if @team.save
      format.html { redirect_to @team, notice: 'Team was successfully created.' }
      format.json { render :show,  status: :created, location: @team }
    else
      format.html { render :new }
      format.json { render json: @team.errors, status: :unprocessable_entity }
    end
  end
end

def team_params
  params.require(:team).permit(:name, players_attributes: [:name, :positions, :_destroy])
end

gambiarra:

@team.players.each do |player|
  player.team_id = 1
end

If i do this to the nested attribute BEFORE save team it works, team 1 must exists for it to work. If i save only the team and after create the relation it DOESN'T works either, only if I set the "gambiarra" solution.

How to solve this relation? As mentioned, my controller is filtering for only attributes for nested data. If i submit with HTML, works fine, if i use a JSON as nested objects, it doesn't work unless i force the relation to find a team_id for my player before save and so on, rails will save and commit the right player as is expected to do without a team_id in my player.

  • Do you mean `"computer must exists"`? Also, i'm not sure i understood your problem, could you please clarify what is the expected behavior? – Gerry Jun 02 '17 at 02:58
  • yup! exactly. I Was developing with team and player as classes, lol, my fault. Well, the problem is rails is denying me to add the row, returning a error that says i should have a computer before add a speaker, but this computer is new too, i was expecting that nested attributes to feed the relation. – Allan Felipe Murara Jun 02 '17 at 03:07
  • Could you post your actual code? The changes between `computer`/`team` and `player`/`speaker` are misleading. We could find issue faster that way. Also, where does `material` in `material.require(:computer).permit(speaker_attributes: [:power])` comes from? – Gerry Jun 02 '17 at 15:39
  • i was trying to better underestand the problem rewriting it, as i do in other social media as i do here, i'm sorry by that. I will rewrite with all code of all components. – Allan Felipe Murara Jun 02 '17 at 15:53
  • 1
    Found [this](https://stackoverflow.com/questions/19574595/rails-4-not-updating-nested-attributes-via-json) answer, take a look, i think that is your same problem. – Gerry Jun 02 '17 at 18:39
  • yup, Gerry, thank u ! fortunatelly rails covers it if i force the id as i said and .save creates the data so i'm not afraid about this now. besides, its learning purpose so if i got a "gambiarra" inside my system that is not a problem. i will keep my eyes on rails, i found another with reactresource, maybe its vue-resource that is causing my this problem. Thank you again! – Allan Felipe Murara Jun 02 '17 at 19:06

1 Answers1

2

The structure of the params you are sending is incorrect, rails expects something like this in order to work with nested attributes:

{
  "computer": {
    "speakers_attributes": {
      "0": {
        "power": "1"
      }
    }
  }
}

Notice three things:

  1. computer: null was removed; you don't need to specify computer attribute since its value will be set with the id of the new computer to be created.

  2. "0": was added; because of the has_many :speakers associations, you can create more than one Speaker (you will use 1: { ... }, 2: { ... }, and so on).

  3. speaker: was changed to speakers_attributes; that's the way rails recognizes nested attributes values.

Now that the parameters had been set correctly, you need to do two more things:

  1. Confirm that your associations are set correctly

    class Computer < ApplicationRecord
      has_many :speakers, dependent: :destroy
      accepts_nested_attributes_for :speakers, allow_destroy: true
    end
    
    class Speaker < ApplicationRecord
      belongs_to :computer
    end
    
  2. Properly setup your controller

    class ComputersController < ApplicationController
      def new
        @computer = Computer.new
        @computer.speakers.build
      end
    
      def create
        @computer = Computer.create(computer_params)
    
        if @computer.save
          # handle success
        else
          # handle error
        end
      end
    
      # other actions
    
      private
      def computer_params
        params.require(:computer).permit(speakers_attributes: [:power])
      end
    end
    

Here @computer.speakers.build is neessary only if you will be creating nested forms using form helpers.

Gerry
  • 10,337
  • 3
  • 31
  • 40
  • Actually, i'm creating nested forms, but my fault again, i was using the new json i edited the post, sorry man! Thank u anyway. But that doesn't seems to help, cause you are telling me how to deal with by activerecord. as i mentioned, my record is created since i set a VALID DATA. As i previous post too, my model and everythings seems to be fine. For numer 2, rails doesn't need 0: you can change it by [ or it will not be a JSON it will be a RAILSON ;D The thing is : If i use a rails button OK works fine, if i use a JSON variable with VUE by webpacker, does not – Allan Felipe Murara Jun 02 '17 at 13:15
  • My record is created since i set a VALID DATA to @computer.player.each do player player.id... ( the awser was uncomplete ) – Allan Felipe Murara Jun 02 '17 at 13:28
  • @AllanFelipeMurara Yes you can create it that way, but you need to add it yourself (which is ok, if your input asks for it); this answer aims to respond to "i was expecting that nested attributes to feed the relation"., and i think it does (yes, using `0:` or `[..]`). Anyway, it seems that your problem has been solved, good luck! :) – Gerry Jun 02 '17 at 14:13
  • but the thing is, i'm using [] as the enumerator for json. the object is beeing provide correctly as i updated my awnser. As i said, the problem is feeding the Computer id in the nested Speaker. As i said in my question too, i'm being able to add the info with html form and i'm being able with JSON as long as i add an existing id to a player.team_id . so i'm kind of facing a problem in 5.1 + Nested Attributes with JSON, and now i reminded maybe could be the field type in mysql. i will try to change it to json. – Allan Felipe Murara Jun 02 '17 at 15:05
  • @AllanFelipeMurara Ok, I get it, looking at your updated question, you are still using `computer_id: nil` (i assume its `computer` instead of `team`), so you will get the error because `nil` is not a valid `computer_id` (does not exists). Try doing the same without `id: nil` and `computer_id: nil` and it should work. – Gerry Jun 02 '17 at 15:09
  • Not =/ unfortunatelly. If i set a valid value for computer_id inside of speaker (speaker.computer.each) it will work, cause raills will find a Valid Active Record Relation, and when .save occurs it will change the computer_id, inside of each player to the new computer_id. player.computer_id = 1; player.computer_id = computer.save.id; as should be with or without id = nil as should be as HTML or as JSON. Version 5.1 the model is in my question, the data i'm accepting in my controller is : `material.require(:computer).permit(speaker_attributes: [:power])` so it does create a valid "params" – Allan Felipe Murara Jun 02 '17 at 15:28
  • @AllanFelipeMurara Check updated answer, please try doing it that way and tell me if you keep getting any error. – Gerry Jun 02 '17 at 17:25
  • Tried and keep hiting same error. As i said in text body, even dont passing it to rails doesn't works. If i use a Form_for submited form it works. it doesn't work ONLY with JSON post! – Allan Felipe Murara Jun 02 '17 at 17:49
  • @AllanFelipeMurara Sorry, i am unable to reproduce your error, tried with both html and json and both worked as expected. I'm not sure what else could be causing the error. One last thing: Could you show your `Player` model? – Gerry Jun 02 '17 at 18:30
  • `class Player < ApplicationRecord belongs_to :team end` – Allan Felipe Murara Jun 02 '17 at 18:59