Rails form rendering multiple times if using `.build` - ruby-on-rails

So I have a user form form_for that accepts nested attributes from account_prices. Whats happening is on my user controller I have this private method.
def pre_build_user_account_prices
if #user.account_prices.empty?
#accountable_default = #user.account_prices.build(status: 'default')
#accountable_temporary = #user.account_prices.build(status: 'temporary')
else
#accountable_default = #user.account_prices.where(status: 'default')
#accountable_temporary = #user.account_prices.where(status: 'temporary')
end
end
reason for the condition is, if I don't do a check here it will render 2 forms. an empty form and with data form. So checking is a need here
but my problem is this.Im on edit route, and when I try to submit an invalid form it renders multiple empty forms. heres an image.
if I kept submitting invalid form it will render multiple times. I was thinking if checking through JS if theres a multiple child I will remove it. is that the best approach?
here's my asscociations
Class User
has_many :account_prices, as: :accountable, autosave: true
accepts_nested_attributes_for :account_prices
end
polymorphic
class AccountPrice
enum status: {default: 'default', temporary: 'temporary'}
validates :accountable, presence: true
validates :status, presence: true
validates :exp_start_date, presence: true, if: :is_temporary_status?
validates :exp_end_date, presence: true, if: :is_temporary_status?
belongs_to :accountable, polymorphic: true
belongs_to :variant_price_set, class_name: "Spree::VariantPriceSet"
belongs_to :shipping_method_price_set, class_name: "Spree::ShippingMethodPriceSet"
def is_temporary_status?
status == 'temporary'
end
end
user controller
Class UsersController
before_action :pre_build_user_account_prices, only: :edit
def update
if #user.update_attributes(user_params)
flash.now[:success] = Spree.t(:account_updated)
redirect_to show_admin_user_path(#user)
else
render :edit
end
end
def pre_build_user_account_prices
if #user.account_prices.empty?
#accountable_default = #user.account_prices.build(status: 'default')
#accountable_temporary = #user.account_prices.build(status: 'temporary')
else
#accountable_default = #user.account_prices.where(status: 'default')
#accountable_temporary = #user.account_prices.where(status: 'temporary')
end
end
end

I guess when you're trying #user.account_prices.where(... it is reading data which is not persisted to the db too. I mean the account_prices which was just build in the previous step.
Try,
def pre_build_user_account_prices
if #user.account_prices.empty?
#accountable_default = #user.account_prices.build(status: 'default')
#accountable_temporary = #user.account_prices.build(status: 'temporary')
else
#accountable_default = #user.reload.account_prices.where(status: 'default')
#accountable_temporary = #user.reload.account_prices.where(status: 'temporary')
end
end
reload, reloads the object's attributes from the database
Also, when using accept_nested_attributes for to update an existing entry, you need to make sure that id of the nested object for which you're accepting nested attribute for is part of the attributes passed.

Related

How to use before_save (with params) to check if a record exist

I would like to check if a record exist before_save, what's the best way tod do that ?
def create
#step = Step.new(step_params)
#course = Course.find(step_params[:course_id])
redirect_to course_path(#course) and return if step_already_present?(#step)
if #step.save
redirect_to course_path(#course.id)
else
render :new
end
end
The method to check :
def step_already_present?(step)
Step.where(poi_start_id: step.poi_start_id, course_id: step.course_id).first.present?
end
You can use the uniqueness validation on the Model
If you need to check the two columns together you can use the scope option like this:
class Step < ActiveRecord::Base
validates :poi_start_id, uniqueness: { scope: :course_id }
end

Rails single table inheritance validation

There is a Request model in my app. On different pages I need different validations, for example on /contacts I need to validate a lot of fields, whereas in a 'call me back later' popup I need to validate only phone number and name.
My problem is: data is saved, but without validations and type is not saved aswell.
Structure:
request.rb
class Request < ApplicationRecord
self.inheritance_column = :_type_disabled
def self.types
%w(ContactRequest CallMeBackRequest)
end
scope :contacts, -> { where(type: 'ContactRequest') }
scope :callmebacks, -> { where(type: 'CallMeBackRequest') }
end
routes.rb:
resources :contact_requests, only: [:new, :create], controller: 'requests', type: 'ContactRequest'
resources :call_me_back_requests, only: [:new, :create], controller: 'requests', type: 'CallMeBackRequest'
contact_request.rb:
class ContactRequest < Request
validates :name, :phone, :email, :company_name, presence: true
def self.sti_name
"ContactRequest"
end
end
call_me_back_request.rb:
class CallMeBackRequest < Request
validates :name, :phone, presence: true
def self.sti_name
"CallMeBack"
end
end
requests_controller.rb:
class Front::RequestsController < FrontController
before_action :set_type
def create
#request = Request.new(request_params)
respond_to do |format|
if #request.save
format.js
else
format.js { render partial: 'fail' }
end
end
end
private
def set_request
#request = type_class.find(params[:id])
end
def set_type
#type = type
end
def type
Request.types.include?(params[:type]) ? params[:type] : "Request"
end
def type_class
type.constantize
end
def request_params
params.require(type.underscore.to_sym).permit(Request.attribute_names.map(&:to_sym))
end
end
My form starts with:
=form_for Request.contacts.new, format: 'js', html: {class: 'g-contact__sidebar-right g-form'}, remote: true do |f|
I tried using ContactRequest.new - result was the same.
What I get when I hit the console:
Request.contacts.create!(name: "something") - does get saved, no validations are applied (why?). No type field is populated - why?
ContactRequest.create!(name: "something") - does not get saved, validations are applied
ContactRequest.create!(name: ..., all other required fields) - does get saved, but field type is empty - why?
Whatever I use for my form - ContactRequest.new or Request.contacts.new - neither validations are applied nor field type is set correctly.
Can anyone point me in the right direction? I'm mainly using this tutorial and other SO question, but without success.
Figured it out - since I'm not using the dedicated pages and paths for those contacts, i.e. contact_requests_path and corresponding new.html.haml, I need to pass the type parameter as a hidden field.
So my form now looks like this:
=form_for ContactRequest.new, format: 'js', html: {class: 'g-contact__sidebar-right g-form'}, remote: true do |f|
=f.hidden_field :type, value: "ContactRequest"
Considering validations - I don't know what I did, but after restarting the server a few times, they work now. The only this I remember really changing was the sti name here:
class CallMeBackRequest < Request
validates :name, :phone, presence: true
def self.sti_name
"CallMeBack" <- changed it from "CallMeBack" to "CallMeBackRequest"
end
end

ruby on rails: instance variable returns nil

I've been trying to push the input data from my form to the database using the create function, but the instance variable in create function keeps returning 'nil' and after the 'begin transaction', it does 'rollback transaction'
The model function works fine as I get the desired parsed data, and so does the timetables_params function. But timetables_params[:start_time] always returns 'nil' even though the timetables_params returns all the start_time values and end_time values.
How can I fix this?
Here is my controller
def index
#user = current_user
#timetables = #user.timetable.all
end
def new
#timetable = Timetable.new
end
def create
timetables_params[:start_time] = Timetable.parse_timetable_time(timetables_params, 'start')
timetables_params[:end_time] = Timetable.parse_timetable_time(timetables_params, 'end')
#timetable = Timetable.create(timetables_params)
if #timetable.save
flash[:success] = "Done"
else
render 'new'
end
end
private
def timetables_params
params.require(:timetable).permit(:user_id, :start_time, :end_time)
end
end
Here is my model
belongs_to :user
attr_accessor :user, :start_time, :end_time
def self.parse_timetable_time(hash, type)
date_string = hash["#{type}_time(1i)"].to_s + "=" + hash["#{type}_time(2i)"].to_s + "=" + hash["#{type}_time(3i)"]
Time.parse(date_string)
end
You probably have not set the user since its belongs_to :user and i dont see it anywhere in your code. That's why its rolling back and you probably required user_id in your Timetable model. My suggestion is below
Instead of:
#timetable = Timetable.create(timetables_params)
Use build:
#timetable = current_user.timetables.build(timetables_params)
Thanks for all the help above, but turns out that I just had to add the following lines in the model and modify the controller create function:
model
validates :user_id, presence: true
validates :start_time, :end_time, presence: true
controller
def create
#timetable = current_user.timetables.build(timetables_params)
if #timetable.save
flash[:success] = "Done"
else
render 'new'
end
And, I don't necessarily need to parse the datetime values, Rails does do it automatically

ruby on rails - undefined method valid?

im following ryan bates screen cast on how http://railscasts.com/episodes/219-active-model on how to validate a form without a database
but i keep getting an undefined method valid?
heres my controller
def create
#contacts = FreshDeskApiWrapper.new().post_tickets(params[:contacts])
if #contacts.valid?
redirect_to new_contact_path
else
flash[:notice] = "OOps"
render action: 'new'
end
end
I can seem to call
$ FreshDeskApiWrapper.new().valid?
just fine in the console but it does not seem to like it when i tack on the
$ FreshDeskApiWrapper.new().post_tickets(params[email: 'user#example.com']).valid?
i get an undefined method valid?
There is something im not understanding about this
heres my fresh_desk_api_wrapper.rb file i created in my models folder
class FreshDeskApiWrapper
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :config, :client, :subject, :email, :custom_field_phone_number_50754, :custom_field_company_50754, :description
validates :subject, presence: true
validates :email, presence: true
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
self.config = YAML.load_file("#{Rails.root}/config/fresh_desk.yml")[Rails.env]
self.client = Freshdesk.new(config[:url], config[:api_key], config[:password])
end
def post_tickets(params)
client.post_tickets(params)
end
def persisted?
false
end
end
post_tickets is something im defining in there
You can call valid? on an single instance of an object, not multiple. #contacts would imply that your post_tickets method is returning multiple objects.
try something like this:
#contacts = FreshDeskApiWrapper.new(post_tickets(params[:contacts])
what seems to be the problem is that the method you are adding dosnt return a active record object, so the method valid? is not available
Edit:
maybe this:
#contacts = FreshDeskApiWrapper.new(FreshDeskApiWrapper.post_tickets(params[:contacts])
omg im so dumb so what i did was
def create
#contacts = FreshDeskApiWrapper.new(params[:contacts])
#contacts.post_tickets(params[:contacts])
if #contacts.valid?
redirect_to new_contact_path
else
flash[:notice] = "OOps"
render action: 'new'
end
end
and it works!
Im still struggling to learn all this.....thanks for your guy's guidance it really helped

Nested Form: "Cannot call create unless the parent is saved" error

I have a nested form where each post has and belongs to many locations. Adding locations to a post with the nested form works just fine. However when I click 'edit' and change anything except a location I get passed the cannot call create unless the parent is saved error.
I'm guessing this is something to do with the bit of code in my model which runs through the location_attributes being submitted and checks them for uniqueness however I have no idea how to fix this. I have tried just #post.save! before I do find_or_initialize_by_name in the controller but get the same error.
Code is below. I know this is pretty unique to me but any suggestions would be great!
posts_controller.rb
...
def update
location_set = params[:post].delete(:locations_attributes) unless params[:post][:locations_attributes].blank?
#post = Post.find(params[:id])
#post.locations = Location.find_or_initialize_location_set(location_set) unless location_set.nil?
if #post.update_attributes(params[:post])
redirect_to #post, notice: 'Blog post was successfully updated.'
else
render action: "edit"
end
end
...
location.rb (model)
include PublicActivity::Model
tracked
tracked owner: Proc.new{ |controller, model| controller.current_user }
include FriendlyId
friendly_id :name
after_save { |location| location.destroy if location.name.blank? }
after_save { |venue| venue.destroy if venue.name.blank? }
has_many :location_post, :order => "position"
has_many :posts, :through => :location_post, :order => 'location_posts.position'
attr_accessible :latitude, :longitude, :name, :post_id, :notes, :asset, :assets_attributes, :venues_attributes
attr_accessor :_destroy, :position, :location_post_id
def self.find_or_initialize_location_set(location_set)
#create a locations array
locations = []
locations = locations.delete_if { |elem| elem.flatten.empty? }
location_set.each do |key, location|
if location.delete(:_destroy) == "1"
locations.delete_if {|elem| elem[:name] == location[:name]}
else
locations << find_or_initialize_by_name(location)
#REMINDER In rails 4 this must be written as where(...).first_or_create
end
end
locations
end
*EDIT - The Error *
app/models/location.rb:10:in `block in <class:Location>'
app/controllers/blogit/posts_controller.rb:97:in `update'
This was caused by a stupid mistake.
The line after_save { |venue| venue.destroy if venue.name.blank? } is no longer relevant.
I'm an idiot and didn't read the error properly. Thanks for all those who helped.

Resources