Wants to create a new instance of books instead of update - ruby-on-rails

What I want is to create a profile pages, where i can view the previous book field and if the customer change the text then it would create a new books.
I have the following models with has_many relationship
Customer -- ID, First, Last, Email
Book -- ID, Description
Book_Managers -- ID, Customer_id, Book_id, Visible
Right now what i have is a customer edit which allow me to see multiple form by rendering from many more models like books, phones, etc...
Here my customer Controller
def edit
#customer = Customer.find(params[:id])
if #customer.books.any?
#book = #customer.books.order("created_at DESC").first
else
#book = #customer.books.build
end
end
What i would like to see is if i created a new instance when going to book form i should see the last and able to modify "The JavaScript Bible" to something "The Java Bible" and it would not update it but just create a new version. Right now when going to the form book i see nothing. And if i do for some odd reason it was only allowing me to update.
class BooksController < ApplicationController
def create
#book = current_customer.books.build(params[:book])
if #book.save
flash[:success] = "Book Created"
redirect_to root_url
else
render 'customer/edit'
end
end
def index
#books = Book.all
end
def destroy
#book.destroy
redirect_to root_url
end
end
ADDED THIS
def update
#book = current_customer.books.build(params[:book])
if #book.save
flash[:success] = "Book Updated"
redirect_to root_url
else
render 'customer/edit'
end
end
To my book controller, the only problem right now is my association, i can't seem to find any book with the current customer. is there somethign wrong with my query?

There is some gems for versioning. This that : https://www.ruby-toolbox.com/categories/Active_Record_Versioning

You can do something like this :
def update
params = params[:book].merge(:previous_version => params[:id])
#book = current_customer.books.create(params[:book])
end
It will create a new book on each update. The last version will be the book without "previous_version".

Related

Offer options if record already exists in Ruby on Rails

