accepts_nested_attributes_for hard code in Controller - ruby-on-rails

I am writing a code stuff for API. Where i am assigning parameters to the class fields.
I have relationship,
event_file.rb
has_many :event_file_attachments
accepts_nested_attributes_for :event_file_attachments
event_file_attachment.rb
mount_uploader :attachment, EventFileUploader
belongs_to :event_file
Wanted to take the value from JOSN and write into nested attribute,
JOSN,
{"event_files"=>{"event_id"=>"1"}, "attachment"=>#<ActionDispatch::Http::UploadedFile:0xc16e4e4 #tempfile=#<Tempfile:/tmp/RackMultipart20141226-5521-12zezuk>, #original_filename="asset.JPG", #content_type="image/jpg", #headers="Content-Disposition: form-data; name=\"attachment\"; filename=\"asset.JPG\"\r\nContent-Type: image/jpg\r\n">}
In controller,
data = params[:event_files]
#event_file = EventFile.new(:event_member_ids => data['event_member_ids'],
:user_id => current_user.id,
:company_id => #current_company.id,
:event_id => data['event_id'],
:status => 0)
#event_file.save
#event_file_attachment = EventFileAttachment.new(:event_file_id => #event_file.id,
:status => 0,
:attachment => params[:attachment])
#event_file_attachment.save
And this is a wrong way to save the nested attribute, How to modify and optimize the code....

The association you created along with the accepts_nested_attribute_for method will create a event_file_attachments_attributes on which you can add the corresponding event file attachment attributes, here is a quick example:
In the controller:
#event_file = EventFile.new(:event_member_ids => data['event_member_ids'],
:user_id => current_user.id,
:company_id => #current_company.id,
:event_id => data['event_id'],
:status => 0,
:event_file_attachments_attributes => [{"0" => {:attr1 => "value", :attr2 => "value"}},{"1" => {:attr1 => "value", :attr2 => "value"}}])
For mor information on this you can check a really helpful railscast http://railscasts.com/episodes/196-nested-model-form-part-1

Related

Rails 4 render json nested objects

I need to render as Json a complex structure. I have next structure working:
render :json => #booking, :include => [:paypal,
:boat_people,
:boat => {:only => :boat_model, :include => {:boat_model => {:only => :name, :include => { :boat_type => {:only => :name}}}}}]
but I´m not able to add a port attribute with some other nested attributes to :boat, such as :boat_model (at same level).
UPDATE:
Although it´s not working, I include my port attribute.
render :json => #booking, :include => [:paypal,
:boat_people,
:boat => {:only => [:boat_model => {:include => {:boat_model => {:only => :name, :include => { :boat_type => {:only => :name}}}}},
:port => {:include => :city => {:only => name}}]}]
I mean, boat_model and port are both boat attributes.
This is the model object:
class Boat < ActiveRecord::Base
attr_accessor :price
#price
attr_accessor :extrasPrice
#extrasPrice
def as_json(options = { })
h = super(options)
h[:price] = #price
h[:extrasPrice] = #extrasPrice
h
end
belongs_to :boat_model
belongs_to :port
belongs_to :state
has_many :photos
end
I got it.
render :json => #booking, :include => [:paypal,
:boat_people,
:boat => {:only => :name, :include => {:port => {:only => :name, :include => {:city => {:only => :name, :include => {:country => {:only => :name}}}}},
:boat_model => {:only => :name, :include => {:boat_type => {:only => :name}}}}}]
You are probably going to want a much more robust system for displaying JSON. The built-in Rails helpers are really designed primarily for simple additions that enable users to do most of what they would want to accomplish. However, in your case, you are trying to make it do more than it was designed for.
I would highly recommend either creating a view object or using a gem like RABL.
My preference is to use Rabl for complex JSON. It basically creates the 'view object' for you by building a domain specific language that makes it relatively easy to build complex JSON objects in rails.
RABL basically allows you to build views that format JSON instead of HTML. The DSL is extremely rich and enables you to do just about anything you would want. In your case, I think the code would look something like this:
app/views/bookings/show.rabl
object #booking
#these are attributes that exist on your booking model:
attributes :booking_attribute, :other_booking_attribute
child :paypal do
#these are attributes that exist on your paypal model:
attributes :paypay_attribute1, :other_paypal_attribute
end
child :boat_people do
#boat_people attributes that you want to include
attributes :blah_blah
end
child :boat do
#boat attributes that you want to include
attributes :boat_attribute1, :boat_attribute2
child :boat_model do
attributes :name
child :boat_type do
attributes :name
end
end
end

