I've got a problem with validators. I have a "contact" model which contains two fields firstname and lastname and I want both required on CREATE and UPDATE method. When I create a record with no data, the server return me a 422 and do the rollback. This is ok. But when I update a record the server don't return the error 422 although the server does the rollback. And I need the return of the error to manage it on the client side.
So I use validators like this :
class Contact < ActiveRecord::Base
validates :lastname, presence: true
validates :firstname, presence: true
end
and my controller is:
class ContactsController < ApplicationController
respond_to :json
def index
respond_with Contact.all
end
def create
respond_with Contact.create(contact_params)
end
def show
respond_with Contact.find(params[:id])
end
def edit
respond_with Contact.find(params[:id])
end
def update
respond_with Contact.find(params[:id]).update(contact_params)
end
def destroy
respond_with Contact.find(params[:id]).destroy
end
private
def contact_params
params.require(:contact).permit(:lastname, :firstname, :position)
end
end
I have a serializer:
class ContactSerializer < ActiveModel::Serializer
attributes :id, :lastname, :firstname, :created_at, :updated_at
end
Someone could help me, please ?
Thanks by advance.
Contact.find(params[:id]).update(contact_params)
returns a Boolean, hence you are telling Rails to render a boolean (which will render a 200 with the boolean serialized as JSON).
Same for destroy. You need to pass the instance.
def update
contact = Contact.find(params[:id])
contact.update(contact_params)
respond_with contact
end
def destroy
contact = Contact.find(params[:id])
contact.destroy
respond_with contact
end
It's also a good habit to extract the finder in a before_action.
before_action :find_contact
def update
#contact.update(contact_params)
respond_with #contact
end
def destroy
#contact.destroy
respond_with #contact
end
protected
def find_contact
#contact = Contact.find(params[:id])
end
You can refactor the other actions to remove the duplicate finder.
you can try the following code, it could be tempo fix
def update
#contact = Contact.find params[:id]
#contact.update contact_params
if #contact.errors.count > 0
# do you like
else
respond_with #contact
end
end
Related
I am creating a todo application via Ruby on Rails. I have created my own model for tags and taggings without the use of act_as_taggable gem. I have used devise for user authentication. One of my feature is to have a page which can show all of the user's tasks which is related by tags. However I have tried to change my show and index method in the tags controller to incorporate current_user but it always throws me this error
(undefined method tag for # Did you mean? tags tap):
Error
I am unable to figure out how I can edit my code to create tags within the current_user and properly process the tag_list for my current_user.
This is are the relevant codes:
Task model
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
validates :item, presence: true,
length: { minimum: 5 }
belongs_to :user
validate :due_date_cannot_be_in_the_past
def self.tagged_with(name)
Tag.find_by!(name: name).tasks
end
def tag_list
tags.map(&:name).join(" ")
end
def tag_list=(names)
self.tags = names.split(" ").map do |name|
Tag.where(name: name).first_or_create!
end
end
def due_date
due.to_s
end
def due_date=(str)
self.due = Chronic.parse(str).to_date.to_s
rescue
#invalid_date = true
end
def validate
errors.add :due, 'is not a valid date' if #invalid_date
end
def due_date_cannot_be_in_the_past
if due.past?
errors.add(:due_date, "is not a valid date")
end
end
end
Task Controller:
def index
#incomplete_tasks = Task.where(complete: false)
#complete_tasks = Task.where(complete: true)
end
def show
#task = current_user.tasks.find(params[:id])
end
def new
#task = current_user.tasks.build
end
def edit
#task = Task.find(params[:id])
end
def create
#task = current_user.tasks.build(task_params)
if #task.save
redirect_to #task
else
render 'new'
end
end
def update
#task = Task.find(params[:id])
if #task.update(task_params)
redirect_to #task
else
render 'edit'
end
end
def destroy
#task = Task.find(params[:id])
#task.destroy
redirect_to tasks_path
end
def complete
#task = current_user.tasks.find(params[:id])
#task.update_attribute(:complete, true)
flash[:notice] = "Task Marked as Complete"
redirect_to tasks_path
end
private
def task_params
params.require(:task).permit(:item, :description, :tag_list, :due)
end
end
Tags Controller:
def show
#tag = current_user.tag.find(params[:id])
end
def index
#tag = current_user.tag.all
end
Please do let me know if any other information is needed to make this problem clearer.
if I have a model that looks like the following:
class Post < ActiveRecord::Base
validate :content, presence: true, on: :post_create_action
end
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
if #post.validate(:post_index_action) && #post.save
redirect_to post_path(#post)
end
end
end
I know the #post.validate doesn't work as I've described in the code, but I'm wondering if this is possible in rails.
It's possible, and it's called validation context:
http://api.rubyonrails.org/classes/ActiveModel/Validations.html#method-i-valid-3F
You can acheive this functionality using lambda
models/post.rb
class Post < ActiveRecord::Base
attr_accessor :post_creation
validate :content, presence: true, if: lambda { self.post_creation == true }
end
controllers/posts_controller.rb
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
if #post.save
redirect_to post_path(#post)
else
# Handle the validation errors
end
end
private
def post_params
params.require(:post).permit(....).merge(post_creation: true)
end
end
I have a "generic" controller in charge of managing all public pages and actions
class PublicController < ApplicationController
def index
end
def contact
#contact = Contact.new(contact_params)
end
private
def contact_params
params.require(:contact).permit(:name, :email, :question, :subject)
end
end
But when I want to access the "contact us" link I have the following error
param is missing or the value is empty: contact
Is it possible to operate strong parameters inside a "generic" controller or should I only use them as part of a controller named "Contact" ?
That looks like the error is because you've not got a contact parameter in the parameters hash. You want something more like the following:
def contact
#contact = Contact.new
end
def send_contact
#contact = Contact.new(contact_params)
end
private
def contact_params
params.require(:contact).permit(:name, :email, :question, :subject)
end
Or
def index
#contact = Contact.new
end
def contact
#contact = Contact.new(contact_params)
end
private
def contact_params
params.require(:contact).permit(:name, :email, :question, :subject)
end
Essentially you should only be calling contact_params on the action you're posting to.
I am new in rails and I am getting ActiveModel::ForbiddenAttributesError. This is my controller for nested resource:
class PostsController < ApplicationController
respond_to :html, :xml, :json
def index
#post=Post.all
end
def new
#user = User.find(params[:user_id])
#post = #user.posts.build
respond_with(#post)
end
def create
debugger
#user = User.find(params[:user_id])
#post = #user.posts.build(params[:post])
if #post.save
redirect_to user_posts_path
else
render 'new'
end
end
def post_params
params.require(:post).permit(:title, :description, {:user_ids =>[]})
end
end
If you have the following models:
post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
has_many :posts
end
You don't actually need to use nested attributes, just do the following in your controller:
class PostsController < ApplicationController
respond_to :html, :xml, :json
def index
#post = Post.all
end
def new
#post = Post.new
respond_with(#post)
end
def create
#user = User.find(params[:user_id])
#post = #user.posts.new(post_params) # This will automatically set user_id in the post object
if #post.save
redirect_to user_posts_path
else
render 'new'
end
end
def post_params
params.require(:post).permit(:title, :description)
end
end
Although I don't recommend that you user a user_id param in the url. Have a look at the devise gem to handle authentication.
I have an issue with my database in a Rails app.
The problem is when I press delete on an uploaded image that it raises the error in the browser, shown below.
What is confusing me is:
A) When I check the items using the Rails console, it shows the item has been deleted.
B) The error refers to items that never existed, ie, id=3 when there were only ever two items present.
Error
ActiveRecord::RecordNotFound in PostsController#destroy
Couldn't find Post with id=4
Extracted source (around line #24):
def destroy
#post = Post.find params[:id]
#post.destroy
redirect_to 'posts/'
end
Posts Controller
class PostsController < ApplicationController
def index
#posts = Post.all
end
def new
authenticate_user!
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to '/posts'
else
render 'new'
end
end
def destroy
#post = Post.find params[:id]
#post.destroy
redirect_to 'posts/'
end
private
def post_params
params.require(:post).permit(:description, :picture)
end
end
Post Model
class Post < ActiveRecord::Base
has_attached_file :picture, styles: { medium: "300x300>", thumb: "100x100>" }
validates :description, presence: true
validates_attachment_content_type :picture, content_type: ["image/jpg", "image/jpeg", "image/png"]
has_many :comments
end
Create Posts Migrate
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.text :description
t.timestamps
end
end
end
Not sure what exactly is going wrong, but it seems to be raising an error on a method that is to all intents and purposes working just fine and also something appears to be going wrong with the id assignment in the database.
Try this:--
In Posts Controller
def destroy
#post = Post.find params[:id]
#post.destroy
redirect_to '/posts'
end
If above doesnot work then try the below
def destroy
#post = Post.find params[:id]
#post.destroy
redirect_to posts_path
end