for now i've got followings:
model => User (name, email)
has_and_belongs_to_many :trips
model => Trip (dest1, dest2)
has_and_belongs_to_many :users
validates :dest1, :dest2, :presence => true
model => TripsUsers (user_id, trip_id) (id => false)
belongs_to :user
belongs_to :trip
As you see from the code, trip model has validation on dest1, and dest2, but it's not showing up an errors. Controller and view defined as follow:
trips_controller.rb
def new
#user = User.find(params[:user_id])
#trip = #user.trips.build
end
def create
#user = User.find(params[:user_id])
#trip = Trip.new(params[:trip])
if #trip.save
#trip.users << #user
redirect_to user_trips_path, notice: "Success"
else
render :new
end
end
_form.html.erb
<%= simple_form_for [#user, #trip] do |f| %>
<%= f.error_notification %>
<%= f.input :dest1 %>
<%= f.input :dest2 %>
<%= f.submit "Submit" %>
<% end %>
According to the rails guide on presence validation, it can't be used with associated objects. Try to use a custom validation:
validate :destinations_presence
def destinations_presence
if dest1.nil?
errors.add(:dest1, "missing dest1")
elsif dest2.nil?
errors.add(:dest1, "missing dest2")
end
end
Related
My goal is to when adding a new product with the new product form, to have an input where one can add a list of emails separated by a space. The list of emails in this string field would be saved as an array of emails in the email_list array attribute of the Product model. This way each product has many emails. (later an email will be sent to these users to fill out questionaire, once a user fills it out there name will be taken off this list and put on completed_email_list array.
I am relatively new to rails, and have a few questions regarding implementing this. I am using postgresql, which from my understanding I do not need to serialize the model for array format because of this. Below is what I have tried so far to implement this. These may show fundamental flaws in my thinking of how everything works.
My first thinking was that I can in my controllers create action first take params[:email].split and save that directly into the email_list attribute (#product.email_list = params[:email].split. It turns out that params[:email] is always nil. Why is this? (this is a basic misunderstanding I have)(I put :email as accepted param).
After spending a long time trying to figure this out, I tried the following which it seems works, but I feel this is probably not the best way to do it (in the code below), which involves creating ANOTHER attribute of string called email, and then splitting it and saving it in the email_list array :
#product.email_list = #product.email.split
What is the best way to actually implement this? someone can clear my thinking on this I would be very grateful.
Cheers
Products.new View
<%= simple_form_for #product do |f| %>
<%= f.input :title, label:"Product title" %>
<%= f.input :description %>
<%= f.input :email %>
<%= f.button :submit %>
<%end %>
Products Controller
class ProductsController < ApplicationController
before_action :find_product, only: [:show, :edit, :update, :destroy]
def index
if params[:category].blank?
#products= Product.all.order("created_at DESC")
else
#category_id=Category.find_by(name: params[:category]).id
#products= Product.where(:category_id => #category_id).order("created_at DESC")
end
end
def new
#product=current_user.products.build
#categories= Category.all.map{|c| [c.name, c.id]}
end
def show
end
def edit
#categories= Category.all.map{|c| [c.name, c.id]}
end
def update
#product.category_id = params[:category_id]
if #product.update(product_params)
redirect_to product_path(#product)
else
render 'new'
end
end
def destroy
#product.destroy
redirect_to root_path
end
def create
#product=current_user.products.build(product_params)
#product.category_id = params[:category_id]
#product.email_list = #product.email.split
if #product.save
redirect_to root_path
else
render 'new'
end
end
private
def product_params
params.require(:product).permit(:title, :description, :category_id, :video, :thumbnail,:email, :email_list)
end
def find_product
#product = Product.find(params[:id])
end
end
To solve your original issue
#product.email_list = params[:email].split. It turns out that params[:email] is always nil
:email is a sub key of :product hash, so it should be:
#product.email_list = params[:product][:email].split
Demo:
params = ActionController::Parameters.new(product: { email: "first#email.com last#email.com" })
params[:email] # => nil
params[:product][:email] # => "first#email.com last#email.com"
I'd say that what you have is perfectly fine, except for the additional dance that you're doing in #product.email_list=#product.email.split, which seems weird.
Instead, I'd have an emails param in the form and an #emails= method in the model (rather than email and #email=):
def emails=(val)
self.email_list = val.split
end
Alternatively, you could do that in the controller rather than having the above convenience #emails= method, similar to the way you're handling the category_id:
#product = current_user.products.build(product_params)
#product.category_id = params[:category_id]
#product.email_list = product_params[:emails].split
Because you need validations on your emails and to make it cleaner I would create an email table, make Product table accept Email attribues and use cocoon gem to have a nice dynamic nested form with multiple emails inputs.
1) models
class Product < ActiveRecord::Base
has_many :emails, dependent: :destroy
accepts_nested_attributes_for :emails, reject_if: :all_blank, allow_destroy: true
end
class Email < ActiveRecord::Base
belong_to :product
validates :address, presence: true
end
2) Controller
class ProductsController < ApplicationController
def new
#product = current_user.products.build
end
def create
#product = current_user.products.build(product_params)
if #product.save
redirect_to root_path
else
render 'new'
end
end
private
def product_params
params.require(:project).permit(:title, :description, :category_id, :video, :thumbnail, emails_attributes: [:id, :address, :_destroy])
end
end
3) View
<%= simple_form_for #product do |f| %>
<%= f.input :title, label:"Product title" %>
<%= f.input :description %>
<%= f.association :category %>
<div id="emails">
<%= f.simple_fields_for :emails do |email| %>
<%= render 'emails_fields', f: email %>
<div class="links">
<%= link_to_add_association 'add email', f, :emails %>
</div>
<%= end %>
</div>
<%= f.button :submit %>
<% end %>
In your _emails_fields partial:
<div class="nested-fields">
<%= f.input :address %>
<%= link_to_remove_association "Remove email", f %>
</div>
Then setup cocoon's gem and javascript and you'll be good.
Reference: https://github.com/nathanvda/cocoon
I'm trying to create two model in just one form. I have user and unit model both are associated with has_many :through association.
user.rb
class User < ActiveRecord::Base
has_many :units, :through => :user_unit_assocs
has_many :user_unit_assocs
end
unit.rb
class Unit < ActiveRecord::Base
has_many :users, :through => :user_unit_assocs
has_many :user_unit_assocs
end
users_controller.rb
class UsersController < ApplicationController
def create
#user = User.new(user_params)
#user.units.build
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
else
format.html { render :new }
end
end
end
private
def user_params
params.require(:user).permit(:username, :email, units_attributes:[:unitname])
end
end
This is my form, users new
<%= simple_form_for(#user) do |f| %>
<%= f.simple_fields_for :units do |unit| %>
<%= unit.input :unitname, required: true %>
<% end %>
<%= f.input :name, required: true %>
<%= f.input :email, required: true %>
<%= f.button :submit %>
<% end %>
When I run the code, the form only showing a input-field for :name and :email and there is no input-field for units:[:unitname]. How can I show the input-field in the form? Thanks in advance.
References:
1. Rails 4: accepts_nested_attributes_for and mass assignment
2. https://github.com/plataformatec/simple_form/wiki/Nested-Models
Add
def new
#user = User.new
#user.units.build
end
on your controller
You need to build the association in the new action, not the create action.
I have Users, Users have many Clients and Contacts. Clients also have many Contacts (a Contact belongs to both Users and Clients).
In my client view, I want to create a new Client and on the same form allow the user to create the first Contact for that client. Initially, I thought using nested attributes would do the trick but I'm running into some issues. When I go to save #client in clients_controller#create, I can't save because user_id can't be blank and client_id can't be blank for the Contact. Here's what I have so far:
clients controller index (where the new client form is located):
def index
#clients = current_user.clients
#client = Client.new
#contact = #client.contacts.build
respond_to do |format|
format.html # index.html.erb
format.json { render json: #clients }
end
end
and the create method:
def create
#client = current_user.clients.new(params[:client])
respond_to do |format|
if #client.save
and the form:
= form_for(#client) do |f|
= f.fields_for(:contacts) do |contact|
but when I go to save it requires a client_id and user_id...but I can't really set those using the nested attributes. how can I accomplish this? is there a better way of doing this? here's my params:
{"name"=>"asdf", "contacts_attributes"=>{"0"=>{"name"=>"asdf", "email"=>"asdf#gmail.com"}}}
I just tried adding the missing values directly into the contacts_attributes but since #client hasn't been saved yet, I can't assign the client.id to contact:
params[:client][:contacts_attributes]["0"].merge!(:user_id => current_user.id)
params[:client][:contacts_attributes]["0"].merge!(:client_id => #client.id)
even when user_id is set...it still says user is missing.
Did you add accepts_nested_attributes_for to your model? You need something like this:
class Client < ActiveRecord::Base
has_many :contacts
accepts_nested_attributes_for :contacts
end
Also, you should be using build in your create action:
#client = current_user.clients.build(params[:client])
Here's my setup that worked for your example:
app/models/user.rb:
class User < ActiveRecord::Base
attr_accessible :name, :clients_attributes
has_many :clients
accepts_nested_attributes_for :clients
end
app/models/client.rb:
class Client < ActiveRecord::Base
attr_accessible :company, :contacts_attributes
belongs_to :user
has_many :contacts
accepts_nested_attributes_for :contacts
end
app/models/contact.rb:
class Contact < ActiveRecord::Base
attr_accessible :email
belongs_to :client
end
app/controllers/users_controller.rb:
class UsersController < ApplicationController
# ...
def new
#user = User.new
#client = #user.clients.build
#concact = #client.contacts.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
# ...
end
The actual form:
<% # app/views/users/new.erb %>
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.fields_for :clients do |client_form| %>
<h5>Client</h5>
<%= client_form.label :company, "Company name" %>
<%= client_form.text_field :company %>
<div class="field">
<h5>Contact</h5>
<%= client_form.fields_for :contacts do |contact_form| %>
<%= contact_form.label :email %>
<%= contact_form.text_field :email %>
<% end %>
</div>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The form looks like this:
Here's how params sent by forms look like:
{
"utf8"=>"✓",
"authenticity_token" => "bG6Lv62ekvK7OS86Hg/RMQe9S0sUw0iB4PCiYnsnsE8=",
"user" => {
"name" => "My new user",
"clients_attributes" => {
"0" => {
"company" => "Client's Company Name LLC",
"contacts_attributes" => {
"0" => {
"email" => "emailbox#client.com"
}
}
}
}
},
"commit" => "Create User"
}
Update
To enable adding more companies/contacts afterwards, change the UsersController#edit action to this:
class UsersController < ApplicationController
# ...
def edit
#client = current_user.clients.build
#concact = #client.contacts.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
# ...
end
The following validations may be causing this problem since the parent id has not assigned to a child object at the time the validations are run for it. The workaround to this is to either remove these validations or set them to run on update only. This way you lose out on these validations for the create method but it works.
# client.rb
validates :user, presence: true
# contact.rb
validates :client, presence: true
Hi I'm currently working on my first rails project (a photo uploading site), and am getting an error on a form that creates a new photo album that belongs to a user. I'm trying to use the Paperclip gem. It's giving me the error:
ActiveRecord::RecordNotFound in AlbumsController#create
Couldn't find User without an ID
I'm guessing it has something to do with the instance variables in my create action, but I don't see what's wrong. Any guidance would be helpful. Here are my files:
_form.html.erb
<%= form_for (#album), :remote => true, :html => { :id => "uploadform", :multipart => true } do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.fields_for :avatar do |avatar_form| %>
<%= avatar_form.label :avatar, "Upload" %>
<%= avatar_form.file_field :avatar, :multiple => true %>
<% end %>
<%=f.submit %>
</div>
<% end %>
albums_controller create action:
def create
#users = User.all
#user = User.find(params[:user_id])
#album = #user.albums.build(params[:album])
if #album.save
flash[:success] = "Album created!"
end
end
config/routes
Pholder::Application.routes.draw do
resources :users do
resources :albums
end
resources :albums do
resources :pictures
end
Album model
class Album < ActiveRecord::Base
attr_accessible :avatar, :name, :description
has_and_belongs_to_many :users
has_attached_file :avatar
end
Let me know if you need to see any other files.
Since this is a new album, there is no user_id in the albums hash as I first thought. I added a new method that merges in the user id to the hash.
def create
#users = User.all
#album = Album.new(params[:album].merge!(:user_id => current_user))
if #album.save
flash[:success] = "Album created!"
end
end
If you would rather not have user_id in attr_accessible in your Album model, you can assign it separately.
def create
#users = User.all
#album = Album.new(params[:album])
#album.user_id = current_user.id
if #album.save
flash[:success] = "Album created!"
end
end
I'm working on an association between two models:
class Person < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :person
end
Many person records exist in the system that don't necessarily correspond to a user, but when creating a user you need to either create a new person record or associate to an existing one.
What would be the best way to associate these two models when the person record already exists? Do I need to manually assign the user_id field, or is there a Rails way of doing that?
Where #user is a recently created user, and #person is an existing person.
#user.person = #person
#user.save
Alternately:
User.new :person => #person, ... #other attributes
or in params form:
User.new(params[:user].merge({person => #person}))
As far as forms go:
<% form_for #user do |f| %>
...
<% fields_for :person do |p| %>
<%= p.collection_select, :id, Person.all, :id, :name, :include_blank => "Use fields to create a person"%>
<%= p.label_for :name%>
<%= p.text_field :name %>
...
<% end %>
<% end %>
And in the user controller:
def create
#user = User.create(params[:user])
#person = nil
if params[:person][:id]
#person = Person.find(params[:person][:id])
else
#person = Person.create(params[:person])
end
#user.person = #person
...
end
If you don't want to create/alter a form for this, you can do
#person_instance.user = #user_instance
For has_many relationships, it would be:
#person_instance.users << #user_instance
You first have to do a nested form :
<% form_for #user do |user| %>
<%= user.text_field :name %>
<% user.fields_for user.person do |person| %>
<%= person.text_field :name %>
<% end %>
<%= submit_tag %>
<% end %>
In your User model :
class User < ActiveRecord::Base
accepts_nested_attributes_for :person
end
If you want the person deleted when the user is :
class User < ActiveRecord::Base
accepts_nested_attributes_for :person, :allow_destroy => true
end
And in your controller do nothing :
class UserController < ApplicationController
def new
#user = User.new
#find the person you need
#user.person = Person.find(:first)
end
def create
#user = User.new(params[:user])
#user.save ? redirect_to(user_path(#user)) : render(:action => :new)
end
end