update not propagated in nested models

I have a nested models set :
class Event < ActiveRecord::Base
belongs_to :place
:place
attr_accessible :place_attributes, :reject_if => :all_blank, :allow_destroy => false
class Place < ActiveRecord::Base
has_many :events
validates :label, :presence => true,
:uniqueness => {:case_sensitive => true, :on => :create }
validates :description, :presence => {:on => :create},
:uniqueness => {:case_sensitive => true , :on => :create}
In a test scenario, w the nested form, the user can update only the Place#label attributes, keeping all the other information..
test "should_update_event_place_data" do
put :update, :locale => I18n.locale, :id => #event[:id],
:event => { :place_attributes => { label: "a very beautiful place" } }
which leads to a request to EventsController#update, receiving the parameters :
params
{"event"=>{"place_attributes"=>{"label"=>"a very beautiful place"}}, "locale"=>"en",
"id"=>"145", "controller"=>"backoffice/events", "action"=>"update"}
(rdb:1) #event.update_attributes(params[:event])
false
#messages={:"place.description"=>["cannot be blank"]
But the validation is on create , not update .... no validation error should be detected ..
what could be wrong ?
thanks for help
I did more testing
debugger , right after the test setup ( before sending the put request)
#event_0
#<Event id: 161, account_id: 3, place_id: 249, slug: "my-new-event-on-2013-01-01-at- edinburgh-united-king...", title: "My New Event"
#event_0.place
#<Place id: 249, label: "new fake place",..
test request:
put :update, :locale => I18n.locale, :id => #event_0[:id], :event => { :place_attributes => { label: "a very beautiful place"} }
params in request are OK, #request/method = PUT
In EventsController#update
#event.update_attributes(params[:event])
.... I inserted a debug in the Place model...
(before_validation :i_am_on_create, :on => :create)
def i_am_on_create
debugger
p "CREATING"
end
and it's creating !! don't understand why it's not updating the parent nested model
update_attributes does not propagate the updates to associations. If you watch the source code (http://apidock.com/rails/ActiveRecord/Base/update_attributes) you'll see that the #save
is called in the end. And this is the default behaviour:
# existing resource 'mazeratti car'
car.name = "Wheelz"
car.brand.label = "Ferrari"
car.save
car.reload
car.name #=> "Wheelz"
car.brand.label #=> "Mazeratti"
if you want associations to be updated all the time the object is updated, look into using "autosave" (http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/belongs_to : Options)
If you're only wanting to test that the label attribute is updated, why not try doing update_attribute on that field only rather than the whole 'event'? Something like:
#event.place_attributes.update_attribute(
:label => params[:event][:place_attributes][:label]
)
Not tested - but you get the idea...
SOLVED
in order to update the nested model, I need to add the model instance id :
put :update, :locale => I18n.locale, :id => #event_0[:id], :event => { :place_attributes => { id: #event_0.place[:id], label: "a very beautiful place"} }
so in :place_attributes , I added the existing #event_0.place[:id] , and it's now updating
I found it in Anson answer on Feb 17 at 17:04 bottom page at
accepts_nested_attributes_for with find_or_create?

after_save error in ruby on rails

