Rails 4 - railscast #154 polymorphic association - ruby-on-rails

Im following ryan bates screen cast on polymoprhic associations
http://railscasts.com/episodes/154-polymorphic-association-revised?view=asciicast
I've done this sucessfully before on a Rails 3 app but now im on Rails 4 and i feel like im having issues with strong parameters..... but i can be wrong
when i go into my console to create a new event for a user it works
a = Event.first
c = a.events.create!(name: "Hello World")
this works and posts on my events index page
howwever when i try to use the actual form on the site it creates the record but the name field is nil and blank...and i dont get any errors
heres my controller (im basically just copying what Ryan Bates does on the site)
class EventsController < ApplicationController
before_filter :load_eventable
def index
#eventable = Admin.find(params[:admin_id])
#events = #eventable.events
end
def new
#event = #eventable.events.new
end
def create
#event = #eventable.events.new(params[:events])
if #event.save
redirect_to [#eventable, :events], notice: "Event created."
else
render :new
end
end
private
def load_eventable
resource, id = request.path.split('/')[1,2]
#eventable = resource.singularize.classify.constantize.find(id)
end
def events
params.require(:events).permit(:name, :address, :city, :state, :zip, :date, :time, :admin_id)
end
end
here my form (very simple and im just using name for now)
= form_for [#eventable, #event] do |f|
.field
= f.text_field :name
= f.submit

Try the below: creating a new event with the event_params method you defined instead of the params hash. I changed the name to make it a little less confusing.
class EventsController < ApplicationController
...
def create
#event = #eventable.events.new(event_params)
if #event.save
redirect_to [#eventable, :events], notice: "Event created."
else
render :new
end
end
private
...
def event_params
params.require(:events).permit(:name, :address, :city, :state, :zip, :date, :time, :admin_id)
end
end

Related

Proper model set up

I am new to stackoverflow, so my apologies if this is formatted poorly.
In my current project I have a model Driver, which has many trips. Those trips have many mileages, backhauls, picks, drops and hours. When I create a new trip, i want to be able to associate it to the driver, but I also want to be able to add the mileages, backhauls, picks and drops and hours on the same page. I am unsure how to structure my routes for this. I have been successful in creating a trip for a driver without adding on the additional models to the trip but from there I am stumped. I have only created the mileage model/controller so far for what needs to be associated with the trip. Any nudge in the right direction would be greatly appreciated.
Driver Model
class Driver < ApplicationRecord
has_many :trips
end
Trip Model
class Trip < ApplicationRecord
belongs_to :driver
has_many :mileages
accepts_nested_attributes_for :mileages
default_scope {order(date: :asc)}
validates :total, presence: true
validates :date, presence: true
validates_uniqueness_of :trip_number, :allow_nil => true, :allow_blank =>
true
end
Mileage Model
class Mileage < ApplicationRecord
belongs_to :trip
end
Trips controller
def index
#trips = Trip.all
end
def show
end
def new
#driver = Driver.find(params[:driver_id])
#trip = Trip.new
end
def edit
end
def create
#driver = Driver.find(params[:driver_id])
#trip = Trip.new(trip_params)
#driver.trips.create(trip_params)
respond_to do |format|
if #driver.trips.create(trip_params)
flash[:notice] = "Trip successfully created"
redirect_to new_driver_trip_path(#driver)
else
flash[:warning] = "Unable to create trip"
redirect_to new_driver_trip_path(#driver)
end
end
private
def set_trip
#trip = Trip.find(params[:id])
end
def trip_params
params.require(:trip).permit(:trip_number, :date, :driver_id, :total)
end
end
Mileage Controller
def new
#mileage = Mileage.new
end
def create
#mileage.create(mileage_params)
end
private
def mileage_params
params.require(:mileage).permit(:miles, :rate, :total)
end
end
end
Driver Controller
def index
#drivers = Driver.all
end
def show
end
def new
#driver = Driver.new
end
def edit
end
def create
#driver = Driver.new(driver_params)
respond_to do |format|
if #driver.save
format.html { redirect_to #driver, notice: 'Driver was
successfully created.' }
format.json { render :show, status: :created, location: #driver }
else
format.html { render :new }
format.json { render json: #driver.errors, status:
:unprocessable_entity }
end
end
end
private
def set_driver
#driver = Driver.find(params[:id])
end
def driver_params
params.require(:driver).permit(:first_name, :last_name, :email, :unit)
end
end
If you want to create nested models on the same page. i.e. milages within trip page using accepts_nested_attributes_for, You can use cocoon gem.
https://github.com/nathanvda/cocoon
Drifting Ruby has a video that shows the process in detail that is easy to follow.
https://www.youtube.com/watch?v=56xjUOAAZY8
You can do it manually as well but it will require a little bit more work.
With cocoon you will do have a Driver Controller and Trip controller but you don't need a Milage controller since it is handled with nested_attributes via Trip Controller.
If you want to do it manually, you will need a bit of JavaScript. You can follow Ryan Bates RailsCast on this topic.
railscasts.com/episodes/196-nested-model-form-revised

friendly_id creates new entry on UPDATE

I'm using friendly_id 5.1.0, and when I try to update an entry, for exemple creating a new Article, instead of updating the data of the entry, it creates a new one.
I have slugged the title and when I don't change it when editing an article, it creates a slug with a bunch of numbers/letters :
http://localhost:3000/articles/first-article-2d392b8e-92b8-44b0-ad67-50dd674f7aaa
Here's my article.rb Model :
class Article < ActiveRecord::Base
extend FriendlyId
has_many :comments
friendly_id :title, :use => :slugged
validates :title, presence: true,
length: { minimum: 5}
def should_generate_new_friendly_id?
new_record? || title_changed?
end
when I add :use => [:slugged, :history], when I update an entry and keep the same title, it can't save it because my :slug field is unique :true.
Here's my articles_controller.rb :
class ArticlesController < ApplicationController
def index
#articles = Article.all.order(created_at: :desc)
end
def show
#article = Article.friendly.find(params[:id])
if request.path != article_path(#article)
redirect_to #article, status: :moved_permanently
end
end
def new
#article = Article.new
end
def edit
#article = Article.friendly.find(params[:id])
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.friendly.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.friendly.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
Here's my GitHub repository whith my (unfinished) project : https://github.com/TheDoctor314/blog
This issue has nothing to do with FriendlyID.
Your problem is here (a form used on both new and edit):
<%= bootstrap_form_for :article, url: articles_path do |f| %>
It does not attempt to use your #article object to built that form. So your form always issues a POST request to articles_path, which results in create every time. What you should do instead is:
<%= bootstrap_form_for #article do |f| %>
That way form builder will check if that object is persisted? already, and if so, generate a form that issues a PATCH request to that specific article that triggers an update action. It will try to guess the URL by itself. And it will succeeed only if you follow conventions tightly enough.
If #article is not persisted?, it will do what it did: make a POST to articles_path.
Permit id in params
params.require(:article).permit(:id, :title, :text)
Hope that helps!
The Edit form is routing to create action for articles controller instead of update action. You need to change your form path, when editing the files.
If you see the articles index action, you can see new articles are being added, instead of updating

Can't assign tags in Rails 4.2 from model form using acts_as_taggable

I am trying to use the acts_as_taggable_on gem. Everything works in console, I can asoicate tags with a model instance, render the tag_list and link to a results page based on the tag chosen. My issue is that my tags entered into the create form do not save in the db. I have already checked strong_params:
params.require(:contest).permit(:name, :description, :user_id, :show_name, :tag_list => [],...
I had the same problem in a Rails 4.2, ruby 2.2, and acts-as-taggable-on 3.4 app. I changed the params in the controller to update the model. Originally, I had
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(:title, :body, :tag_list => []))
redirect_to #post
else
render 'edit'
end
end
I changed the params :tag_list => [] to just :tag_list. It works. My code now looks like the following:
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(:title, :body, :tag_list))
redirect_to #post
else
render 'edit'
end
end
def tagged
if params[:tag].present?
#post = Post.tagged_with(params[:tag])
else
#posts = Post.postall
end
end
private
def post_params
params.require(:post).permit(:title, :body, :image, :image2, :tag_list => [])
end
Hope this helps.

Rails 4 ActiveRecord, if record exists, use id, else create a new record

When an employee is created, he is given a title. If the title is unique, the record saves normally. If the title is not unique, I want to find the existing title, and use that instead. I can't figure out how to do this in the create action.
employer.rb
class Employee < ActiveRecord::Base
belongs_to :title, :class_name => :EmployeeTitle, :foreign_key => "employee_title_id"
accepts_nested_attributes_for :title
end
employer_title.rb
class EmployerTitle < ActiveRecord::Base
has_many :employees
validates :name, presence: true, length: { maximum: 50 },
uniqueness: { case_sensitive: true }
end
new.html.erb
<%= f.simple_fields_for :title do |title| %>
<%= title.input :name, label: "Title" %>
<% end %>
employees_controller.rb
def create
if EmployeeTitle.exists?(name: employee_params[:title_attributes][:name])
# find title and use it?
else
#employee = current_user.employee.build(employee_params)
end
if #employee.save
flash[:success] = "Employee #{#employee.title.name} created."
redirect_to #employee
else
render 'new'
end
end
Edit: Using first_or_create
def create
EmployeeTitle.where(name: employee_params[:title_attributes][:name]).first_or_create do |title|
#employee = current_user.employees.build(employee_params, :title => title)
end
if #employee.save
flash[:success] = "Employee #{#employee.title.name} created."
redirect_to #employee
else
render 'new'
end
end
This makes the #employee go out of scope. Error: Undefined method `save' for nil:NilClass.
In addition, if I do this, won't the title be created regardless of whether the rest of the employee data is valid?
Using private method
employee.rb
private
def title_attributes=(attributes)
self.title = EmployeeTitle.find_or_create_by_name(name: attributes[:name])
end
The value is not being set. I get a "cannot be blank" validation error. The parameters include
employee: !ruby/hash:ActionController::Parameters
title: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
name: Consultant
The !ruby/hash:ActiveSupport::HashWithIndifferentAccess was not there before.
employee_params
private
def employee_params
params.require(:employee).permit(
title_attributes: [:id, :name],
)
end
What you need to do is to change this:
def create
if EmployeeTitle.exists?(name: employee_params[:title_attributes][:name])
# find title and use it?
else
#employee = current_user.employee.build(employee_params)
end
if #employee.save
flash[:success] = "Employee #{#employee.title.name} created."
redirect_to #employee
else
render 'new'
end
end
with this:
def create
#employee = current_user.employee.build(employee_params)
if #employee.save
flash[:success] = "Employee #{#employee.title.name} created."
redirect_to #employee
else
render 'new'
end
end
Now, override title_attributes method by putting this code in your app/models/employee.rb file:
def title_attributes=(attributes)
self.title = EmployeeTitle.find_or_create_by_name(attributes[:name])
end
Now, every time you'll create an employee whose name already exists with the particular name, it'll be used by default for associating it as title. Let the controller be skinny as it used to be.
Read more about find_or_create_by method here.
However, your question's title says: Rails 4, but you have tagged ruby-on-rails-3.2. If you're using Rails 4 then you can use this instead:
EmployeeTitle.find_or_create_by(name: attributes[:name])

Passed params drop after failed submit

In my app I pass parameters from one controller to another
Firstly I'm creating Company object and pass its id in parameters in redirecting link
companies_controller:
class CompaniesController < ApplicationController
def new
#company = Company.new
end
def create
#company = current_user.companies.build(company_params)
if #company.save
redirect_to new_constituent_path(:constituent, company_id: #company.id)
else
render 'new'
end
end
private
def company_params
params.require(:company).permit(:name)
end
end
After successfully Company saving I'm redirected to creating a Constituent object. I fill company_id or entrepreneur_id with parameters passed in link http://localhost:3000/constituents/new.constituent?company_id=9 for example
constituents/new:
= simple_form_for #constituent do |f|
= f.input :employees
- if params[:entrepreneur_id]
= f.hidden_field :entrepreneur_id, value: params[:entrepreneur_id]
- elsif params[:company_id]
= f.hidden_field :company_id, value: params[:company_id]
= f.button :submit
constituents_controller:
class ConstituentsController < ApplicationController
def new
#constituent = Constituent.new
end
def create
#constituent = Constituent.create(constituent_params)
if #constituent.save
redirect_to root_url
else
render 'new'
end
end
private
def constituent_params
params.require(:constituent).permit(:employees, :company_id, :entrepreneur_id)
end
end
The problem is parameters I passed in link is dropping after failed attempt to save #constituent and company_id or entrepreneur_id is nil. How can I fix it?
This happens because after you submit your form, there are no params[:company_id] = 9 anymore. After render :new is done, you will have params[:constituent][:company_id] = 9.
So, to solve this problem, you need to send not this get request to new Constituent:
http://localhost:3000/constituents/new?company_id=9
But something like this:
http://localhost:3000/constituents/new?constituent[company_id]=9
Your view will become a little bit more ugly, to avoid error if params[:constituent] not exist:
- if params[:constituent]
- if params[:constituent][:entrepreneur_id]
= f.hidden_field :entrepreneur_id, value: params[:constituent][:entrepreneur_id]
- elsif params[:constituent][:company_id]
= f.hidden_field :company_id, value: params[constituent][:company_id]

Resources