I have following table in database and also the model created for it.
|id |name |description |created_date |updated_date |
-----------------------------------------------------------------------
|1 |HELLO |greeting |2017-09-28 18:51:51 |2017-09-28 18:51:51|
model.rb
class Person < ApplicationRecord
has_many :person_activities
validates :name, uniqueness: true
end
I want to create update action in controller. It will update the name and description based on the name passed. e.g i want to update the name HELLO to HI. How can i create update action for that?
I tried following update action in controller but it wont hit the update action
def update
byebug
redirect_to Person.find(name: params[:id]).tap { |person|
person.update!(person_params)
}
end
private
def person_params
params.require(:person).permit(:name)
end
routes.rb
resources :person, only: [:index, :show, :create, :update, :destroy], defaults: { format: :json }
Tried testing like this http://localhost:1111/person/HELLO and passing { name: 'HI'} in body params. I am using postman for testing.
You can modify your model, something like this:
class Person< ActiveRecord::Base
before_save :change_name
private
def change_name
self.name="HI" if self.name=="HELLO"
end
end
To answer this properly we would need to understand the logic of how creating a new record with name = "HELLO" would need to be set to "HI". Do you have a lookup table for this logic or is it hardcoded?
The easiest thing I could think of is you can change the value of the passed parameter before doing the update by setting the value of the parameter. E.G.
params["name"] = "HI"
So when you do a person.update it will use the "name" param set to "HI".
Then you could do this in a
def update
respond_to do |format|
params["name"] = "HI"
if #person.update(person_params)
format.html { redirect_to #person, notice: 'Person was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
You could also do the update then just set the value after the update:
def update
respond_to do |format|
if #person.update(person_params)
#person.name = "HI"
#person.save
format.html { redirect_to #person, notice: 'Person was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
From postman you need to call login action first. So it will create a session in postman and then you can call update action.
Related
How do I go about making this code block dry?
I understand that dry means dont repeat yourself but I don't see any obvious opportunity for refactoring.
Index, show, edit, and create seem like basic/necessary methods. They appear to be pretty dry to me.
The methods after I am not sure about.
I haven't tried anything other than googling so far.
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
Slack.notify_channel
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
Slack.notify_channel
end
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
Slack.notify_channel
end
private
def user_params
params.require(:user).permit(:username, :email)
end
end
There is no rails backend attached to this code snippet. I am assuming it is just theoretical - wanting us to refactor the code to make it shorter.
IMHO you can do something like that.
class UsersController < ApplicationController
include ExceptionHandling
before_action :load_user, only: [:show, :edit, :update, :destroy]
after_action :slack_notify_channel, only: [:create, :update, :destroy]
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.create!(user_params)
respond_to do |format|
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
end
end
def update
#user.update!(user_params)
respond_to do |format|
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
end
end
def destroy
#user.destroy!
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def load_user
#user = User.find(params[:id])
end
def slack_notify_channel
Slack.notify_channel
end
def user_params
params.require(:user).permit(:username, :email)
end
end
I would recommend you to create a concern to manage exceptions and render each specific error by exception. Then you can avoid having two ways in each action to render a good and bad case.
I have to guess a bit about what's in your ApplicationController and User classes. But the obviously repeated code is #user = User.find(params[:id]). Your show and edit methods both just run this line. So, they do exactly the same thing, which they oughtn't to do. One method for one thing.
Also, once you resolve show and edit into a single method, your create, update and destroy methods should call it instead of repeating the line.
Next, I wouldn't use new as a method name, since it is already used by BasicObject::new. I tested a few things and it gets pretty unclear:
class Test
attr_reader :test
def initialize
#test = 'test'
end
end
class Test2
attr_reader :test2
def new
p 'test2new'
#test2 = Test.new
end
end
testx = Test2.new
p testx.new.test
p testx.test2.test
=> "test2new"
=> "test"
=> "test"
It takes extra effort to see when you're calling your own new method and when you're calling BasicObject::new. So, I'd change that to new_user or something, if you even need it at all — I don't see why you need both it and your create method. (If you don't need it, get rid of it.)
Finally, #user = User.find(params[:id]) doesn't imply either showing or editing, so understanding what you're trying to do and coming up with a name that reflects it (e.g. set_user_id) is something that ought to be done as well.
Im trying to create a new Category based on the title of the 'grow' that im creating (for a garden management tool'
But Im getting the following error:
'param is missing or the value is empty: category'
[edit] this is how the code is fixed. As per the suggestion in the comments:
remove the #grow = Grow.new(grow_params.merge(category: Category.create(cat_params)))
and replace with #grow = Grow.new(grow_params)
and in the Grow model add
after_create do
Category.create(name: self.title)
end
and its fixed.
class GrowsController < ApplicationController
before_action :set_grow, only: [:show, :edit, :update, :destroy]
def index
#grows = Grow.all
end
def show
end
def new
#grow = Grow.new
end
def edit
end
def create
#grow = Grow.new(grow_params.merge(category: Category.create(cat_params)))
respond_to do |format|
if #grow.save
format.html { redirect_to #grow, notice: 'Grow was successfully created.' }
format.json { render :show, status: :created, location: #grow }
else
format.html { render :new }
format.json { render json: #grow.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /grows/1
# PATCH/PUT /grows/1.json
def update
respond_to do |format|
if #grow.update(grow_params)
format.html { redirect_to #grow, notice: 'Grow was successfully updated.' }
format.json { render :show, status: :ok, location: #grow }
else
format.html { render :edit }
format.json { render json: #grow.errors, status: :unprocessable_entity }
end
end
end
# DELETE /grows/1
# DELETE /grows/1.json
def destroy
#grow.destroy
respond_to do |format|
format.html { redirect_to grows_url, notice: 'Grow was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_grow
#grow = Grow.find(params[:id])
end
def cat_params
params.require(:category).permit(:name)
end
def grow_params
params.require(:grow).permit(:title, :notes, :category_id)
end
end
I simply want to pass in the grow.title to new category name
However im not sure how to implement it.
In your create method leave the statement to be
#grow = Grow.new(grow_params)
and in your Grow model, write an after_create callback
within which you create your Category like Category.create(name: self.title)
Before I begin, let me say that I have two different devise user models.
I began removing the unnecessary methods from the scaffold_controller, because devise handles certain actions.
I'm trying to route to the current_user profile page, but I get an error every time I try to add the link in my navbar <%= 'My Profile', current_user %> doesn't work in this case. I plan to setup the associations as soon as I finish hooking up these controllers.
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
# GET /profiles
# GET /profiles.json
def index
#profiles = Profile.all
end
# GET /profiles/1
# GET /profiles/1.json
def show
#user = User.find(params[:id])
respond_to do |format|
format.html
format.json {render :json => #user }
end
end
# GET /profiles/1/edit
def edit
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
#profile = Profile.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params[:profile]
end
end
profiles GET /profiles(.:format) profiles#index
POST /profiles(.:format) profiles#create
new_profile GET /profiles/new(.:format) profiles#new
edit_profile GET /profiles/:id/edit(.:format) profiles#edit
profile GET /profiles/:id(.:format) profiles#show
PATCH /profiles/:id(.:format) profiles#update
PUT /profiles/:id(.:format) profiles#update
DELETE /profiles/:id(.:format) profiles#destroy
Assuming (such as bad thing to do), that you have already created the profile while creating the user ( since there is no create action in the controller ) and that a profile belongs_to a user, then this should work:
<%= link_to "My Profile", current_user.profile %>
Generally, I would get my associations set up properly before I even look at the controllers.
I have some code for a Rails 4 project I'm working on. It uses active_record (mysql2), and there is a has_many :through relationship that works properly when I interact through rails c (in either production or development). When I try to submit the relationship in a form (I am using simple_form), I can't seem to get it to save.
Here is how my information is currently set up (just showing snippets, I can't really show the whole source):
Model:
has_many :categorizations
has_many :resource_categories, through: :categorizations
accepts_nested_attributes_for :resource_categories
accepts_nested_attributes_for :categorizations
Form:
= simple_form_for #resource do |f|
= f.association :resource_categories
Controller:
# POST /resources
# POST /resources.json
def create
#resource = Resource.new(resource_params)
set_categories(#resource, params[:resource][:resource_category_ids])
respond_to do |format|
if #resource.save
format.html {
redirect_to #resource, notice: 'Resource was successfully created.'
}
format.json {
render action: 'show', status: :created, location: #resource
}
else
format.html {
render action: 'new'
}
format.json {
render json: #resource.errors, status: :unprocessable_entity
}
end
end
end
# PATCH/PUT /resources/1
# PATCH/PUT /resources/1.json
def update
respond_to do |format|
if #resource.update(resource_params)
set_categories(#resource, params[:resource][:resource_category_ids])
format.html {
redirect_to #resource, notice: 'Resource was successfully updated.'
}
format.json {
head :no_content
}
else
format.html {
render action: 'edit'
}
format.json {
render json: #resource.errors, status: :unprocessable_entity
}
end
end
end
# Never trust parameters from the scary internet, only allow the white list
# through.
def resource_params
params.require(:resource).permit(
:title, :slug, :ancestry, :status, :author_id, :published, :parent_id,
:resource_category_ids, :preview, :body
)
end
def set_categories(resource, categories)
# Clean out the existing categories (if there are any)
unless resource.resource_categories.blank?
resource.resource_categories.each do |category|
resource.resource_categories.delete(category)
end
end
unless categories.blank?
categories.each do |category|
unless category.blank?
resource.resource_categories << ResourceCategory.find(category)
end
end
end
end
When I issue the following commands using rails c -e production (or just plain rails c) it works (In this example, I assign all categories to all resources):
Resource.all.each do |resource|
ResourceCategory.all.each do |category|
resource.resource_categories << category
end
end
It seems like my problem is that the controller is not calling the helper function
Use this instead:
def create
#resource = Resource.new(resource_params)
#resource.set_categories(params[:resource][:resource_category_ids])
..
end
Move the method in the Resource model:
def set_categories(categories)
# Clean out the existing categories (if there are any)
unless new_record?
unless resource_categories.blank?
resource_categories.each do |category|
resource_categories.delete(category)
end
end
end
unless categories.blank?
categories.each do |category|
unless category.blank?
resource_categories << ResourceCategory.find(category)
end
end
end
end
#resource is instance variable of your Controller, you don't need to pass it to a method. Perform all your operations directly on the instance variable.
OP still had problem while saving the record, changed :resource_category_ids to :resource_category_ids => [] in resource_params method:
def resource_params
params.require(:resource).permit(
:title, :slug, :ancestry, :status, :author_id, :published, :parent_id,
:preview, :body, :resource_category_ids => []
)
end
I wrote the following migration:
class AddValidationsToAnimals < ActiveRecord::Migration
def change
add_index :animals, [:name, :user_id], :unique => true
end
end
Ok. Then, in my model, I add the following validation:
validates_uniqueness_of :name, :scope => :user_id
When I try to add a registry that will hurt this rule, unless get a pretty message in my view, I got a RecordNotUnique Exception.
Why? How can I fix that?
Thanks in advance.
def create
#animal = current_user.animals.new(params[:animal])
#animal.valid?
respond_to do |format|
if #animal.save
format.html { redirect_to #animal, notice: 'Animal registrado com sucesso.' }
format.json { render json: #animal, status: :created, location: #animal }
else
format.html { render action: "new" }
format.json { render json: #animal.errors, status: :unprocessable_entity }
end
end
end
Please paste your method code here. Is it a common object.save? Try to call object.valid? before and check if it's returning false.