My User model has_many Responses. I'm trying to create a nested form to create a new user with three child responses. My code looks identical to the Rails cast, but although it will save the user it does not save their responses. Can anyone see what is wrong?
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
3.times{#user.responses.build}
#responses = #user.responses
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user #todo: where do we want to redirect?
else
render 'new'
end
end
def show
#user = User.find(params[:id])
#responses = #user.responses
end
def index
#users = User.all
end
private
def user_params
params.require(:user).permit(:username, :email, :responses)
end
end
user.rb
class User < ActiveRecord::Base
attr_accessor :responses_attributes
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses#, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
before_save { self.email = email.downcase }
validates :username, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } ,
uniqueness: {case_sensitive: false};
validates :responses, presence: true
end
response.rb
class Response < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id, :content
end
users/new.html.erb (the form)
<h1>This is a test form for adding Users with child Responses</h1>
<%= form_for #user do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.label :email %>
<%= f.text_field :email %>
<p>
<%= f.fields_for :responses do |builder| %>
<%= builder.label :content, "Response" %>
<%= builder.text_field :content %>
<% end %>
</p>
<%= f.submit "Submit" %>
<% end %>
Edit:
I have change the strong parameters in the Users controller to:
def user_params
params.require(:user).permit(:username, :email, responses_attributes: [:content])
end
And I have updated the User model to:
class User < ActiveRecord::Base
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses#, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
before_save { self.email = email.downcase }
validates :username, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } ,
uniqueness: {case_sensitive: false};
validates_associated :responses, presence: true
end
Now it continues to fail my validations for the responses, and also fails my validations for username and email.
The problem is in your Response class:
validates_presence_of :user_id, :content
This validation requires the existence of user_id, which means the User has to be created before Response. To make it work you can try couple of things:
Change validates_presence_of :user_id to validates_presence_of :user to make it validate the User object instead of the user's id.
Use inverse_of option. See the Rails API doc.
Updated code.
user.rb
class User < ActiveRecord::Base
attr_accessible :responses_attributes
has_many :responses, :dependent => :destroy, :inverse_of => :user
accepts_nested_attributes_for :responses
# ...
end
response.rb
class Response < ActiveRecord::Base
belongs_to :user, :inverse_of => :responses
validates_presence_of :user, :content
end
Answer for RoR v3:
The problem is the following line in your User model:
attr_accessor :responses_attributes
Replace it with:
attr_accessible :responses_attributes
The answer to question Difference between attr_accessor and attr_accessible should help you understand the difference between the two.
Also your validation validates :responses, presence: true needs to be updated to use validates_associated:
validates_associated :responses, presence: true
Answer for RoR v4:
I hadn't noticed the use of Strong parameters ealier, apologies for this. A couple of changes should fix your issue:
Remove attr_accessor :responses_attributes from User model.
Replace validates :responses, presence: true with validates_associated :responses, presence: true
Define responses attributes in permit list.
Something like follows:
class User < ActiveRecord::Base
# attr_accessor :responses_attributes # Remove this line
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses#, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
...
validates_associated :responses, presence: true # Update here
end
#Users Controller
class UsersController < ApplicationController
...
private
def user_params
params.require(:user).permit(:username, :email, :responses_attributes[:content])
end
end
Your code looks almost correct:
user.rb
class User < ActiveRecord::Base
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses
before_save { self.email = email.downcase }
validates :username, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } ,
uniqueness: {case_sensitive: false};
end
users_controller.rb
private
def user_params
params.require(:user).permit(:username, :email, responses_attributes: [:content])
end
Related
On my application, I have an object for many-to-many relationships called "selection"
class Selection < ActiveRecord::Base
belongs_to :question
belongs_to :projecttype
end
and 2 objects to relate to each other
class Projecttype < ActiveRecord::Base
has_many :selections
has_many :questions, :through => :selections
validates :projecttype, uniqueness: true, length: { is: 3 }
validates :name, presence: true
mount_uploader :image, ImageUploader
end
And...
class Question < ActiveRecord::Base
has_many :selections
has_many :projecttypes, :through => :selections
validates :question, presence: true
validates :comment, presence: true, length: { minimum: 5}
validates :sequence, presence: true
end
Now in my edit form for the projecttype, I want to be able to select related questions (using check boxes).
<div class="row">
<div class="col-md-2"><%= f.label :selection %></div>
<div class="col-md-4"><%= f.select :selection, {}, {:multiple => true, :style => "width:100%; border:none" } %></div>
</div>
Have tried options_for_selection and _from_collection and don't anymore know what to do as I get many errors.
what should the f.select code be?
I'm a little rusty with Rails now, but it should be something like this.
<%= f.select :selection, Selection.all.collect {|x| x.name}, {}, :multiple => true %>
and don't forget to add
def post_params
params.require(:post).permit(:title, :body, category_ids: [])
end
to your selection.rb file
I have this problem. I need to validate the attributes of two models in the same form in Rails. One is the parent of the other.
The form is like this:
<%= semantic_form_for #professional do |pro| %>
<%= pro.inputs :id => "information" do %>
<%= pro.input :name, label: t("Artistic Name") %>
<%= pro.semantic_fields_for #user do |user| %>
<%= user.inputs :id => "register" do %>
<%= user.input :email, :placeholder=>"email#example.com" %>
<%= user.input :password, label: t('Password') %>
<%end%>
<% end %>
<% end %>
<% end %>
The models I am using are like this:
User:
class User < ActiveRecord::Base
belongs_to :role, polymorphic: true
validates :email, :password, presence: true
end
Professionals:
class Professional < ActiveRecord::Base
has_one :user, as: :role, dependent: :destroy
accepts_nested_attributes_for :user
validates :date_birthday, :gender, :height, :name, :description, :Weight, :address, :languages,:services, :category, :phonenumber, :fullname, :hair_color, :age, :orientation, presence: true
end
So, what is the problem?
When I clicked in the submit button the professional attributes are marked but not the users attributes.
Like this:
The fields marked in red belongs to the professional model but the fields email and password belongs to the user model aren't marked in red when it should be because they are empty.
What can i do? I need the warning message for the user is attributes too
Thanks in advances.
We've achieved what you need before.
We had to use inverse_of so that the object was a singular piece of data (rather than multiple pieces as is the case by default):
#app/models/user.rb
class User < ActiveRecord::Base
belongs_to :role, polymorphic: true, inverse_of: :user
validates :email, :password, presence: true
end
#app/models/professional.rb
class Professional < ActiveRecord::Base
has_one :user, as: :role, dependent: :destroy, inverse_of: :role
accepts_nested_attributes_for :user
end
This will help.
You also need to make sure you're passing these objects correctly (I see so many people not doing this).
You need to tell Professional to validate the associated User:
class Professional < ActiveRecord::Base
...
validates_associated :user
I'm writing a simple time tracking app where the user has_many clients and where the client has_many projects.
I want a user to be able to view a list of their projects (for all clients). To implement this, I've set up a has_many, through relationship between the users and projects.
But for some reason I can't get the projects to show up in their index view. There's probably a really simple reason for this that I'm just not noticing, so apologies upfront if that's the case.
Here's the relevant code.
Project index controller:
def index
#projects = current_user.projects.paginate(:page => params[:page], :per_page => 6)
end
Project model:
class Project < ActiveRecord::Base
belongs_to :client
validates :name, presence: true, length: { maximum: 30 }
validates :fee, presence: true, numericality: { only_integer: true,
greater_than_or_equal_to: 0, less_than_or_equal_to: 100000 }
validates :client_id, presence: true
end
Client model:
class Client < ActiveRecord::Base
belongs_to :user
has_many :projects, dependent: :destroy
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 30 }
validate :user_id_is_valid
private
def user_id_is_valid
errors.add(:user_id, "is invalid") unless User.exists?(self.user_id)
end
end
Relevant part of the User model:
class User < ActiveRecord::Base
has_many :clients, dependent: :destroy
has_many :projects, through: :clients
index.html.erb:
<div id="projects-list">
<% if current_user.projects.any? %>
<h3>Projects</h3>
<ul class="project-list">
<% render #projects %>
</ul>
<%= will_paginate #projects %>
<% end %>
</div>
_project.html.erb:
<li>
<%= link_to "#{project.name}", '#' %>
</li>
It should be:
<%= render #projects %>
Not:
<% render #projects %>
I have a many-to-many relationship set up between blog posts and blog categories using the has_many :through relationship and I am having trouble creating a blog post resource through active admin.
class BlogPost < ActiveRecord::Base
attr_accessible :body, :created_at, :updated_at, :image_url, :title
validates :body, :image_url, :title, presence: true
validates :title, uniqueness: true
has_many :blog_post_categorizations
has_many :blog_categories, :through => :blog_post_categorizations
accepts_nested_attributes_for :blog_categories, allow_destroy: true
end
class BlogCategory < ActiveRecord::Base
attr_accessible :id, :name, :created_at, :updated_at
validates :name, presence: true, uniqueness: true
has_many :blog_post_categorizations
has_many :blog_posts, :through => :blog_post_categorizations
accepts_nested_attributes_for :blog_posts, allow_destroy: true
end
class BlogPostCategorization < ActiveRecord::Base
belongs_to :blog_post
belongs_to :blog_category
end
Now as for my activeadmin formtastic setup, I have:
ActiveAdmin.register BlogPost do
form do |f|
f.inputs "Blog Post" do
f.input :title
f.input :body, as: :html_editor
f.input :image_url
end
f.inputs "Blog Categories" do
f.has_many :blog_categories do |s|
s.input :name
end
end
f.actions
end
end
This allows the new blog post page to load successfully, but when I attempt to submit the new post with categories attached, it results in a "Can't mass-assign protected attributes: blog_categories_attributes" error.
I suspect that my models are set up properly, but the form in ActiveAdmin is not. Any ideas as to how I can correctly set up the form?
Additionally, included below is the blog_category data that's being sent in the params hash of the POST request
"blog_categories_attributes"=>{"1408936652467"=>{"name"=>"cooking"}, "1408936656066"=>{"name"=>"eat"}}
Using Rails version 3.2
It's been awhile since I've worked in Rails and I'm having some issues with setting up my routes properly. Any help would be greatly appreciated.
URL: http://localhost:3000/admin/products/new
Error: undefined method `products_path' for #<#<Class:0x007f9f569d0150>:0x007f9f578ccb18>
rake routes
admin_products GET /admin/products(.:format) admin/products#index
POST /admin/products(.:format) admin/products#create
new_admin_product GET /admin/products/new(.:format) admin/products#new
edit_admin_product GET /admin/products/:id/edit(.:format) admin/products#edit
admin_product GET /admin/products/:id(.:format) admin/products#show
PUT /admin/products/:id(.:format) admin/products#update
DELETE /admin/products/:id(.:format) admin/products#destroy
routes.rb
Aneprize::Application.routes.draw do
devise_for :admins, :users
namespace :admin do
match '/', to: 'dashboard#index', as: '/'
authenticated :admin do
root to: 'dashboard#index', as: :root
resources :products do
resource :contest
resources :bids
resources :photos
resources :tags
end
end
end
root to: 'contests#index'
end
product.rb
class Product < ActiveRecord::Base
attr_accessible :name, :retail_price, :short_description, :long_description, :weight
has_one :contest, dependent: :destroy
has_many :photos, dependent: :destroy
has_many :bids, dependent: :destroy
has_many :tags
validates :name, :short_description, :long_description, presence: true
validates :retail_price, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :weight, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1 }
end
contest.rb
class Contest < ActiveRecord::Base
attr_accessible :product_id, :duration
belongs_to :product, dependent: :destroy
validates :product_id, presence: true, numericality: { only_integer: true, greater_than: 0 }
validates :duration, presence: true, numericality: { only_integer: true, greater_than: 2 }
end
product.rb
class Photo < ActiveRecord::Base
attr_accessible :product_id, :image_url
belongs_to :product
validates :product_id, presence: true, numericality: { only_integer: true, greater_than: 0 }
validates :image_url, presence: true, format: { with: /^[^-\d].+/ }
end
bid.rb
class Bid < ActiveRecord::Base
attr_accessible :product_id, :account_id, :amount
belongs_to :account
belongs_to :product, dependent: :destroy
validates :account_id, :product_id, :amount, presence: true, numericality: { only_integer: true, greater_than: 0 }
end
tag.rb
class Tag < ActiveRecord::Base
attr_accessible :name, :product_id
belongs_to :product
validates :name, presence: true, format: { with: /^\D+$/ }
validates :product_id, presence: true, numericality: { only_integer: true, greater_than: 0 }
end
As you are getting error at URL: http://localhost:3000/admin/products/new so you should have some problems with your form builder.
While you have products resources in :admin namespace you should take this fact into account while building form. Correct example is:
<%= form_for [:admin, #product] do |f| %>
... # whatever
<% end %>
instead of just
<%= form_for #product do |f| %>
... # whatever
<% end %>
Somewhere in your new template you are calling products_path. Since it is under admin you should change it to admin_products_path