I have two models linked to each other and I am trying to do an after_save, create in the model and code is as follows.
class CustomerBill < ActiveRecord::Base
after_save :updating_and_creating_ledger_items
has_many :customer_ledgers, :dependent => :destroy
def updating_and_creating_ledger_items
CustomerLedger.create(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)
end
end
customer ledger model
class CustomerLedger < ActiveRecord::Base
belongs_to :customer_bill, :foreign_key => "customer_bill_id"
end
Now the problem is the program executes perfectly but the value are not been put in the database. If I check Customer ledger it is still empty.
The values are not getting stored. what seems to be the problem? Guidance towards this matter will be helpful.
Thanks in advance. :)
Add
validates_associated :customer_ledgers
In customer_bill.rb
Try
ledger = customer_ledgers.build(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)
ledger.save
EDITED for to avoid Validations, use
ledger = customer_ledgers.build(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)
ledger.save(:validate => false)
The CustomerLedger may be failing a validation check. Replace:
def updating_and_creating_ledger_items
CustomerLedger.create(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)
end
with:
def updating_and_creating_ledger_items
ledger = customer_ledgers.build(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)
ledger.save(:validate => false)
end
You're not setting the customer_bill_id in the create, change create to build as so:
customer_ledger.build(:date => self.date, :customer_id => self.customer_id, :user_id => self.user_id)

Why the mass assignment code does not work

There is a mass assignment defined in sys_log model in our rails 3.1.4 app:
attr_accessible :log_date, :user_name, :user_id, :user_ip, :action_logged, :as => :new_log
A method is defined in application_controller to save the log:
def sys_logger(action_logged)
log = SysLog.new(:log_date => Time.now, :user_id => session[:user_id], :user_name => session[:user_name], :user_ip => session[:user_ip],
:action_logged => action_logged, :as => :new_log)
log.save
end
However, the mass assignment does not work. Here is the warning message:
WARNING: Can't mass-assign protected attributes: log_date, user_id, user_name,
user_ip, action_logged, as
:new_log is not working as defined. What's wrong with the code above? Thanks so much.
The :as => :new_log is now part of the hash of attributes, instead of a separate option you pass in.
Adding some curly braces should help:
def sys_logger(action_logged)
log = SysLog.new({:log_date => Time.now, :user_id => session[:user_id],
:user_name => session[:user_name], :user_ip => session[:user_ip],
:action_logged => action_logged }, :as => :new_log)
log.save
end
Or assigning it temporarily:
def sys_logger(action_logged)
attrs = { :log_date => Time.now, :user_id => session[:user_id],
:user_name => session[:user_name], :user_ip => session[:user_ip],
:action_logged => action_logged }
log = SysLog.new(attrs, :as => :new_log)
log.save
end

group_by using value from other model

Using ROR 2.3.8
I have this in cities_controller.rb:
#shops = Shop.published.search params[:keyword], {
:conditions => conditions,
:star => true,
:group_by => 'city_id',
:group_function => :attr,
:page => params[:page]
}.merge(:order => 'rating_average DESC')
#cities = #shops.collect { |shop| shopy.city }
How can I tell Rails to get the rating_average from City model instead of Shop model? Because the Shop model does not have rating_average. It's actually City model that gets rated.
Thank you.
UPDATES
published namescope in Shop.rb
sphinx_scope(:published) {
{:conditions => {:status => 'published'}}
}
Indexes in Shop.rb
define_index do
indexes city.name, :as => :name, :sortable => true
indexes city.duration, :as => :duration
indexes city.status, :as => :status
#has city.budget, :as => :budget
#has city(:created_at), :as => :created_at
has city(:rating_average), :as => :rating_average
has city_id
end
UPDATES 2
class City < ActiveRecord::Base
has_many :shops, :dependent => :destroy
...
end
You should use joins to acheive this:
#shops = Shop.published.search params[:keyword], {
:conditions => conditions,
:star => true,
:group_by => :city_id,
:group_function => :attr,
:page => params[:page]
}.merge(:order => :rating_average,
:sort_mode => :desc)
You should also add cities. or shops. before columns to specify which table's column.

Resources