Rails: Saving an object into DB

I asked this in the Rails mailing list but nobody answered me, I think it is a newbie (I am a newbie) error but
I don't see the light.

In a view I have a link to a controller's action (TeamController#add_player) that calls to Player.create_player method, that initialize a player object then try to save it, but when it calls to save method an error happens:

NoMethodError in TeamController#add_player

You have a nil object when you didn't expect it!
The error occured while evaluating nil.keys

/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1557:in
`attribute_names'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:2060:in
`clone_attributes'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1519:in
`attributes'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1945:in
`attributes_with_quotes'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1725:in
`update_without_lock'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/locking.rb:33:in
`update_without_callbacks'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/callbacks.rb:274:in
`update_without_timestamps'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/timestamp.rb:39:in
`update'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1718:in
`create_or_update_without_callbacks'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/callbacks.rb:249:in
`create_or_update'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1392:in
`save_without_validation'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/validations.rb:724:in
`save_without_transactions'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/transactions.rb:126:in
`save'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/connection_adapters/abstract/database_statements.rb:51:in
`transaction'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/transactions.rb:91:in
`transaction'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/transactions.rb:118:in
`transaction'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/transactions.rb:126:in
`save'
#{RAILS_ROOT}/app/models/player.rb:15:in `create_player'
app/controllers/team_controller.rb:53:in `add_player'

And my Player model is:

class Player < ActiveRecord::Base
   belongs_to :team

   # Crea un jugador con los valores por defecto
   def Player.create_player(team_id)
     player = Player.new
     player.relate_to_team(team_id)
     player.save <-------------------------------- LINE 15
     return player
   end

   # Relaciona el jugador con un equipo
   def relate_to_team(team_id)
     @team_id = team_id
   end
end

What means that error? Anybody knows what I'm doing wrong?

And my Player model is:

class Player < ActiveRecord::Base
  belongs_to :team

  # Crea un jugador con los valores por defecto
  def Player.create_player(team_id)
    player = Player.new
    player.relate_to_team(team_id)
    player.save <-------------------------------- LINE 15

try @player.save

···

--
"In vain you tell me that Artificial Government is good, but that I fall out with the Abuse. The Thing! The Thing itself is the Abuse!" - Edmund Burke

Eduardo Yáñez Parareda wrote:

   def relate_to_team(team_id)
     @team_id = team_id
   end

This won't work. Try "self.team_id = team_id" if you want to do this.
team_id is not an instance variable, it's a special field managed by
ActiveRecord. ActiveRecord also provides read_attribute(:my_attr) and
write_attribute(:my_attr, a_value) if you can't call its field accessor
functions or want to overwrite them.

May I also suggest just using:

Player.create :team => the_team

There's no need to write many small functions like this when their
equivalents already exist.

Thanks to all for the answers, finally I've got it working fine

try @player.save

I've changed the code to:

   def Player.create_player(team_id)
     @player = Player.new
     @player.relate_to_team(team_id)
     @player.save
     return @player
   end

but get the same error.

Why do you think that would work? @player isn't defined.

And for the original question. I have no idea if this will work, but

Firstly, what's with relate_to_team? Just use team_id =, ActiveRecord
does tricky things in the background, so always use the accessors.

Secondly, just so you know, you don't need the return on the last line
- the result of the last evaluated expression is always returned, so
for example:
def two_plus_one
  2+1
end

x = two_plus_one
=> x = 3

class Player < ActiveRecord::Base
  belongs_to :team

  def Player.create_player(team_id)
    player = Player.new
    player.team_id = team_id
    player.save
    player # The last evaluated line in a Ruby method is automatically returned
  end
end

···

On 7/19/06, Kev Jackson <foamdino@gmail.com> wrote:

>
> And my Player model is:
>
> class Player < ActiveRecord::Base
> belongs_to :team
>
> # Crea un jugador con los valores por defecto
> def Player.create_player(team_id)
> player = Player.new
> player.relate_to_team(team_id)
> player.save <-------------------------------- LINE 15
>

try @player.save

from my experience with ActiveRecord it might help.
--
Phillip Hutchings
http://www.sitharus.com/

After some changes, same error. I don't understand anything, I have a model class Player, a table players and the only thing I want to do is create a new player and save it in the database. I have another models and when I save them
through a view generated by Rails it works fine.

/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1557:in `attribute_names'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:2060:in `clone_attributes'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1519:in `attributes'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1945:in `attributes_with_quotes'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1725:in `update_without_lock'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/locking.rb:33:in `update_without_callbacks'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/callbacks.rb:274:in `update_without_timestamps'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/timestamp.rb:39:in `update'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1718:in `create_or_update_without_callbacks'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/callbacks.rb:249:in `create_or_update'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/base.rb:1392:in `save_without_validation'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/validations.rb:724:in `save_without_transactions'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/transactions.rb:126:in `save'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/connection_adapters/abstract/database_statements.rb:51:in `transaction'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/transactions.rb:91:in `transaction'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/transactions.rb:118:in `transaction'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/transactions.rb:126:in `save'
#{RAILS_ROOT}/app/models/player.rb:21:in `create_player'
app/controllers/team_controller.rb:53:in `add_player'

I've added some accessors because I don't know if they are needed by ActiveRecord, but nothing changes.

class Player < ActiveRecord::Base
   attr_reader :team_id, :name, :age, :goal, :defence, :stamina, :goalkeeper,
               :playmaking, :pass, :shot, :speed, :head, :creativity, :free_kicks,
               :captain, :mood, :aggresiveness
   attr_writer :team_id, :name, :age, :goal, :defence, :stamina, :goalkeeper,
               :playmaking, :pass, :shot, :speed, :head, :creativity, :free_kicks,
               :captain, :mood, :aggresiveness
   belongs_to :team

   def initialize
     # De momento ponemos cualquier cosa, un numero aleatorio como cadena
     @name = (rand * 1000000).truncate.to_s
     # Edad entre 20 y 34 años
     @age = rand(34) + 1 + 20
   end

   # Crea un jugador con los valores por defecto
   def Player.create_player(team_id)
     player = Player.new
     player.team_id = team_id
     player.save
   end

   # Relaciona el jugador con un equipo
   def relate_to_team(team_id)
     @team_id = team_id
   end
end

it's already there:

class Team < ActiveRecord::Base
  has_many :players

···

#
  # ...
end

team = Team.find(4711)
player = team.players.create

-------- Original-Nachricht --------
Datum: Wed, 19 Jul 2006 19:06:16 +0900
Von: Phillip Hutchings <sitharus@sitharus.com>
An: ruby-talk@ruby-lang.org
Betreff: Re: Rails: Saving an object into DB

On 7/19/06, Kev Jackson <foamdino@gmail.com> wrote:
> >
> > And my Player model is:
> >
> > class Player < ActiveRecord::Base
> > belongs_to :team
> >
> > # Crea un jugador con los valores por defecto
> > def Player.create_player(team_id)
> > player = Player.new
> > player.relate_to_team(team_id)
> > player.save <-------------------------------- LINE 15
> >
>
> try @player.save

Why do you think that would work? @player isn't defined.

And for the original question. I have no idea if this will work, but
>from my experience with ActiveRecord it might help.

Firstly, what's with relate_to_team? Just use team_id =, ActiveRecord
does tricky things in the background, so always use the accessors.

Secondly, just so you know, you don't need the return on the last line
- the result of the last evaluated expression is always returned, so
for example:
def two_plus_one
  2+1
end

x = two_plus_one
=> x = 3

class Player < ActiveRecord::Base
  belongs_to :team

  def Player.create_player(team_id)
    player = Player.new
    player.team_id = team_id
    player.save
    player # The last evaluated line in a Ruby method is automatically
returned
  end
end

--
Phillip Hutchings
http://www.sitharus.com/

I've added some accessors because I don't know if they are needed by ActiveRecord, but nothing changes.

Think of ActiveRecord as magic. It constructs the class and all
attributes from the DB at runtime. When working with ActiveRecord
never use attr_accessor and its like for DB attributes. Also, always
use the automatically created ActiveRecord accessors, eg name = "1234"
rather than @name = "1234".

I suspect that player might be used by ActiveRecord, try using
new_player as the variable name instead.

···

--
Phillip Hutchings
http://www.sitharus.com/

I've added some accessors because I don't know if they are needed by ActiveRecord, but nothing changes.

class Player < ActiveRecord::Base
  attr_reader :team_id, :name, :age, :goal, :defence, :stamina, :goalkeeper,
              :playmaking, :pass, :shot, :speed, :head, :creativity, :free_kicks,
              :captain, :mood, :aggresiveness
  attr_writer :team_id, :name, :age, :goal, :defence, :stamina, :goalkeeper,
              :playmaking, :pass, :shot, :speed, :head, :creativity, :free_kicks,
              :captain, :mood, :aggresiveness
  belongs_to :team

  def initialize
    # De momento ponemos cualquier cosa, un numero aleatorio como cadena
    @name = (rand * 1000000).truncate.to_s
    # Edad entre 20 y 34 años
    @age = rand(34) + 1 + 20
  end

  # Crea un jugador con los valores por defecto
  def Player.create_player(team_id)
    player = Player.new
    player.team_id = team_id
    player.save
  end

  # Relaciona el jugador con un equipo
  def relate_to_team(team_id)
    @team_id = team_id
  end
end

just try

class Player < ActiveRecord::Base
   belongs_to :team
end

1 - you don't need to specify properties, ActiveRecord gets them from the table definition, so no attr_reader etc
2 - you don't need to add a method to save the player, as it inherits all the methods from ActiveRecord

If you can save a standard ActiveRecord-based object, then you can change it and add instance/class methods later

···

--
"Man is truly free only among equally free men" - Michael Bakunin