I'am using decent_exposure gem and cocoon gem for nested forms and after each create or update I am getting duplicate entries. What am I doing wrong? X_X
Controller
class Admin::DealsController < ApplicationController
expose :deals
expose :deal, attributes: :deal_params
def create
if deal.save
redirect_to [:admin, deal], notice: 'Deal was successfully created.'
else
render :new
end
end
def update
if deal.update(deal_params)
redirect_to [:admin, deal], notice: 'Deal was successfully updated.'
else
render :edit
end
end
private
def deal_params
params.require(:deal).permit :title, :end_date,
deal_variants_attributes: [:id, :title, :description, :price, :discount, :_destroy]
end
end
Model
class Deal < ActiveRecord::Base
has_many :deal_variants
accepts_nested_attributes_for :deal_variants, reject_if: :all_blank, allow_destroy: true
end
Thanks in advance!
I met the same problem today and here is my solution and explanation:
This happens because decent_exposure already sets required attributes for you, and when you call .update(deal_params) it duplicates entries since first instance of DealVariant was set by decent_exposure, and second one was set by .update(deal_params) call. What you need to do instead is to use simple .save method in both create and update actions.
Related
I'm building an application where I have used nested attributes to store different option records under a question record. There is a form where the user can dynamically add and remove options. Everything works fine in my create action, but in the update action, if I remove an existing option and submit the form, it is not deleted from the database.
When updating the question record, is there any way to completely overwrite the existing nested parameters and replace it with the ones we pass in the update request?
I understand that adding _destroy to the attributes and passing it as a parameter would satisfy my requirement here. Since I'm deleting the option information from my frontend state on press of a "remove" button in the UI, I'm not sending it along with the params. Is there any other method in Rails to completely overwrite nested attributes and delete those nested records which are not passed in the update request, from the update action in the controller itself?
question.rb
class Question < ApplicationRecord
belongs_to :quiz
has_many :options
validates :body, presence: true
accepts_nested_attributes_for :options
end
option.rb
class Option < ApplicationRecord
belongs_to :question
validates :body, presence: true
validates :is_correct, inclusion: { in: [ true, false ], message: "must be true or false" }
end
questions_controller.rb
class QuestionsController < ApplicationController
...
def update
#question = Question.find_by(id: params[:id])
if #question.update(question_params)
render status: :ok, json: { notice: t("question.successfully_updated") }
else
render status: :unprocessable_entity, json: { error: #question.errors.full_messages.to_sentence }
end
end
...
private
def question_params
params.require(:question).permit(:body, :quiz_id, options_attributes: [:id, :body, :is_correct])
end
Relevant question
If I understand you correctly you're deleting the options one by one by clicking a button next to the option. Thats not actually something you need or want to use nested attributes for. Nested attributes is only relevant when you're creating/editing multiple records at once.
While you can destroy a single nested record by updating the parent:
patch '/questions/1', params: {
question: { options_attributes: [{ id: 1, _destroy: true }] }
}
Its very clunky and not really a good RESTful design.
Instead you can just setup a standard destroy action:
# config/routes.rb
resources :options, only: :destroy
<%= button_to 'Destroy option', option, method: :delete %>
class OptionsController < ApplicationController
# #todo authenticate the user and
# authorize that they should be allowed to destroy the option
# DELETE /options/1
def destroy
#option = Option.find(params[:id])
#option.destroy
respond_to do |format|
format.html { redirect_to #option.question, notice: 'Option destroyed' }
format.json { head :no_content }
end
end
end
This uses the correct HTTP verb (DELETE instead of PATCH) and clearly conveys what you're doing.
I can share my recent project work which is a bit similar to your where I am using shrine gem for upload images and I can update/destroy images which is associated with a Product model
product.rb
.
.
has_many :product_images, dependent: :destroy
accepts_nested_attributes_for :product_images, allow_destroy: true
product_image.rb
.
belongs_to :product
.
_form.html.erb for update
<%= f.hidden_field(:id, value: f.object.id) %>
<%= image_tag f.object.image_url unless f.object.image_url.nil? %>
<%= f.check_box :_destroy %>
and in products controller,I have whitelisted this
product_images_attributes: [:_destroy,:image, :id]
Hope this helps you to solve on your case
I hope I'm forming this question correctly. But I'm trying to create a location model that has a geocoded address and that address is able to be located on a map using gmap for rails.
My issue is associating my Location model with other application models. Currently I'm attempting to have a location field within my post form.
My application is as followed using the geocoder and gmaps for rails gems.
class Location < ActiveRecord::Base
belongs_to :locatable, polymorphic: true
belongs_to :post
geocoded_by :address
acts_as_gmappable
after_validation :geocode
validates :address, presence: true
validates :latitude, presence: true
validates :longitude, presence: true
def gmaps4rails_address
"#{address}"
end
validates :locatable, presence: true
end
Post model
class Post < ActiveRecord::Base
has_one :location, as: :locatable , :dependent => :destroy
accepts_nested_attributes_for :locations,:reject_if =>:all_blank ,allow_destroy: true
end
Controllers
Post Controller
def new
#post= current_user.posts.build
end
def create
#post = current_user.posts.new(post_params)
#post = Location.new
if #post.save
redirect_to root_path
else
render 'new'
flash[:error] = !"
end
end
def post_params
params.require(:post).permit(locations_attributes[:address,:latitude,:longitude, :locatable_id, :locatable_type, :gmaps])
end
Locations Controller
class LocationsController < ApplicationController
def new
#location = Location.new
end
def create
#location = Location.new(params[:location])
end
def destroy
#location.destroy
respond_to do |format|
format.html { redirect_to locations_url, notice: 'Location was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def location_params
params.require(:location).permit(:address, :latitude, :longitude, :locatable_id, :locatable_type, :gmaps)
end
end
My code might be snippet out but my overall issue is happening within the controller I have everything going right. The location field within my post form is a nested form of the model location, the address field is geocoded and has a location search for the address input. My problem arises when submitting the post, I've tried several methods but I cannot get the location to save to post model. Any help would be amazingly helpful.
I am brand new here. I have been fighting with a rails app for hours now and need an answer. I have searched and tried many suggestions related to what I am trying to accomplish, but to no avail. I got the paperclip gem running for my rails app yesterday, and it was a breeze to add an attachment to a single model. However, I defined an agnostic, polymorphic attachments table to hold attached files for all models that need this functionality.
My issue is that I cannot get the attached file to save through nested parameters. All my parameters are accepted, but the db rolls back and doesn't save (using guard). Message is: 'attachments.attachable_id'=>'can't be blank'. I need this to be the foreign key of the related table, and this has to be saved along with the attachment_type. Here's what I have:
class ReportsController < ApplicationController
def new
#report = Report.new
#report.attachments.build(attachable_id: #report.id)
end
def create
#report = Report.new(params)
#report.attachments.build
respond_to do |format|
if #report.save
format.html { redirect_to #report, notice: 'Report was successfully created.' }
format.json { render json: #report, status: :created, location: #report }
else
format.html { render action: "new" }
format.json { render json: #report.errors, status: :unprocessable_entity }
end
end
end
private
def report_params
params.require(:report).permit(:filing_year, :filing_number, :order_number, :location, :environmental_review,:biological_review, :cultural_review, :date_received, :status, attachments_attributes: [:id, :attachable_id, :attachable_type, :attachment])
end
end
And for the models:
class Attachment < ActiveRecord::Base
belongs_to :attachable, polymorphic: true
validates :attachable_id, :attachable_type, presence: true
do_not_validate_attachment_file_type :attachment
Paperclip.interpolates :attached_to do |attachment, style|
attachment.instance.attachable.class.to_s.downcase
end
has_attached_file :attachment,
:url => "/attachments/:id/:basename.:extension",
:path => ":rails_root/public/attachments/:attached_to/:id/:basename.:extension",
:default_url => "/attachments/original/no-file.txt"
end
class Report < ActiveRecord::Base
has_one :environmental_review
has_many :attachments, as: :attachable
accepts_nested_attributes_for :attachments
validates :order_number, presence: true
.
.
.
end
And view (in slim):
.report
= form_for #report do |f|
.
.
.
= f.fields_for :attachments do |a|
= a.file_field :attachment
.
.
.
Thank you.
Well for one in your create method you call #reports.attachments.build again, but don't set the assignable_id, you need to do that. The other thing you can do is add a hidden form field that has the name attachable_id.
I'm stumped on my Rails app. I've created 3 models: user, event, category that have the following associations:
class User
has_many :events, :dependent => :destroy
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
class Category
attr_accessible :name
has_many :events
class Event
attr_accessible :address, :cost, :date, :details, :end_time, :fav, :start_time, :title, :venue
belongs_to :user
belongs_to :category, :foreign_key => :name #not sure if this foreign key is working
The idea is that Users create events that are filed under a single category. The categories are pre-populated so users don't get to make them, only choose which to file under.
My user and events association has been working for a while, but since I've added the category model I get a "Can't mass-assign protected attributes: category" error when I try to create a new event.
I've been browsing pages all day and can't seem to track the error down. I tried adding a foreign key using belongs_to :category, :foreign_key => :name in the Event class but that didn't seem to help.
Would graciously appreciate any help, solutions, and/or pointers in the right direction!
Edit 2: I'm pretty new at Rails, but I think I've tracked down where the problem is from the error screen. Says "ActiveModel::MassAssignmentSecurity::Error in EventsController#create" and further down says "app/controllers/events_controller.rb:58:in create" which equates to this line of code: #event = current_user.events.new(params[:event]).
If I'm reading it correctly, that would mean the error occurs because I'm trying to create a new event with a category param passed in the hash and it doesn't know what to do with it. Unfortunately, I don't know what to do either...
Edit 3: As requested, here's the Event controller's create action:
def create
#event = current_user.events.new(params[:event])
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render json: #event, status: :created, location: #event }
else
format.html { render action: "new" }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
Place the :foreign_key => :name in class User OR provide attr_accessible to class User
Add :category to attr_accessible in Event model.
And using name as foreign key is a bad idea, becouse it isn't unique
For create event with relation with user and category you can do:
user.events.build do |event|
event.category = category
end
or
category.events.build do |event|
event.user =user
end
Also, you can use method create
I'm having a regular for with checkboxes for my has_many :through relation. My problem is that I can't create a new project and have checkboxes checked. I get a validation error "Users is invalid". This is really weird.
If I create a project with no user checked it works and I can check them when I'm editing the project.
- User.each do |user|
%label.checkbox{title: user.email}
= check_box_tag 'project[user_ids][]', user.id, #project.user_ids.include?(user.id)
= truncate(user.full_name, length: 16)
So in short: I can edit projects but not create new ones. Any ideas?
EDIT:
I have three models, User, Project, Projectship where the latest is the relation between the others. It's when I'm trying to create a project and pass user relations to it my problem occurs. When editing everything works like a charm.
User
id
email
has_many :projectships, dependent: :destroy
has_many :projects, through: :projectships
Project
id
name
has_many :projectships, dependent: :destroy
has_many :users, through: :projectships
Projectship
id
user_id
project_id
belongs_to :project
belongs_to :user
validates :project_id, presence: true
validates :user_id, presence: true
ProjectsController:
# GET /projects/new
def new
#project = Project.new
end
# POST /projects
def create
#project = Project.new(project_params)
if #project.save
redirect_to #project, notice: t('flash.project_created')
else
render :new
end
end
# GET /projects/:id/edit
def edit
#project = Project.includes(:users).find(params[:id])
end
# PUT /projects/:id
def update
#project = Project.find(params[:id])
if #project.update_attributes(project_params)
redirect_to :back, notice: t('flash.project_updated')
else
render :edit
end
end
private
def project_params
params.require(:project).permit(
:client_id, :currency, :description, :end_date, :estimated_hours,
:fixed_price, :hourly_rate, :name, :start_date, :status,
:billable_type, :user_ids
)
end
P.S I'm using 4.0.0.beta D.S
To store users_ids the model needs to be saved and that's way it didn't work. So now I store the ids in the create action and than add them directly after I save the project.