Rails store to different tables within the same controller - ruby-on-rails

I have two models, Livestock and History
a livestock has many histories and history belongs to livestock
This is the create method inside the LivestockController
# POST /livestocks
# POST /livestocks.json
def create
#livestock = Livestock.new(livestock_params.permit!)
respond_to do |format|
if #livestock.save
format.html { redirect_to #livestock }
flash[:success] = "Livestock was successfully created"
format.json { render :show, status: :created, location: #livestock }
else
format.html { render :new }
format.json { render json: #livestock.errors, status: :unprocessable_entity }
end
end
end
I wanted to create a record in the histories table with
history = History.new(livestock_id: #livestock.id, event: "Purchased", event_date: #livestock.purchase_date, image: #livestock.image)
history.save!
inside the create method
How can I do it? I can't put it in the create method because it says
Validation failed: Livestock must exist
apparently #livestock has not yet have the id attribute
Edit:
it still raises the same exception when I put it after
if #livestock.save
However I found a work around by using the session variable. Since it is redirected to the show page, I created the following inside the create method
session[:created] = "created"
And in my show method
# GET /livestocks/1
# GET /livestocks/1.json
def show
if session[:created] == "created"
history = History.new(livestock_id: params[:id], event: "Purchased", event_date: #livestock.purchase_date, image: #livestock.image)
history.save!
session.delete(:created)
end
end
Now I am wondering what are consequences if I use this approach.

Livestock record is created when you call save (and there is no validation error). So one option is to create the history inside this if condition:
if #livestock.save
Another option is to use after_create callback in the livestock model that will create a history object right after creating livestock. You have to be careful because the callback might be called when you do not need it (i.e. when importing data).
The last option is to create a separate service object that will create livestock and all other required objects. That's probably the best approach, but it will require more customized code.
Update
Please also make sure to move if/else block outside the respond_to block:
if #livestock.save
# create history object here
respond_to do |format|
format.html { redirect_to #livestock }
flash[:success] = "Livestock was successfully created"
format.json { render :show, status: :created, location: #livestock}
end
else
respond_to do |format|
format.html { render :new }
format.json { render json: #livestock.errors, status: :unprocessable_entity }
end
end

Related

Controller to add current_user email to database in rails

I have a column in my database that I'm trying to update in my controller. I'm trying to take the current logged in user's email and send it to the column in the model using the create method. It's not sending the email to the database though.
Here's my create method in the controller
def create
#request = Request.new(request_params)
respond_to do |format|
if #request.save
format.html { redirect_to #request, notice: 'Request was successfully created.' }
format.json { render :show, status: :created, location: #request }
#request.email = current_user.email
#request.increment!(:voteCount)
else
format.html { render :new }
format.json { render json: #request.errors, status: :unprocessable_entity }
end
end
end
I'm fairly certain the trouble lies in the line #request.email = current_user.email but Im not sure why.
The user model and the request models are different, so im trying to grab the data from one model and send it to another.
Sergio's comment helped me figure this out.
Added #request.save

Rails Pundit unable to find policy of nil only on Create action after adding restrictions on some params

I've modified one of my Goal.rb model to only allow one field to be edited by admins. This model and all actions within it was working prior to this update. This update also works for the edit action, but on the create action returns
Pundit::NotDefinedError in GoalsController#create
unable to find policy of nil
I'm hoping this is just a syntax error, I don't understand why it's not working as no model/policy file names have been changed, only the params and policy settings (which work for the edit action)
I thought it might be because I'm calling the policy on #goal while creating it, but how else would I check permissions there, if not this way?
goals_controller.rb:
def create
#goal = Goal.new(permitted_attributes(#goal))
#stuff
authorize #goal
respond_to do |format|
if #goal.save
# format.json { render :show, status: :created, location: #goal }
else
# format.json { render json: #goal.errors, status: :unprocessable_entity }
end
end
end
def update
authorize #goal
update_params = permitted_attributes(#goal)
#
end
respond_to do |format|
if #goal.update(update_params)
# format.json { render :show, status: :ok, location: #goal }
else
# format.json { render json: #goal.errors, status: :unprocessable_entity }
end
end
end
I just realised, you can call permissions on a new class. I had read this three times, but only after posting the question did it click.
I was trying to check the allowed params of #goal, which didn't yet exist and was showing a nil policy, but with Pundit you can run a check against the allowed params of a Class, in this case:
#goal = Goal.new(permitted_attributes(Goal))

Editing Triple Nested Fields is Creating Duplicates Rails 5.1

I have a triple nested resource, which I am able to create new values for perfectly well. However when trying to edit the record, I am getting duplicated fields for the nested values, which is then creating multiple entries.
I am multiplying the nested fields by 3.
def new
#roast = Roast.new
3.times {#roast.countries.build.regions.build}
end
Edit method:
def edit
#roast = Roast.friendly.find(params[:id])
3.times {#roast.countries.build.regions.build}
end
Should I be removing the 'build' element here? I do want the user to be able to add new values if required however.
And create has nothing special for this:
def create
#roast = Roast.new(roast_params)
respond_to do |format|
if #roast.save
format.html { redirect_to #roast, notice: 'Roast was successfully created.' }
format.json { render :show, status: :created, location: #roast }
else
format.html { render :new }
format.json { render json: #roast.errors, status: :unprocessable_entity }
end
end
end
I obviously want the 3 nested fields to show on the edit page, but what am I doing wrong for it to keep repeating.
You don't need to add countries or regions in the edit. Just find the Roast.
#This is wrong
3.times {#roast.countries.build.regions.build}
When you edit a Roast, you can access its countries through #roast.countries
If you want, you can define an instance variable to use in the form (although not needed) #countries = #roast.countries

Create more than one object at once using the standard create method in Ruby on Rails

I am trying to use the standard create method created for Ruby/Rails projects and simply pass in an additional form field that tells the method how many objects to create (vs just creating one object). The standard create method looks like so:
def create
#micropost = Micropost.new(micropost_params)
respond_to do |format|
if #micropost.save
format.html { redirect_to #micropost, notice: 'Micropost was successfully created.' }
format.json { render :show, status: :created, location: #micropost }
else
format.html { render :new }
format.json { render json: #micropost.errors, status: :unprocessable_entity }
end
end
end
I want to pass in an additional data (form field called number_to_create) which tells the method how many of the microposts to create. I just added a new form field like this, in addition to the other micropost form field params:
<%= text_field_tag :number_to_create %>
My question is how do I modify the create method code such that it creates N number of micropost objects vs. just one. So if I pass in 3 from the form along with the other micropost attributes, the method creates 3 identical micropost objects, not just one as it currently does.
Thanks in advance for your help on this.
You could use the param as times
#microposts = Micropost.transaction do
[].tap do |microposts|
param[:number_to_create].times do
microposts << Micropost.create(micropost_params)
end
end
end
respond_to do |format|
if #microposts.all? &:persisted?
format.html { redirect_to #micropost, notice: 'Micropost was successfully created.' }
format.json { render :show, status: :created, location: #micropost }
else
format.html { render :new }
format.json { render json: #micropost.errors, status: :unprocessable_entity }
end
end
The transaction block is to make sure that either all of them gets saved, or none of them gets saved, this way you can fix your errors and recreate them without worrying of getting any stray saved objects

Using .dup and .update_attributes to generate a model instance is causing creation of extra instances upon failing validation

I have a validation for the Activity model and this is the create action in the activities_controller. When I try to save the model without meeting the validations, an extra activity instance is being created. Why is an activity being created when it fails the validations?
def create
#activity_last = Activity.find(params[:activity_id])
#activity = #activity_last.dup
#activity.activity_date = Time.now
respond_to do |format|
if #activity.update_attributes(params[:activity])
format.html { redirect_to #activity.tenant, notice: 'Activity was successfully created.' }
format.json { render json: #activity.tenant, status: :created, location: #activity.tenant }
else
format.html { render action: "new" }
format.json { render json: #activity.errors, status: :unprocessable_entity }
end
end
end
The reason I am using dup here is because I want to create a duplicate of the latest activity and then update a subset of its attributes from the params. So for explanation sake, say Activity had 10 attributes. The user would go to a form that has field for 3 of these attributes. I then want to create a new activity that has all the same attribute values as the latest activity for the attributes not being updated by the user.
--Update--
I have uncovered why this was happening. I shortened the code above so that there would be less clutter but as part of that shortening I cut out the following lines:
#comment = Comment.new(params[:comment])
#comment.user = current_user
#comment.activity = #activity
#comment.save!
The app works correctly when I move the saving of the comment to after the saving of the activity. My guess is that saving a comment that is linked to an activity that hasn't been saved causes the activity to be saved?

Resources