I'm a noob to Rails and have been working on this for 3-4 days. I've created a many-to-many :through association, but when I submit a new object, I get this error:
#<ActiveModel::Errors:0x007fbced7cda88 #base=#<BlogPost id: nil, created_at: nil, updated_at: nil, title: "title 13", content: "pajfposjpfej 13", posted_by: "poster 13", comments: nil, blog_pic: nil>, #messages={:"categorizations.blog_post"=>["must exist"], :title=>[], :posted_by=>[], :content=>[], :blog_pic=>[]}, #details={"categorizations.blog_post"=>[{:error=>:blank}]}>
My form submission I'm submitting I'm selecting a category so it shouldn't be empty as the error indicates. Really appreciate any help with this. Demoralized for days now.
models:
class BlogPost < ApplicationRecord
has_many :categorizations
has_many :categories, :through => :categorizations
accepts_nested_attributes_for :categorizations
has_many :comments
mount_uploader :blog_pic, BlogPicUploader
end
class Categorization < ApplicationRecord
belongs_to :blog_post
belongs_to :category
end
class Category < ApplicationRecord
has_many :categorizations
has_many :blog_posts, :through => :categorizations
end
views/blog_posts/new.html.erb
<%= form_for #blog_post do |b| %>
<%= b.label :title %>
<%= b.text_field :title %><br>
<%= b.fields_for :categorizations do |cat| %>
<%= cat.label :category_name, "Category 1" %>
<%= cat.collection_select(:category_id, Category.all, :id, :category_name, include_blank: "Select Category") %><br>
<% end %>
<%= b.submit "Submit", class: "btn btn-primary" %>
<% end %>
controller:
class BlogPostsController < ApplicationController
def index
#blog_posts = BlogPost.order(id: :desc)
end
def new
#blog_post = BlogPost.new
#categorizations = #blog_post.categorizations.build
#categories = #blog_post.categories.build
end
...
def create
#blog_post = BlogPost.new(blog_post_params)
respond_to do |format|
if #blog_post.save
format.html { redirect_to #blog_post, notice: 'Your blog was submitted successfully' }
format.json { render :show, status: :created, location: #blog_post }
else
format.html { render :new }
format.json { render json: #blog_post.errors, status: :unprocessable_entity }
end
end
puts #blog_post.errors.inspect
end
private
def blog_post_params
params.require(:blog_post).permit(:title, :content, :posted_by, :comments, :blog_pic, {categorizations_attributes: [:category_id, :category_name]})
end
end
you need to declare nested attribute for categories as well.
like:
class BlogPost < ApplicationRecord
has_many :categorizations
has_many :categories, :through => :categorizations
accepts_nested_attributes_for :categorizations
accepts_nested_attributes_for :categories
has_many :comments
mount_uploader :blog_pic, BlogPicUploader
end
You can refer below link for description
https://hackhands.com/building-has_many-model-relationship-form-cocoon/
The answer here had nothing to do with the code - simply put - in Rails 5, they've made a change that now requires belongs_to association by default, where as in Rails 4, this was optional. See link below that explains:
http://blog.bigbinary.com/2016/02/15/rails-5-makes-belong-to-association-required-by-default.html
You have to change this line in config/initializers/new_framework_defaults from true to false:
Rails.application.config.active_record.belongs_to_required_by_default = false
Related
I'm trying to create a form in Rails 5.2 for a model with a has_many :through relationship to another model. The form needs to include nested attributes for the other model. However, the params are not nesting properly. I've created the following minimal example.
Here are my models:
class Order < ApplicationRecord
has_many :component_orders, dependent: :restrict_with_exception
has_many :components, through: :component_orders
accepts_nested_attributes_for :components
end
class Component < ApplicationRecord
has_many :component_orders, dependent: :restrict_with_exception
has_many :orders, through: :component_orders
end
class ComponentOrder < ApplicationRecord
belongs_to :component
belongs_to :order
end
The Component and Order models each have one attribute: :name.
Here is my form code:
<%= form_with model: #order do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= fields_for :components do |builder| %>
<%= builder.label :name %>
<%= builder.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
When I fill out the form, I get the following params:
{"utf8"=>"✓", "authenticity_token"=>"ztA1D9MBp1IRPsiZnnSAIl2sEYjFeincKxivoq0/pUO+ptlcfi6VG+ibBnSREqGq3VzckyRfkQtkCTDqvnTDjg==", "order"=>{"name"=>"Hello"}, "components"=>{"name"=>"World"}, "commit"=>"Create Order", "controller"=>"orders", "action"=>"create"}
Specifically, note that instead of a param like this:
{
"order" => {
"name" => "Hello",
"components_attributes" => {
"0" => {"name" => "World"}
}
}
}
There are separate keys for "order" and "components" at the same level. How can I cause these attributes to nest properly? Thank you!
EDIT: Here is my controller code:
class OrdersController < ApplicationController
def new
#order = Order.new
end
def create
#order = Order.new(order_params)
if #order.save
render json: #order, status: :created
else
render :head, status: :unprocessable_entity
end
end
private
def order_params
params.require(:order).permit(:name, components_attributes: [:name])
end
end
You should include accepts_nested_attributes_for :components in the Order model.
class Order < ApplicationRecord
has_many :component_orders, dependent: :restrict_with_exception
has_many :components, through: :component_orders
accepts_nested_attributes_for :components
end
And change
<%= fields_for :components do |builder| %>
to
<%= f.fields_for :components do |builder| %>
to get the desired params. accepts_nested_attributes_for :components creates a method namely components_attributes
More Info here
I'm building a simple top-to-bottom Workout Routine app on ROR. I'm able to create a Workout Day (parent) and an Exercise (child) on the same form. But I can't seem to save the Weighted Set (grandchild) when I submit the form. The interesting thing is that since the Exercise is saved, I can go to that exercise edit page, add a Weighted Set, and the Weighted Set will show up in the Workout Day show page. I think it has to do with the Weighted Set not being associated with the Exercise at the time of creation. How cam I tie wll three models together? I know I'm close!
I have the whole app on github. I the link isn't working, try this URL https://github.com/j-acosta/routine/tree/association
Models
class WorkoutDay < ApplicationRecord
has_many :exercises, dependent: :destroy
has_many :weighted_sets, through: :exercises
accepts_nested_attributes_for :exercises
accepts_nested_attributes_for :weighted_sets
end
class Exercise < ApplicationRecord
belongs_to :workout_day, optional: true
has_many :weighted_sets, dependent: :destroy
accepts_nested_attributes_for :weighted_sets
end
class WeightedSet < ApplicationRecord
belongs_to :exercise, optional: true
end
Workout Day Controller
class WorkoutDaysController < ApplicationController
before_action :set_workout_day, only: [:show, :edit, :update, :destroy]
...
# GET /workout_days/new
def new
#workout_day = WorkoutDay.new
# has_many association .build method => #parent.child.build
#workout_day.exercises.build
# has_many :through association .build method => #parent.through_child.build
# #workout_day.weighted_sets.build
#workout_day.weighted_sets.build
end
...
# POST /workout_days
# POST /workout_days.json
def create
#workout_day = WorkoutDay.new(workout_day_params)
respond_to do |format|
if #workout_day.save
format.html { redirect_to #workout_day, notice: 'Workout day was successfully created.' }
format.json { render :show, status: :created, location: #workout_day }
else
format.html { render :new }
format.json { render json: #workout_day.errors, status: :unprocessable_entity }
end
end
end
...
private
# Use callbacks to share common setup or constraints between actions.
def set_workout_day
#workout_day = WorkoutDay.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def workout_day_params
params.require(:workout_day).permit(:title, exercises_attributes: [:title, :_destroy, weighted_sets_attributes: [:id, :weight, :repetition]])
end
end
New Workout Day form
<%= form_for #workout_day do |workout_day_form| %>
<% if workout_day.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(workout_day.errors.count, "error") %> prohibited this workout_day from being saved:</h2>
<ul>
<% workout_day.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= workout_day_form.label :title, 'Workout Day Name' %>
<%= workout_day_form.text_field :title %>
</div>
exercise_field will go here
<div>
<%= workout_day_form.fields_for :exercises do |exercise_field| %>
<%= exercise_field.label :title, 'Exercise' %>
<%= exercise_field.text_field :title %>
<% end %>
</div>
weighted_set_fields will go here
<div>
<%= workout_day_form.fields_for :weighted_sets do |set| %>
<%= render 'exercises/weighted_set_fields', f: set %>
<% end %>
</div>
<div>
<%= workout_day_form.submit %>
</div>
<% end %>
The culprit is the workout_day_params. In the form you have the fields of weighted_sets nested under the workout_day. But in the workout_day_params, you have weighted_sets_attributes under exercises_attributes which is the reason for your problem. Changing it to below should solve the issue.
def workout_day_params
params.require(:workout_day).permit(:title, exercises_attributes: [:title, :_destroy], weighted_sets_attributes: [:id, :weight, :repetition])
end
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection
This due to wrong associations. You should consider tweaking your associations like below
class WorkoutDay < ApplicationRecord
has_many :weighted_sets, dependent: :destroy
has_many :exercises, through: :weighted_sets
accepts_nested_attributes_for :exercises
accepts_nested_attributes_for :weighted_sets
end
class Exercise < ApplicationRecord
has_many :weighted_sets, dependent: :destroy
has_many :workout_days, through: :weighted_sets
end
class WeightedSet < ApplicationRecord
belongs_to :exercise, optional: true
belongs_to :workout_day, optional: true
end
Really stumped here. I'm trying to get my form to update the categories on the edit form. Problem is, everything in my form updates when submitted except the categories. It ends up inserting the new category chosen like it's going through the create method instead of the update method, so when the edit form is shown again after submission, it keeps doubling the fields of categories. 1, then 2, then 4, then 8, etc. after each submission. Please please help anyone. Appreciate it.
views/blog_posts/edit.html.erb
<div class="col-md-6 col-md-offset-3 blog-submit">
<%= form_for #blog_post do |b| %>
<%= b.label :title %>
<%= b.text_field :title %><br>
<%= b.fields_for :categorizations do |cat| %>
<%= cat.label :category_name, "Category 1" %>
<%= cat.collection_select(:category_id, Category.all, :id, :category_name, {blank: "Select Category"}) %>
<%= link_to "Add Categories", new_category_path %>
<br>
<% end %>
<%= b.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
Blog_post controller:
class BlogPostsController < ApplicationController
protect_from_forgery
before_action :authenticate_admin!, only: [:new, :edit]
def index
#blog_posts = BlogPost.order(id: :desc)
end
def new
#blog_post = BlogPost.new
#blog_post.categorizations.build.build_category
#blog_post.categories.build
end
def edit
#blog_post = BlogPost.find(params[:id])
end
def create
#blog_post = BlogPost.new(blog_post_params)
respond_to do |format|
if #blog_post.save
format.html { redirect_to #blog_post, notice: 'Your blog was submitted successfully' }
format.json { render :show, status: :created, location: #blog_post }
else
format.html { render :new }
format.json { render json: #blog_post.errors, status: :unprocessable_entity }
end
end
puts #blog_post.errors.inspect
end
def update
#blog_post = BlogPost.find(params[:id])
if #blog_post.update_attributes(blog_post_params)
render 'show'
else
render 'edit'
end
end
def show
#blog_post = BlogPost.find(params[:id])
end
private
def blog_post_params
params.require(:blog_post).permit(:title, :content, :posted_by, :comments, :blog_pic, {categorizations_attributes: [:category_id, :category_name]})
end
end
models:
class BlogPost < ApplicationRecord
has_many :categorizations
has_many :categories, :through => :categorizations
accepts_nested_attributes_for :categorizations
has_many :comments
mount_uploader :blog_pic, BlogPicUploader
end
class Categorization < ApplicationRecord
belongs_to :blog_post
belongs_to :category
end
class Category < ApplicationRecord
has_many :categorizations
has_many :blog_posts, :through => :categorizations
end
Add id in blog_post_params as shown below. This will work for you.
def blog_post_params
params.require(:blog_post).permit(:title, :content, :posted_by, :comments, :blog_pic, {categorizations_attributes: [:id,:category_id, :category_name]})
end
I have a model (User) that has_many of another model (Profession) - and this is supposed to be represented by one (or multiple) select menu in a form.
I cannot get my head around why the select menu doesn't get rendered? Am I constructing the select helper in the wrong way? Or is something else wrong in the view or the controller? The name attribute of the User is showing up alright in the form.
The models:
class User < ActiveRecord::Base
has_many :occupations, dependent: :destroy
has_many :professions, through: :occupations
accepts_nested_attributes_for :occupations
end
class Profession < ActiveRecord::Base
has_many :occupations, dependent: :destroy
has_many :users, through: :occupations
end
class Occupation < ActiveRecord::Base
belongs_to :user
belongs_to :profession
end
The controller:
def edit
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user, notice: 'User was successfully created.'
else
render action: 'new'
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :email, ocuppations_attributes: [:id, :user_id, :profession_id])
end
The view (compressed):
<%= form_for(#user) do |f| %>
<%= f.text_field :name %>
<%= f.fields_for :occupations do |builder| %>
<%= builder.select :profession_id, Profession.all.collect {|x| [x.title, x.id]} %>
<% end %>
<% end %>
Shouldn't that be a collection select?
<%= builder.collection_select(:profession_id, Profession.all, :id, :title) %>
I have three models:
category.rb
class Category
include Mongoid::Document
# Relationships
has_many :posts, :autosave => true
has_many :boards, :autosave => true
accepts_nested_attributes_for :boards
accepts_nested_attributes_for :posts
#fields
field :name
#attr
attr_accessible :name
end
My model board.rb
class Board
include Mongoid::Document
# Relationships
has_many :posts, :dependent => :destroy , :autosave => true
accepts_nested_attributes_for :posts
belongs_to :category
#fields
field :name
field :description
#attr
attr_accessible :name, :posts_attributes, :description, :category_id
end
My post.rb
class Post
include Mongoid::Document
# Relationships
belongs_to :board
belongs_to :category
belongs_to :user
#fields
field :content
#attr
attr_accessible :content :board_id, :category_id
end
In my posts_controller
def create
#post = current_user.posts.new(params[:post])
#board = #post.board
#board.user = current_user
if #board.category_id?
#post.category_id = #board.category_id
end
respond_to do |format|
if #post.save
format.html { redirect_to root_url, notice: 'Post was successfully created.' }
format.json { render json: root_url, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
In my view new action:
<%= form_for(#post) do |f| %>
<%= f.text_area :content %>
<%= f.collection_select :board_id, Board.where(:user_id => current_user.id), :id, :name%>
<%= f.submit :id => "button_submit_pin_edit" %>
<% end %>
The boards in select field, may or may not have a parent category already assigned.
I want get the attributes from category (the name of the category for this event) in my post view without using a select field, or an input field.
with this code in posts.controller.rb
if #board.category_id?
#post.category_id = #board.category_id
end
I see in my console for Post.first e.j.:
<Post _id: 4f1d96241d41c8280800007c, _type: nil, created_at: 2012-01-23 17:17:24 UTC, user_id: BSON::ObjectId('4f0b19691d41c80d08002b20'), board_id: BSON::ObjectId('4f1455fa1d41c83988000510'), category_id: BSON::ObjectId('4f1c2d811d41c8548e000008'), content: "nuevo post">
If I write:
post = Post.first
and
post.board
I get the object board fine. This does works fine.
but If I write:
post.category
I get:
=> nil
I have try in view new post add hidden field:
hidden_field(:category, :name)
How can I get the params of object category?
Thanks
Don't set it using the ID fields. Instead of this in your PostsController:
if #board.category_id?
#post.category_id = #board.category_id
end
Try this line:
#post.category = #board.category
Which will set the #post.category belongs_to relationship to point to #board.category assuming it exists (is not nil). Mongoid will handle the setting of the category_id field for you. Read the mongoid documentation about relationships for a more detailed explanation.