I am new to ruby on rails and i want to apply if exists update or create in the database. Currently it is creating a new row everytime. How it can be done?
Here is "gmatches_controller.rb" (Controller):
class GmatchesController < ApplicationController
def savedata
#match = Gmatch.new(match_params)
if #match.save
redirect_to root_path
else
render 'index'
end
end
private
def match_params
params.permit(:tournament_id, :player_name, :played_with, :start_date, :winner_name)
end
end
Try using find_or_initialize_by:
# replace
# #match = Gmatch.new(match_params)
#match = Gmatch.find_or_initialize_by(match_params)
# now #match will either be a new record, or a persisted record
# #match.new_record?
# #match.persisted?
if #match.new_record?
# do something if it's brand new (like save it)
else
# do something if it already existed (like update it)
end
Related
I'm fairly new to rails and struggling on changing database values after the user successfully paid via stripe. Additionally after paying, it somehow redirects me everytime to '/subscriberjobs/1' which doesn't exist. Instead it should direct to the root_path of the application.
Here is what I've got:
Routes
resources :subscriberjobs
resources :jobs
Jobs Controller
def new
if current_user
#job = current_user.jobs.build
else
redirect_to new_user_session_path
end
end
def create
#job = current_user.jobs.build(job_params)
if #job.save
redirect_to '/subscriberjobs/new'
else
render 'new'
end
end
Subscriberjobs Controller (Here is what doesn't work!)
class SubscriberjobsController < ApplicationController
before_filter :authenticate_user!
def new
end
def update
token = params[stripeToken]
customer = Stripe::Customer.create(
card: token,
plan: 1004,
email: current_user.email
)
Job.is_active = true # doesn't work
Job.is_featured = false # doesn't work
Job.stripe_id = customer.id # doesn't work
Job.save # doesn't work
redirect_to root_path # doesn't work
end
end
Please tell me if you need additional information. Every answer is very appreciated. Thanks!
Send saved job id to subscriberjobs/new as a param. You can keep hidden field which will have value job_id in subscriberjobs/new html form, which will call your SubscriberjobsController#update method. There access it using params.
In JobController #create
redirect_to "/subscriberjobs/new?job_id=#{#job.id}"
In your SubScribeJob form
hidden_field_tag 'job_id', params[:job_id]
In your SubScribeJobCotroller
#job = Job.find(params[:job_id])
When a post is updated I want to grab the number of words before it is saved and subtract that from the newly saved post word count. I want to do this in the post update controller.
Here is the update method
def update
#guide = Guide.friendly.find(params[:guide_id])
#post = Post.friendly.find(params[:id])
post_word_count_before = #no idea how to get it
post_word_count_after = #post.post.scan(/\w+/).size
change_in_words = post_word_count_after - post_word_count_before
if #post.update post_params
PostContributer.create!(user_id: current_user.id, post_id: #post.id, no_of_contributions: 1, no_of_words_contributed: change_in_words)
redirect_to guide_post_path(#guide, #post)
flash[:success] = "Updated successfully"
else
render 'edit'
end
end
I'm using #post.post.scan(/\w+/).size to get the word count after the post is updated. But I dont know how to get the word count before it is updated and store it in the variable post_word_count_before
The #post object is not changed until the #post.update post_params line, so the way you have it, the post_word_count_after variable contains the word count before the update.
I think this is what you are after:
def update
#guide = Guide.friendly.find(params[:guide_id])
#post = Post.friendly.find(params[:id])
post_word_count_before = #post.post.scan(/\w+/).size
if #post.update post_params
post_word_count_after = #post.post.scan(/\w+/).size
change_in_words = post_word_count_after - post_word_count_before
PostContributer.create!(user_id: current_user.id, post_id: #post.id, no_of_contributions: 1, no_of_words_contributed: change_in_words)
redirect_to guide_post_path(#guide, #post)
flash[:success] = "Updated successfully"
else
render 'edit'
end
end
active_model which is mixed into active_record provides API for record changes. You could use record.changes which provides hash of all the attributes that got changed with before and after values.
So instead of putting all that business logic in the controller, I would that rather move that into model where it belongs and with the use of ActiveModel::Dirty API and the callbacks you could achieve what you are after.
For example: Following could be your updated controller logic, clean and simple
class PostsController < ApplicationController
before_action :load_guide, only [:update] # add other actions that require #guide
before_action :load_post, only [:update] # add other actions that require #guide
def update
if #post.update(post_params)
redirect_to(guide_post_path(#guide, #post), success: "Updated successfully")
else
render 'edit'
end
end
private
def load_guide
#guide = Guide.friendly.find(params[:guide_id])
end
def load_post
#post = Post.friendly.find(params[:id])
#post.contributer = current_user
end
end
And your updated model:
class Post < ActiveRecord::Base
attribute_accessor :contributer # to get the current_user info from controller
before_update :create_post_contributer!
def create_post_contributer!
before = self.changes[:post][0].scan(/\w+/).size
after = self.changes[:post][1].scan(/\w+/).size
change_in_words = after - before
PostContributer.create!(
user_id: self.contributer.id,
post_id: self.id,
no_of_contributions: 1,
no_of_words_contributed: change_in_words
)
end
end
Refer to ActiveModel::Dirty for more info.
Try using it through session.
Store the previous updated value in session variable and delete the session variable once you do all the calculation from older post and newer post.
I'm not too sure what's gone on, but basically in my reviews table in my database I did the following migration:
def change
remove_column :reviews, :name, :string
add_column :reviews, :user_id, :integer
end
So remove the name column and added the user_id column. I then ran rake db:migrate and rake db:setup, opened up the rails server through rails s and am now having this error:
TypeError in ProductsController#index
can't convert nil into String
def record_not_found
flash[:alert] = "Cannot find record number " + params[:id] + ".
Displaying all records."
redirect_to root_path
end
I am not really too sure what's gone on and what the problem is, the about code it's complaining about is in my application_controller.rb:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
# make these methods available / visible in views
helper_method :the_date, :active_menu, :admin? , :logged_in?, :logged_in_as, :acting_role
# from an error generated when a request is
# made for a record with an 'id' that does not exist
rescue_from ActiveRecord::RecordNotFound,
:with => :record_not_found
private # these methods below can only be called by the object itself
def record_not_found
flash[:alert] = "Cannot find record number " + params[:id] + ".
Displaying all records."
redirect_to root_path
end
Products_controller.rb :
class ProductsController < ApplicationController
# When this controller is called, this method is always run first.
# The method 'set_product' found below, will be called for 'only' those
# actions indicated. Used to retrieve object data from the database.
before_action :set_product, only: [:show, :edit, :update, :destroy]
before_filter :check_authorization, except: [:index, :show, :search ]
# GET /products
# This action uses the class method 'all' sent to the classs Product to get all products
# similar to "SELECT * FROM PRODUCTS" , "#products" represents a collection of different
# product objects. Notice it is plural. Ordered by product title.
def index
# #products = Product.all.order :title
#products = Product.order(:title).page(params[:page]).per(6)
end
# GET /products/1
# The 'before_action' method above causes the private set_product' method to be called.
# The class method 'find' is sent to the Product class to find an existing product by
# its id. The retrieved database record is used to create an instance variable #product .
# The associated view 'show.html.erb' is called - using the instance variable #product .
def show
end
# GET /products/new
# A new empty Product object '#product' is created, all attributes are blank.
# This empty instance variable is sent to the view 'new.html.erb', #product is
# used in the blank form when entering new values.
def new
#product = Product.new
end
# GET /products/1/edit
# The 'before_action' method above causes the private set_product' method to be called.
# The class method 'find' is sent to the Product class to find an existing product by
# its id. The retrieved database record is used to create an instance variable #product .
# The associated view 'edit.html.erb' is called - using the instance variable #product,
# #product is used in the blank form when editing existing values.
def edit
end
# POST /products
# product_params is a hash object with all attribute values populated from a form used
# in 'new' above. The attributes in product_params are used in a new method to create
# a new product object. If the object's values are saved to the database then a flash
# notice is displayed, else the 'new' action is called again so a user can alter any errors.
def create
#product = Product.new(product_params)
if #product.save
redirect_to #product, notice: 'Product was successfully created.'
else
render action: 'new'
end
end
# PATCH/PUT /products/1
# this update action is run after a form is used to edit a record.
# The 'before_action' method above causes the private 'set_product' method to be called
# which instantiates a #product object from the database. This object is then updated with
# a call to product_params. The private method 'product_params'checks that the attributes
# used from the form are permitted.
# If successful this action redirects the user to show.html.erb or it will again
# render / display the edit form to correct any invalid data
# When editing, the file_field img_url textbox holds the value of a product's newly
# browsed filename, while the textbox img_url displays any existing filename.
# If a new filename has not been selected the file_field will remain empty but will
# be used to update the product's record.
def update
# temp_url_string is used to temporarily save from the database, an existing product's
# img_url filename.
temp_url_string = Product.find(params[:id]).img_url
if #product.update(product_params)
# if product's img_url attribute from the form is blank
if #product.img_url.empty?
# then a copy of the filename string is assigned to the img_url attribute
#product.update_attribute(:img_url, temp_url_string)
end
# redirect_to #product, notice: 'Product was successfully updated.'
# redirect_to #product, notice: "'" + #product.title + "'" + " was successfully updated."
redirect_to #product, notice: "'#{#product.title}' was successfully updated."
else
render action: 'edit'
end
end
# DELETE /products/1
# The 'before_action' method above causes the private set_product' method to be called.
# The class method 'find' is sent to the Product class to find an existing product by
# its id. The retrieved database record is used to create an instance variable #product .
# This object is then sent the message 'destroy' to delete the record from the database.
# A redirection method, redirects to the products_path (products_url) which means ... products#index
def destroy
#product.destroy
redirect_to products_url
end
# 'fuzzy_search' assigns to an instance variable collection '#products' all objects which include some
# of the search_string in their title, the collection is ordered by title.
# If there are some products to display, this action redirects the user to the 'index' action.
def search
# #products = Product.simple_search(params[:search_string])
products = Product.fuzzy_search2(params[:search_string])
#products = Kaminari.paginate_array(products.order :title).page(params[:page]).per(6)
if products.empty?
flash.now[:alert] = "No records found - displaying all records ..."
##products = Product.all.order :title
#products = Product.order(:title).page(params[:page]).per(6)
end
render :action => "index"
end
def multi_find
# call a Product class method, using two parameters; a category unique identifier and a search string (author or title)
products = Product.multi_find(params[:cat_id], params[:search_string])
#
#products = Kaminari.paginate_array(products.order :title).page(params[:page]).per(6)
# if no products have been found
if products.empty?
# display a notice
flash.now[:alert] = "No records found - displaying all records ..."
# then display all products
#products = Product.order(:title).page(params[:page]).per(6)
end
# use the index view
render :action => "index"
end
private
# The 'before_action' method above causes the private 'set_product' method to be called.
# The class method 'find' is sent to the Product class to find an existing product by
# its id, the retrieved database record is used to create an instance variable #product .
# This class method 'find' is similar to "SELECT * FROM PRODUCTS WHERE PRODUCT.ID = params[:id]"
def set_product
#product = Product.find(params[:id])
end
# This private method is called by the actions; create and update.
# 'product_params' checks that only permitted attributes are allowed.
# A hash 'params' of these parameter attributes and associated values is returned.
def product_params
params.require(:product).permit(:title, :description, :img_url, :price, :date_published, :author_name, :stock_level,:genre)
# returns 'params' a hash collection of keys and associated values.
end
end
looks like you have an ActiveRecord::RecordNotFound error in ProductsController#index,
so method record_not_found called with params[:id] = nil,
which in turn causes an TypeError at this line:
flash[:alert] = "Cannot find record number " + nil + ".
Displaying all records."
anyway for a more exact answer need to look at the ProductsController
My QuizzesController#index action looks like this:
def index
#user = current_user
#quiz = Quiz.create(user_id: current_user.id)
end
My view draws the quiz form fine. It goes to the results/index view as intended. BUT the various attributes of the quiz are NOT updated on the Quiz instance which is pulled from the database, in the QuizzesContoller#update action:
def update
#results = Quiz.where(user_id: current_user.id).last
redirect_to results_path
end
('update' is called in this case because the Quiz instance already exists, having been created in the 'index' action).
So, I tried changing the 'update' action to:
def update
#quiz.save
#results = Quiz.where(user_id: current_user.id).last
redirect_to results_path
end
But this triggers the error:
undefined method 'save' for nil:NilClass
Why is that? Shouldn't my QuizzesController have access to the #quiz variable as set up in the 'index' action? Can anyone explain what the problem is there?
Others have answered this question, so I thought I would explain why the answer is what it is. In Ruby, variables that begin with the # symbol are instance variables. This means that they are created when a new instance of their parent object is instantiated and are unique to that instance of the object.
Rails based web apps, for the most part, are stateless, meaning that state is not persisted between http requests. In layman terms, the app treats each and every request independent of all other requests. Due to this, the controllers are instanced classes. Every request instantiates a new instance of the controller class.
EDIT:
More I look at your code, you aren't following proper conventions
class QuizzesController < ApplicationController
# GET index: for displaying a list of quizzes
def index
#quizzes = Quiz.where(user_id: current_user.id)
end
# GET show: for getting a single quiz record
def show
#quiz = Quiz.find(params[:id])
end
# GET new: for initializing a new quiz record
def new
#quiz = Quiz.new
end
# POST create: for saving a new quiz record
def create
#quiz = current_user.quizzes.create(quiz_params)
if #quiz.errors
render :new
else
redirect_to #quiz #or whereever
end
end
# GET edit: for initializing existing quiz for update
def edit
#quiz = Quiz.find(params[:id)
end
# PUT/PATCH update: for updating an existing quiz record
def update
#quiz = Quiz.find(params[:id])
if #quiz.update(quiz_params)
redirect_to #quiz # or whereever
else
render :edit
end
# DELETE destroy: for deleting a quiz record
def destroy
Quiz.find(params[:id]).destroy
redirect_to :index # or whereever
end
end
You have not #quiz variable in your update action. Actions in the controller does not have access to variables in other actions.
The QuizzesController instance is not persisted between requests. The real reason instance variables are used in controllers is to pass that variable to the view.
A normal update action would look something like:
def update
#quiz = current_user.quiz # I'm assuming a user has one quiz?
#quiz.update(quiz_params) # Where quiz params takes the posted parameters from your update form
if #quiz.errors.any?
render :edit
else
redirect_to results_path
end
The key is you need to reassign #quiz with each request
I have a new select box appearing after edit. The model that I modified in the edit is another model using fields_for option.
Someone mentioned that I had the problem with the new and create actions in the controller.
the current controller:
def new
#print = Print.new
end
def create
#print = Print.new(params[:print])
#print.user_id = current_user.id
if #print.save
redirect_to print_path(#print), :flash => { :success => "Successfully created your Print Order." }
else
render :action => 'new'
end
end
def edit
#print = Print.find(params[:id])
#print.blackwhites.build
end
The fields_for that edit data from the model:
def index
def new
#blackwhite = Blackwhite.new
end
def create
#blackwhite = Blackwhite.new(params[:blackwhite])
#blackwhite.print_id = #print.id
end
def update
#blackwhite = Blackwhite.find(params[:id])
end
def show
#blackwhite = Blackwhite.find(params[:id])
end
def edit
#blackwhite = Blackwhite.find(params[:id])
end
Edit:
Fixed the problem.
The first thing I see:
def create
#blackwhite = Blackwhite.new(params[:blackwhite])
#blackwhite.print_id = #print.id
render :action => 'new' <<<< ?????
end
Try
redirect_to print_path(#print)
That would be the typical default thing to do, show the data that just got created, or in your nested case, show the parent of the record that just got created. All you really need to do is STOP rendering the new action after you create, that's NOT right!