I'm creating a database of products and want to check when a new product is created, if that product already exists.
99% of the time, the product should have a unique product code, but sometimes this will not be the case when a revised version comes out with the same product code.
I would like to have it so that the database checks if the product code exists already. If it does, then the user can either go ahead and create the product anyway, go to the product that already exists, or cancel.
I am trying to achieve this in the controller but cannot seem to get the syntax of the exists? method correct. Can someone point out where I am going wrong, please?
def create
#product = Product.new(product_params)
if Product.product_code.exists?(#product.product_code)
render 'new'
flash[:error] = "This product already exists."
elsif #product.save
redirect_to #product
else
render 'new'
end
end
You should note that what you are doing in the controller or adding a standard uniqueness validation to the model will not allow duplicate Products to be created at all.
This will just keep sending the user back to the form:
def create
#product = Product.new(product_params)
if Product.exists?(product_code: #product.product_code)
render 'new'
flash[:error] = "This product already exists."
elsif #product.save
redirect_to #product
else
render 'new'
end
end
If you want to simply warn the user once you can attach a virtual attribute on the model and use it as condition for the validation:
class Product < ApplicationRecord
attribute :dup_warning, :boolean, default: true
validate :lax_product_code_uniquenes
def lax_product_code_uniqueness
if new_record? && !dup_warning && Product.exists(product_code: self.product_code)
errors.add(:product_code, 'is not unique - are you sure?')
self.dup_warning = true
end
end
end
Then add the virtual attribute to the form:
<%= form_with(model: #product) do |f| %>
...
<%= f.hidden_input(:dup_warning) %>
...
<% end %>
And you don't need to really do anything in the controller besides add dup_warning to the params whitelist.
def create
#product = Product.new(product_params)
if #product.save
redirect_to #product
else
render 'new'
end
end
def product_params
params.require(:product)
.permit(:foo, :bar, :product_code, :dup_warning)
end
Instead if Product.product_code.exists?(#product.product_code) possibly you should use if #product.product_code.present?
UPD: thanks to all, commented below.
Correct use of exists? is Product.where(product_code: #product.product_code).exists?

Rails overrides data from objects

when i'm trying to edit my Students and change the subjects the have, the last student i edited loses all his subjects... Can Somebody Help me? Example: Added Math to Josh. Added Math and History to Jenny. Josh's subjects are now empty.
#students_controller
def edit
#subjects = Subject.all
end
def update
#subjects = Subject.find(subjects_params)
#subjects.each do |subject|
#student.subjects << subject
#student.save
end
if #student.update(student_params)
flash[:success] = "Success"
redirect_to students_path
else
flash[:danger] = "Error"
render :new
end
end
Not clearly sure what logic must be implemented, but i think you need to add subjects_attributes to student_params method. And also add accept_nested_attributes_for :subjects in Student model
After it you can do something like this
def update
#student.update(student_params)
end
This will add needed subjects to selected student.
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html more info about nested attributes
Also you can see simple_nested_form gem which wraps all nested attributes.

Authorizing users so they can only view their own objects

I have a user model and users have many patients. A user should only be able to view their own patients.
What's the best way to implement this?
Here is my original patients show action. This allows any logged in user to view any patient:
def show
#patient = Patient.find(params[:id])
#assessments = #patient.assessments
end
I can switch it to below, but then I get an error page (patient id not found) and I'm not sure if this is the best way to handle it:
def show
#patient = current_user.patients.find(params[:id])
#assessments = #patient.assessments
end
What's the best approach for handling simple authorizations like this? Nothing fancy, just want users to only be able to view and edit their own stuff.
Edit
Here is the error I get when trying to access another user's patient:
Couldn't find Patient with 'id'=27 [WHERE "patients"."user_id" = ?]
Instead, I'd prefer to show the user a flash message saying they're not authorized.
def show
#patient = Patient.where(id: params[:id],user_id: current_user.id)
#assessments = #patient.assessments
end
here #pateint will give all the patient of the current login users & #assessments will give asssessments related to patients.
You could use find_by, which does not throw an error if nothing is found.
def show
#patient = Patient.find_by(id: params[:id])
if #patient && #patient.user == current_user
#assessments = #patient.assessments
elsif #patient.user != current_user
flash.now[:danger] = 'You are not authorized!'
else
flash.now[:danger] = 'The patient was not found'
end
end
Then you should test in your view if the patient exists.
That looks a little messy and repetitive, however, so you could refactor, for example by making a new function. Put this in the model patient.rb, and if current_user comes from application_helper, you may have to include ApplicationHelper.
authorized?
self.id == current_user.id
end
Now the conditional of the first code example could be like this:
if #patient
#assessments = #patient.assessments
unless #patient.authorized?
flash[:danger] = 'You are not authorized!'
# here, redirect the user away from the show page
end
else
flash.now[:danger] = 'The patient was not found'
end

redirecting with wrong parameter

I have an app that allows users to follow/unfollow other users. This relationship is stored in the relationships table, which has the fields id, follower_id, and followed_id. When I call the destroy method to unfollow a user, it destroys the relationship, but then tries to redirect back to the followed user by using the destroyed relationships id and not the users id. This id is/was stored in the followed_id field of the relationships table. I don't know how to trouble shoot this in rails.
Here is the relationship controller
class RelationshipsController < ApplicationController
def create
#relationship = Relationship.new
#relationship.followed_id = params[:followed_id]
#relationship.follower_id = current_user.id
if #relationship.save
redirect_to User.find params[:followed_id]
else
flash[:error] = "Couldn't Follow"
redirect_to root_url
end
end
def destroy
#relationship = Relationship.find(params[:id])
#relationship.destroy
redirect_to user_path params[:id]
end
end
replace:
redirect_to user_path params[:id]
with:
redirect_to user_path(#relationship.followed_id)
#relationship is removed from db but you still have the object in memory.
def destroy
#relationship = Relationship.find(params[:id])
#followed_user_id = #relationship.followed_id
#relationship.destroy
redirect_to user_path #followed_user_id
end
Hope this may help :)

ROR: Updating two models from one form

I have two models profiles and users on my form. After a user is created he can then move to editing his profile. The views work well. But when I click Save to update the editted profile. It doesn't update, but the flash notice displays that profile has been updated. What might be wrong? I'm not sure what went wrong. Below is the code.
class ProfilesController < ApplicationController
def new
##user.profile = Profile.new
#user = User.find(current_user.id)
#identity = #user.profile || #user.build_profile()
#identity.save
end
def update
#user = current_user
#identity = #user.profile
if #identity.update_attributes(params[:identity])
flash[:notice] = 'Profile was successfully updated.'
redirect_to(new_profile_path())
else
render :action => "new"
end
end
end
class UsersController < ApplicationController
def show
#user = current_user
#user = User.find(params[:id])
#identity = #user.profile || #user.build_profile()
#identity.save
end
......
end
Thanks for your assistance.
There are potentially a few things wrong here. But the best solution to this problem would be to simplify and use the built in rails features for editing associations.
What I suggest doing is using nested attributes, Ryan Daigle has a great article on them.
I'm not sure why you're calling save in a new action and not in a create, that doesn't feel right. Also check that the name of the model in the form you're submitting is identity and not user or profile.
Can a user exist without a profile?

Resources