Ruby 2.1
Rails 4.2
I need to generate (by concatenation) a code (which contains the product id) on new records.
The problem is that the record must be created to have a id. So I can't generate the code on "create" because de id don't exists yet. I'm stuck.
So far it works, but only on update.
class Product < ActiveRecord::Base
before_save :generate_code
private
def generate_code
tag = tags.map(&:name).join(", ").first(2)
self.code = ("#{category_id} #{tag} #{id} #{glaze.code}").parameterize
end
end
How to create a new record and, at the same time, concatenate your id on "code"?
Update:
I need it on update too.
The final solution is:
class Product < ActiveRecord::Base
after_save :generate_code
private
def generate_code
tag = tags.first.name.first(2)
update_column(:code, ("#{category_id}" + "#{tag}" + "#{id}" + "#{glaze.code}").parameterize.upcase)
end
end
You could Try something like this. This will generate a new code after creation (Part 1 issue where you have no id) and before update (Part 2 issue which works fine as a before because you already have an id)
class Product < ActiveRecord::Base
after_create :generate_code
before_update :generate_code
private
def generate_code
tag = tags.map(&:name).join(", ").first(2)
if changed?
self.code = "#{category_id} #{tag} #{id} #{glaze.code}"
else
update_attribute(:code,"#{category_id} #{tag} #{id} #{glaze.code}")
end
end
end
As #MohammadAbuShady mentioned this should also work as it is a direct DB edit with no callbacks or validations
class Product < ActiveRecord::Base
after_save :generate_code
private
def generate_code
tag = tags.first.name.first(2)
update_column(:code, "#{category_id} #{tag} #{id} #{glaze.code}")
end
end
Related
Here is my issue. I have two models (Construction and Customer)
class Construction < ApplicationRecord
has_many :works
belongs_to :customer
end
class Customer < ApplicationRecord
has_many :constructions
end
I would like to associate a Customer to a Construction during the creation of a new construction.
To do so I have de following controller's method (which is obviously false)
def create
# #construction = Construction.new(constructions_params) (commented)
#construction = Construction.new(customer: #customer)
#customer = Customer.find(params[:customer_id])
#construction.save!
end
from the params I am able to understand that the construction is not saved because it is not attached to a customer and so cannot be created.
I am new to rails and I have been struggling for hours now..
Hope someone will be able to help me.
thanks a lot
Try to revert the order:
#customer = Customer.find(params[:construction][:customer_id])
#construction = Construction.new(customer: #customer)
#construction.save!
you need to assign #customer instance variable before you use it. Otherwise it's nil and nothing is assigned to the new Construction record.
If you have the customer_id available at the point of form creation I reckon that you can do something like this.
Also given the belongs_to relations with the customer on the construction, you should be able to update the customer_id on the construction.
def create
#construction = Construction.new(construction_params)
if #construction.save
# whatever you want to do on success
else
# Whatever you want to do on failure
end
end
# Given you have construction params
private
def construction_params
params.require(:construction).permit(:all, :the, :construction, :attributes, :customer_id)
end
I have an ActiveRecord model User where I am overriding the timestamp attributes as follows:
class User < ActiveRecord::Base
def updated_at
<some calculation>
end
def updated_at=
<some calculation>
end
def created_at
<some calculation>
end
def created_at=
<some calculation>
end
end
Everything works fine when I pass in those attributes explicitly on creation. I want to be able to do those calculations even on regular updates and creates.
Eg:
User.create
User.update_attributes(:not_timestamp_attributes => <some value>)
should also update the timestamps with the calculations.
Is there a best practice around this? I have Googled and I couldn't find anything on overriding timestamp attributes.
The best practice would be to let ActiveRecord handle updating those values for you.
But if you still need to do some sort of computation you could try adding some callbacks to before_save and before_create to explicitly do that, something like this:
class User < ActiveRecord::Base
before_save :compute_updated_at
before_create :compute_created_at, :compute_updated_at
def created_at
read_attribute(:created_at)
end
def created_at=(value)
compute_created_at
end
def updated_at
read_attribute(:updated_at)
end
def updated_at=(value)
compute_updated_at
end
private
def compute_updated_at
write_attribute(:updated_at, Time.now + 1.month)
end
def compute_created_at
write_attribute(:created_at, Time.now + 2.month)
end
end
You could use a separate column for your calculated values and one for the system updated values using a before_save action and ActiveRecord::Dirty's "column_changed?" method
before_save :calculate_created_at, if: :created_at_changed?
def calculate_created_at
update_column(:calculated_created_at, created_at - 1.days)
end
When I GET /admin/consoles/1/edit, for instance, this happens:
Couldn't find Brand with 'id'=
And then it is highlighting the following code fragment which I have in
/app/models/console.rb:
def full_name
brand = Brand.find(self.brand_id).name
"#{brand} #{self.name}"
end
Seems like it isn't recognizing self.brand_id. Ideas?
I would need to see your app/models/console.rb to be sure, but it seems that you should have a belongs_to relation and then you could just use that relation ...like this:
class Console < ActiveRecord::Base
belongs_to :brand
def full_name
"#{brand.name} #{name}"
end
end
But maybe you should have something guarding that like this:
def full_name
("#{brand.name} " if brand.present?) << "#{name}"
end
You could avoid the error with a test for the presence of brand_id parameter:
def full_name
if self.brand_id.present?
brand = Brand.find(self.brand_id).name
"#{brand} #{self.name}"
else
self.name #or other combination of parameters not using the brand model
end
end
Let us know if this helps you.
Here is my problem...I am setting up a game where several users can play a game with 10,000 raffle tickets. Every game will have 10,000 raffle tickets and will not start until all 10,000 raffle tickets are sold. That being said, I have two simple classes in my DB, and for every game that goes, I need to initialize the 10,000 unique tickets for a single game relationship. I'm not sure where I'm going wrong here. Any help would be greatly appreciated.
models/game.rb
class Game < ActiveRecord::Base
has_many :tickets
end
models/ticket.rb
class Ticket < ActiveRecord::Base
belongs_to :game
end
controllers/games_controller.rb
class GamesController < ApplicationController
def create
g = Game.new
g.winning_ticket_num = params["winning_ticket_num"]
g.value_per_ticket = params["value_per_ticket"]
g.save
10000.times do
ticket = Ticket.new
ticket.game_id = g.id
ticket.nickname = "null"
ticket.save
end
end
end
controllers/tickets_controller.rb
class TicketsController < ApplicationController
def create
t = Ticket.new
t.nickname = params["nickname"]
t.game_id = params["game_id"]
t.save
end
end
After all the comments exchanged, let's summarize:
class GamesController < ApplicationController
def create
#game = Game.new(params[:game])
10000.times do
#game.tickets.build(nickname: "null")
end
#game.save
end
end
Game.new creates your game, based on the parameters in your view. The "winning_ticket_num" and "value_per_ticket" will be automatically "copied" to your new game object. You must make sure these parameters can be assigned to, either using strong parameters in Rails, or using attr_accessible in Rails < 4.0
The #game.tickets.build creates the 10000 tickets. The game's ID will be automatically assigned when the game is finally saved. Also the tickets themselves will be saved when the parent game is saved
The thing i see that is wrong, If you are using form_for, then the params are params[:game]
class GamesController < ApplicationController
def create
g = Game.new
g.winning_ticket_num = params[:game]["winning_ticket_num"]
g.value_per_ticket = params[:game]["value_per_ticket"]
g.save
10000.times do
ticket = Ticket.new
ticket.game_id = g.id
ticket.nickname = "null"
ticket.save
end
end
end
So adding params[:game] will get the values
Don't set the IDs yourself, use ActiveRecord associations. Note that tickets.create will save the Ticket object to the database when it is created (which seems what you wanted to do above).
def create
#g = Game.new
#g.winning_ticket_num = params["winning_ticket_num"]
#g.value_per_ticket = params["value_per_ticket"]
#g.save
10000.times do
#g.tickets.create(nickname => "null")
end
end
EDIT: syntax error, the = changed to =>. Also remove the ticket. part. Sorry, this is what happens without tests.
Here's what is happening with my application.
My model looks like this
class Model1 < ActiveRecord::Base
def self.foo(value)
Model1.where(:field => value)
end
end
and then i have a controller using this model
...
Model1.foo('foo)
...
Now, i am expecting it to trigger a single query to get the records. Instead of that, what i am getting is 2 queries.
SELECT COUNT(*) FROM `MODEL1` WHERE `MODEL1`.`field` = 'foo'
SELECT * FROM `MODEL1` WHERE `MODEL1`.`field` = 'foo'
Not able to understand why the first query is being fired and how to avoid it. Couldn't find anything on net.
I'm a bit confused (like others in the comments) but here's what you can try -
class Model1 < ActiveRecord::Base
def self.foo(value)
Model1.where(:field => value)
end
end
should be
class Model1 < ActiveRecord::Base
def foo(value)
self.where("field = ?", value) #see http://guides.rubyonrails.org/active_record_querying.html#array-conditions
end
end