class Contact < ActiveRecord::Base
attr_accessor :email
def initialize(data)
data.each { |k, v| send("#{k}=", v) }
end
end
In rails console
Contact.create!({"email"=>"foo#gmail.com"})
The record saved to the database has email as nil
Update:
The data is being passed in is JSON. I am getting all the data from the JSON and trying to save that into the database.
Did you try:
Contact.create!(email: "foo#gmail.com")
The email as a :symbol and no curly brackets?
Also, why are you initializing in your model?
With Mohamed El Mahallaway, I think your code setup could be improved (to negate initializing your model). I think you'll be better using the strong_params functionality of Rails:
#app/controllers/contacts_controller.rb
def new
#contact = Contact.new
end
def create
#contact = Contact.new(contact_params)
#contact.email = "foo#gmail.com"
#contact.save
end
private
def contact_params
params.require(:contact).permit(:email, :other, :params)
end
I may have miscalculated your competency with Rails, but this is the "Rails" way to save the correct data to your model :) You may to have a before_save method in your model to use the email attribute, considering it's a virtual attribute
Related
Hello I'm quite new to rails API. I'm having trouble on how can I access the :guest object from the params. What I want to to do is to create a new record in Booking and Guest. Thank you.
Booking Controller
attr_accessor :guest
def create
booking = Booking.new(reservation_params)
booking.guest.build({booking_id: booking.id, first_name: ?, last_name: ?})
end
def reservation_params
params.require(:booking).permit(:start_date, :end_date :guest => [:first_name, :last_name])
end
POST
{
"start_date": "2021-03-12",
"end_date": "2021-03-16",
"guest": {
"first_name": "John",
"last_name": "Doe",
}
}
1. You're assigning a local variable - not an instance variable.
attr_accessor :guest
def create
booking = Booking.new(reservation_params)
end
Here you might assume that since you declared a setter with attr_accessor that this would set the instance variable #booking so that you can access it from the view? Wrong. When performing assignment you need to explicitly set the recipient unless you want to assign a local variable.
attr_accessor :guest
def create
self.booking = Booking.new(reservation_params)
end
But you could actually just write #booking = Booking.new(reservation_params) since that setter is not actually doing anything of note.
2. Models don't have an id until they are saved.
This line:
booking.guest.build({booking_id: booking.id, first_name: ?, last_name: ?})
Is actually equivilent to:
booking.guest.build(booking_id: nil, first_name: ?, last_name: ?)
One big point of assocations is that the ORM takes care of linking the records for you. Let it do its job. If you're ever assigning an id manually in Rails you're most likely doing it wrong.
3. You're not saving anything to the DB
.new (build is just an alias for new) just instanciates an new model instance. You need to actually save the object for it to have any effect beyond the current request.
How do I fix it?
If you want to use that parameter structure it can be done with a bit of slicing and dicing:
def create
#booking = Booking.new(reservation_params.except(:guest)) do |b|
b.guest.new(reservation_params[:guest])
end
if #booking.save
redirect_to #booking
else
render :new
end
end
The reason you use except(:guest) to remove the guest param is that the setter defined by the assocation expects an instance of guest and not a hash so it will blow up otherwise
Nested attributes
accepts_nested_attributes is the Rails way of passing attibutes through another model. It expects the parameter to be named guest_attributes not guest.
class Booking < ApplicationRecord
belongs_to :guest
accepts_nested_attributes_for :guest
end
If you really need to use the existing params structure you can just alter the parameters in your whitelisting method:
class BookingsController < ApplicationController
# ...
def create
#booking = Booking.new(reservation_params)
if #booking.save
redirect_to #booking
else
render :new
end
end
private
def reservation_params
params.require(:booking)
.permit(:start_date, :end_date, guest: [:first_name, :last_name])
.tap do |p|
# replaces the key :guests with :guest_attributes
p.merge!(guest_attributes: p.delete(:guest))
end
end
end
You're trying to create two associated models as once, you can use:
accepts_nested_attributes_for.
In the booking model add:
# models/booking.rb
accepts_nested_attributes_for :guest
And then in the controller:
def reservation_params
params.require(:boking).permit(:start_date,...,
guest_attributes: [:first_name, :last_name])
end
And then you can just create the booking including the guest like so:
booking = Booking.new(reservation_params)
Find more info here:
https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Here i'm trying to save json data to sqlite database using rails controller, but i'm not getting json data to controller parameters
In a specific controller I have the below list of params:
Parameters: {"person"=>"{\"name\":\"akhil\",\"profession\":\"it\",\"address\":\"hyderabad\",\"mobilenum\":67588}"}
Controller
def createPerson
puts "parameters are : "+params[:person].to_s
user_params = ActiveSupport::JSON.decode(params[:person])
puts "parameters name:"+user_params[:name].to_s
#person = Person.new(name: user_params[:name], profession:
user_params[:profession], address: user_params[:address], mobilenum:
user_params[:mobilenum])
#person.save
end
It is showing below error
(no implicit conversion of nil into String)
I'm getting the nil value in user_params[:name].to_s
Could you please help me to solve this
Seems like all you need to do is to create a new Person record after submitting a form. Well, probably you would want to use strong params and make a little refactor, so your controller will look something like this:
class PersonsController < ApplicationController
# you can name your action simply `create`, so you can use resource routing
def create
# most probably you don't need to make person an instance variable
person = Person.new(person_params)
# and here it is a good practice to check if you really saved your person
if person.save
# do something to tell user that the record is saved, e.g.
flash[:success] = 'person has been saved'
else
# show user why your person record is not saved, e.g.
flash[:error] = "person cannot be saved: #{person.errors.full_messages.to_sentence}"
end
end
private
# and here is the method for your permissible parameters
def person_params
params.require(:person).permit(:name, :profession, :address, :mobilenum)
end
end
How to refactor this code for more DRY controller?
#places = Place
#places = #places.address(params[:address]) if params[:address].present?
#places = #places.address(params[:name]) if params[:name].present?
#places = #places.price_greater_than_equal params[:price_from] if params[:price_from].present?
#and more..
method address is build from scope in model
sorry for bad english
Why are you not using the Rails Way ? if params are present they will be updated. the validations should be in the model in necessary.
def update
#places.update(places_params)
respond_with(#places)
end
private
def set_places
#places = Places.find(params[:id])
end
def places_params
params.require(:places_params).permit(:address, :postcode, address_attributes: :address (nested attributes))
end
In my Rails 3.1.1 project I have an ActiveModel that talks to API (ripped from Paul Dix's book, shortened for readability):
class Job
include ActiveModel::Validations
include ActiveModel::Serializers::JSON
ATTRIBUTES = [ :id,
:title,
:description,
:company_id ]
attr_accessor *ATTRIBUTES
validates_presence_of :title, :description
validates_numericality_of :company_id, :id
def initialize(attributes = {})
self.attributes = attributes
end
def attributes
ATTRIBUTES.inject(
ActiveSupport::HashWithIndifferentAccess.new
) do |result, key|
result[key] = read_attribute_for_validation(key)
result
end
end
def attributes=(attrs)
attrs.each_pair {|k, v| send("#{k}=", v)}
end
def read_attribute_for_validation(key)
send(key)
end
# More method definitions...
end
I instantiate #job in my controller, new action (company_id is a segnment key in the route: /companies/:company_id/jobs/new) like this:
#job = Job.new(company_id: params[:company_id])
Then, using CanCan, I check user's permissions to create to create a job. Basically, CanCan checks if current_user's company_id attribute matches job's company_id. This check fails because #job.company_id is returned as String.
Certainly, I can use params[:company_id].to_i while instantiating the object, but this seems like a workaround that I would have to repeat later.
Question: is there a way to make my Job ActiveModel more "type-aware" and make it return int for #job.company_id call?
I googled around, checked activemodel source code, but doesn't seem to find an answer. Any help is greatly appreciated.
Update
I was thinking more of something like schema block for ActiveModel, just like the one in ActiveResource.
attr_accessor *ATTRIBUTES
create a method like this:
def company_id
#company_id
end
You can just override that with
def company_id
#company_id.to_i
end
Answering my own question....
mosch's answer suggested to override the getter for company_id in my ActiveModel. However, I would have to repeat this for all of _id attributes in the model. Therefore, I decided to cast all of the '_id' attributes to integers while initializing the object. As follows:
def attributes=(attrs)
attrs.each_pair do |k, v|
if "#{k}".include? "_id"
send("#{k}=", v.to_i)
else
send("#{k}=", v)
end
end
end
I'm assuming your Company has_many => :jobs? If so, you could try
def new
#company = Company.find(params[:company_id])
#job = #company.jobs.new
end
My registration form, which is a form for the Users model, takes a string value for company. However, I have just made a change such that users belongs_to companies. Therefore, I need to pass an object of Company to the Users model.
I want to use the string value from the form to obtain the an object of Company:
#user.company = Company.find_by_name(params[:company])
I believe the above works, however the form is passing the :company (which is string) into the model when I call:
#user = User.new(params[:user])
Therefore, I want to know (and cannot find how) to remove the :company param before passing it to the User model.
Rails 4/5 - edited answer
(see comments)
Since this question was written newer versions of Rails have added the extract! and except eg:
new_params = params.except[the one I wish to remove]
This is a safer way to 'grab' all the params you need into a copy WITHOUT destroying the original passed in params (which is NOT a good thing to do as it will make debugging and maintenance of your code very hard over time).
Or you could just pass directly without copying eg:
#person.update(params[:person].except(:admin))
The extract! (has the ! bang operator) will modify the original so use with more care!
Original Answer
You can remove a key/value pair from a Hash using Hash#delete:
params.delete :company
If it's contained in params[:user], then you'd use this:
params[:user].delete :company
You should probably be using hash.except
class MyController < ApplicationController
def explore_session_params
params[:explore_session].except(:account_id, :creator)
end
end
It accomplishes 2 things: allows you to exclude more than 1 key at a time, and doesn't modify the original hash.
The correct way to achieve this is using strong_params
class UsersController < ApplicationController
def create
#user = User.new(user_params)
end
private
def user_params
params.require(:user).permit(:name, :age)
end
end
This way you have more control over which params should be passed to model
respond_to do |format|
if params[:company].present?
format.html { redirect_to(:controller => :shopping, :action => :index) }
else
format.html
end
end
this will remove params from the url
Rails 5+: Use the handy extract! method with strong params!
The extract! method removes the desired variable from the Parameters object (docs) and returns a new ActionController::Parameters object. This allows you to handle params properly (i.e. with strong params) and deal with the extracted variable separately.
Example:
# Request { user: { company: 'a', name: 'b', age: 100 } }
# this line removes company from params
company = params.require(:user).extract!(:company)
# note: the value of the new variable `company` is { company: 'a' }
# since extract! returns an instance of ActionController::Parameters
# we permit :name and :age, and receive no errors or warnings since
# company has been removed from params
params.require(:user).permit(:name, :age)
# if desired, we could use the extracted variable as the question indicates
#company = Company.find_by_name(company.require(:company))
Full example in controller
Of course, we could wrap this up in a handy method in our controller:
class UsersController < ApplicationController
before_action :set_user, only: [:create]
def create
# ...
#user.save
end
def set_user
company = params.require(:user).extract!(:company)
#user = User.new(params.require(:user).permit(:name, :age))
#user.company = Company.find_by_name(company.require(:company))
end
end
To be possible to delete you can do a memo:
def parameters
#parameters ||= params.require(:root).permit(:foo, :bar)
end
Now you can do:
parameteres.delete(:bar)
parameters
=> <ActionController::Parameters {"foo" => "foo"} permitted: true>