I am attempting to update database fields from an external JSON response using Ruby on Rails. I would like this action to take place upon creation of a new record, as the data is relatively static. In the future I'll need to tackle the ability to refresh the data, but that is for another day.
I have created the rails scaffolding using:
rails generate scaffolding hotel hotelId:integer hotelName:string hotelCity:string
I would like to create a new record using only the hotelId, send the hotelId in my URI request, and then update the hotelName and hotelCity field.
I'm stuck on both sending the request using the hotelId field, and saving the results.
hotel.rb Model:
class Hotel < ActiveRecord::Base
def self.save_data_from_api
api = Expedia::Api.new
response = api.get_information({:hotelId => '#hotelID'})
hotel_data = response.body
hotel_parsed = JSON.parse(hotel_data.to_json)
h = Hotel.new
h.hotelName = hotel_parsed['HotelInformationResponse']['HotelSummary']['name']
h.hotelCity = hotel_parsed['HotelInformationResponse']['HotelSummary']['city']
h.save!
h
end
end
Included in my hotels_controller.rb
def new
#hotel = Hotel.new
#hotelID = Hotel.hotelID
end
I have not updated the new.html.erb View because I did not know how or where to call the save_data_from_api method.
Incidentally, the Expedia API gem I'm using is located here: https://github.com/zaidakram/expedia
Like:
class Hotel < ActiveRecord::Base
before_save :save_data_from_api
def save_data_from_api
return if hotel_id.blank?
api = Expedia::Api.new
response = api.get_information({:hotelId => hotel_id})
hotel_data = response.body
hotel_parsed = JSON.parse(hotel_data.to_json)
self.hotel_name = hotel_parsed['HotelInformationResponse']['HotelSummary']['name']
self.hotel_city = hotel_parsed['HotelInformationResponse']['HotelSummary']['city']
end
end
and then use like Hotel.create(hotel_id: '33').
Note that I've changed your AR attribute names to be more Ruby-ish.
EDIT
Depending on what you're doing with your application, it may not be necessary or the best idea to add hotels from a controller. You might want to just load them from rails console, or, if you have a list of hotel IDs, using a Rake task.
However, if you want to load them from a controller, you can have:
# in controller:
def new; #hotel = Hotel.new; end
def create
# note: if you're using Rails 4, use strong params here
#hotel = Hotel.new(params[:hotel])
respond_to do |format|
if #hotel.save
format.html { redirect_to #hotel, notice: 'Hotel was successfully created.' }
else
format.html { render action: "new" }
end
end
end
And in your new.html.erb, something like:
<%= form_for #hotel do |f| %>
<%= f.text_field :hotel_id %>
<%= f.submit 'Submit' %>
<% end %>
Related
I am working on a Project where there as two models, (User,Project). They have many to many relationship and is implemented by has_many through which is (UserProject).
I want the functionality that when I am creating a project, below projects models fields, all the db users should be listed below having checkbox in front of each. When user enters all the projects model fields and checks the checkboxes for example 2 checkboxes below(2 users), All the data should be saved which includes projects creation as well as two records of UserProject.
Is there any way of doing this..
I am using form_with helper.
User(id,email,password)
Project(id,name)
UserProject(id,user_id,project_id)
If I'm understanding correctly, you want a form with sub-objects in it and a single submit.
I have done this in the past by #building the empty objects, so that they can be displayed in the form, and then handling them "correctly" in the params section:
Controller:
class MeetTypesController < ApplicationController
def new
#meet_type = MeetType.new
Job.all.each do |job|
#meet_type.meet_type_jobs.build(job: job, count: 0)
end
#meet_type.meet_type_jobs.sort_by{ |mtj| mtj.job.name }
end
def create
#meet_type = MeetType.new(meet_type_params)
if #meet_type.save
redirect_to meet_types_path
else
render 'new'
end
end
def edit
(Job.all - #meet_type.jobs).each do |job|
#meet_type.meet_type_jobs.build(job: job, count: 0)
end
#meet_type.meet_type_jobs.sort_by{ |mtj| mtj.job.name }
end
def update
if #meet_type.update(meet_type_params)
redirect_to meet_types_path
else
render 'edit'
end
end
def show; end
def index
#meet_types = MeetType.in_order
end
private
def meet_type_params
params.require(:meet_type).permit(:name, meet_type_jobs_attributes: [:id, :job_id, :count])
end
end
view:
%h1 Editing Meet Type
= bootstrap_form_for(#meet_type, layout: :horizontal) do |f|
= f.text_field :name, autocomplete: :off
%h2 Count of Jobs
= f.fields_for :meet_type_jobs do |jf|
= jf.hidden_field :job_id
= jf.number_field :count, label: jf.object.job.name, step: 1, in: 0..5
= f.form_group do
= f.primary
%para
= errors_for #meet_type
I hope this helps.
New to Ruby on Rails and been cracking my head on this. I have been following this tutorial here to make this form to save records into my DB - https://human-se.github.io/rails-demos-n-deets-2020/demo-resource-create/
This is my controller:
class ViewaddparamController < ActionController::Base
def view_add_param
newParam = ViewaddparamController.new
respond_to do |format|
format.html{ render :viewaddparam, locals: { newParam: newParam } }
end
end
def add_param
# new object from params
mlParam = ViewaddparamController.new(params.require(:Viewaddparam_Controller).permit(:name, :machine_model, :parameter_1, :parameter_2, :parameter_3, :data_train_set, :start_date, :end_date))
# respond_to block
respond_to do |format|
format.html do
if mlParam.save
# success message
flash[:success] = "ML parameters saved successfully"
# redirect to index
redirect_to model_url
else
# error message
flash.now[:error] = "Error: parameters could not be saved"
# render new
render :viewaddparam, locals: { newParam: newParam }
end
end
end
end
end
My route:
get 'viewparam', to: 'viewaddparam#view_add_param'
post 'add_param', to: 'viewaddparam#add_param', as: 'add_param'
My view:
<%= form_with model: newParam, url: add_param_path, method: :post, local: true, scope: :Viewaddparam_Controller do |f| %>
...
I kept getting this error whenever I try to submit the form
ArgumentError in ViewaddparamController#add_param
Wrong number of arguments (given 1, expected 0)
The error highlighted at my controller class, line 11.
What am I doing wrong here? I looked through the tutorial over and over but I can't see the fault here.
Thanks in advance.
It seems that you’re treating ViewaddparamController as both the controller – which in Rails terms is what responds to user requests - and the data model.
There’s often a one-to-one correlation to controllers and models, especially if you’re following what’s known as the RESTful pattern. So if your model was a Product, you might set it up in routes using a resources directive:
resources :products
That would set the app up to expect to use a ProductsController. And, using a similar coding style to your example, that would look a little like:
class ProductsController < ApplicationController
def new
product = Product.new
render :new, locals: { product: product }
end
def create
product = Product.new(params.require(:product).permit(…))
# etc
end
# etc
end
So you can see from this example that controller and model are related, but are named differently.
By using your controller name where you should be using the model, you’re calling #new on the controller, which is not normally something you need to do – controller instances are managed by the Rails app framework. And their initialiser doesn’t take any arguments, so Ruby complains that an initialiser that takes 0 arguments is being given 1.
I am working with a Rails application that allows users to create projects. Inside these projects, users can make lists. I am trying to figure out how to allow users to choose their "Default Working Project" from the projects index page. This would then propagate throughout the app, showing only lists associated with the current project. What is the best approach to making something like this.
You can achieve this easily by adding default_working_project_id field to your users table.
Then in your controller index set:
#default_working_project = current_user.default_working_project
In your user model add:
belongs_to :default_working_project, class_name: Project, foreign_key: :default_working_project_id
You can create your own action. In your routes file:
resources :projects do
member do
get 'set_default'
end
end
In your projects_controller:
def set_default
project.find params[:id]
current_user.default_working_project_id = project.id
respond_to do |format|
if current_user.save
format.html { redirect_to projects_path }
else
format.html { render 'index', notice: "your error message" }
end
end
end
In your views just add
link_to 'set default', set_default_project_path(project.id)
UPDATED
To remove current default project id from user:
You can make some methods to achieve this, like:
In your Project model
If you have a relation that project belongs_to user try this.
def is_a_current_project?
self.id == self.user.default_working_project_id
end
Then create an after_destroy :remove_current_project_relation callback method.
And the method, I recommend to add it inside your private methods:
def remove_current_project_relation
if is_a_current_project?
self.user.default_working_project = nil
end
end
Create a database field in Users table with default_project_id and set it.
On the model set:
def default_project
return projects.find_by_id(default_project_id) if default_project_id
false
end
And then, you can use something like this:
lists = user.default_project ? user.default_project.lists : user.lists
If only one user can see his projects and other users won't be able to see other user's projects, my suggestion is:
Make a boolean value is_default in the projects table. Add it with a migration.
Add :is_default to def project_params in the controller.
In the projects index page use:
<%= render #projects %>
Create file _project.html.erb in views/projects folder, add to it:
<%= form_for project, remote: true do |f| %>
# some project data
<%= f.check_box :is_default, class: 'project_default' %>
<% end %>
In projects_controller:
def update
#project = Project.find(params[:id])
if #project.update_attributes(project_params)
respond_to do |format|
format.html { redirect_to project_page } # this will run when you update project from edit page in form without 'remote: true'
format.js # this will run if you update project with 'remote: true' form
end
end
end
In projects.coffee in assets/javascripts folder:
$('input.project_default').change -> $(this).closest('form').submit()
Create update.js.erb in the views/projects folder, add to it:
$('#edit_project_<%= #project.id %>').replaceWith("<%= j render 'project' %>");
In projects_helper
def current_project
current_user.projects.find_by(is_default: true)
end
Maybe you'll need to change these a little, based on your tasks. This solution will update projects through JavaScript.
Also it would be great to add a method in the Project model, which will make the previous default project not default when the user makes other project default and so on.
When you need to use lists from default project you can use default_project.lists in your views.
I have a piece of code in Rails,
def create
#registration = Registration.new(registration_params)
if #registration.save
redirect_to #registration.paypal_url(registration_path(#registration))
else
render :new
end
end
I took it from tutorial. But I need just in this line:
#registration.paypal_url(registration_path(#registration))
Now, about my own controller, feed_controller, where
def create
#feed = Feed.new(check_params)
end
In the view erb file I put:
#feed.paypal_url(feed_path(#feed))
In my feed.rb (model):
def paypal_url(return_path)
values = {
business: "merchant#gotealeaf.com",
cmd: "_xclick",
upload: 1,
return: "#{Rails.application.secrets.app_host}#{return_path}",
invoice: id,
amount: course.price,
item_name: course.name,
item_number: course.id,
quantity: '1'
}
"#{Rails.application.secrets.paypal_host}/cgi-bin/webscr?" + values.to_query
end
Rake routes:
feed GET /:locale/feed(.:format) feed#index
feed#create POST /:locale/feed/create(.:format)
feed#new feed_new GET /:locale/feed/new(.:format)
feed#destroy feed_destroy GET /:locale/feed/destroy(.:format)
feed#edit feed_edit GET /:locale/feed/edit(.:format)
feed#update feed_update GET /:locale/feed/update(.:format)
But it prints the next error:
undefined method `paypal_url' for <#Feed::ActiveRecord_Relation:0x007fee24f5fc98>
How can I fix it? What is the problem?
UPDATE
def index
#current_user_is = current_user.email
session[:email] = #current_user_is
session[:id] = current_user.id
unless (current_user.member.present?)
#member = Member.new(:user_id => current_user.id)
#member.save()
redirect_to '/feed'
else
#new_feed = Feed.new
#feed = Feed.where(:member_id => current_user.member.id)
#category = Category.all
render 'home/uploads'
end
end
Simply use def self.paypal_url(return_path) instead of def paypal_url(return_path).
Explanation
You ran into your problem by defining a Class Method instead of an Instance Method, there's other posts discussing this.
The basic difference is, when defining:
def self.get_some_url
# code to return url of an instance
end
you can easily get the desired url of any objects, as in a view:
<% #feeds.each do |feed| %>
<%= feeds.get_some_url %>
<% end %>
Now calling Feed.get_some_url on the class would make no sense. Which url of the thousands would it call?
But there is a lot of use for class methods (where you define the method without self as you did)
def get_top_5
# code to return the top 5 most viewed feeds
end
Since this has nothing to do with a single instance, you define it for the entire Class. Leading to this call: Feed.get_top_5, which makes perfectly sense.
The second problem was not understanding the difference between where & find, this post will help you out with that.
Beginner with some dev experience here.
I have an app with multiple models and I have managed to work everything out but i am stuck here.
I have a model, called CartEntries
class CartEntry < ActiveRecord::Base
acts_as_paranoid
belongs_to :cart
belongs_to :sign
With a create method in the Cart Entry controller
def create
#entry = #cart.entries.create(entry_params)
if #entry.save
flash[:notice] = translate 'flash.notice'
else
flash[:error] = translate 'flash.error'
end
support_ajax_flashes!
respond_to do |format|
format.html # renders view
format.json { render json: #entry }
end
end
And a Model Sign with static signs inputed in the database and no create method.
class Sign < ActiveRecord::Base
has_many :cart_entries
accepts_nested_attributes_for :cart_entries
And from a Sign's view I initialize a new instance of a CartEntry and succsessfuly create a new Cart Entry after clicking the link, generating a notification.
<% #entry = CartEntry.new(sign: #sign)%>
<%= link_to t('.add_to_cart'), user_cart_entries_path(:entry => #entry.attributes),method: :post, remote: true, "data-type" => :json%>
The Cart Entry has another field called Count with a default value of 1. Im looking for a way for the user to input this number in a text field and when creating the Cart Entry , pass the Count the user inputed instead of the default 1.
What ever I try passes the default value.
While
<% #entry = CartEntry.new(sign: #sign, count: 5)%>
Does the trick properly, and passes 5 as the value , but I want the user to input this number since its clearly a variable.
While I understand that
<% #entry = CartEntry.new(sign: #sign)%>
Initializes the entry object on page load and that i must move it, I'm asking you kind people, where?
UPDATE
Entry Params:
private
def entry_params
params.require(:entry).permit(:sign_id, :count)
end
Answering in reverse order from your question:
Initializing the new CartEntry object should probably be in the new action of your controller. Rails controllers often have both new and create, new being tied to rendering the form to receive input and create being the action tied to the 'Submit' button. Your new action is often just something like:
def new
#entry = CartEntry.new(sign: #sign)
end
Your view to prompt the user for data should be named new.html.erb and have the form in it.
For getting the data from the form to your create method, you are half way there I think. If you moved the example you gave:
<% #entry = CartEntry.new(sign: #sign, count: 5)%>
to the create action in the controller, it would be
#entry = CartEntry.new(sign: #sign, count: params[:count])
#entry.save
Remember 'params' is just a hash that contains the form input data.
Hope that helps!