I want every user to upload a profile image on sign up. I have two models, a User model, and an Image model. So how should I update the User model with a new user, and an Image model with a new image for that user with one form?
user model
class User < ActiveRecord::Base
has_one :profile_image, class_name: 'Image', foreign_key: 'user_id'
end
image model
class Image < ActiveRecord::Base
# do I need to put something else here to make this relationship work?
end
user/new view
<%= form_for(#user) do |f| %>
<%= f.label :email %><br>
<%= f.text_field :email %>
<%= f.label :password %><br>
<%= f.password_field :password %>
# this needs to update a seperate params hash, but how?
<%= f.file_field :profile_image %>
<%= f.label :password_confirmation %><br>
<%= f.password_field :password_confirmation %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
user#create action
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
# save their uploaded image
Image.create() # help needed here!
sign_in #user
format.html { redirect_to #user, notice: 'Welcome, ' + #user.user_name + '!' }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
# how do I create a method to require certain parameters for the Image?
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :profile_image)
end
Now I've actually done the hard bit of creating their image in the filesystem (via rmagick), the easy bit, simply updating the images table with information pertaining to this image, is where I'm struggling!
Also I know of nested forms, but I'm thinking the new image and the new user should both be created in the users#create action. It makes it a lot easier to see what's going on rather than if the image was created in its own image#create action. I was also thinking of putting the user creation and the image creation in a transaction block, but this is impossible if they were both happening in their own actions, so I do think the answer is splitting a form into two params hashes and doing something with the first hash, then doing something else with the second hash.
Use accepts_nested_attributes_for, it's a little hard to figure out at first, but it's exactly what you need in this case. Something like this:
user.rb:
class User < ActiveRecord::Base
has_one :profile_image, class_name: 'Image', foreign_key: 'user_id'
accepts_nested_attributes_for :profile_image
end
the view:
<%= f.fields_for :profile_image do |f| %>
<%= file_field :file %>
<% end %>
The controller seems to be all set.
Related
i have a restaurant controller, and in the show method i render a form of another controller (dishes)
#new.html.erb (from dishes controller)
<%= render 'dishes/dish_form' %>
#show.html.erb (from restaurant controller)
<%= render template: 'dishes/new' %>
and this is the form:
<%= form_for #dish, :url => { :controller => "dishes", action_name => "create" } do |f| %>
<div class="field">
<%= f.label :dish_name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.label :image %>
<%= f.file_field :avatar %>
</div>
<div class="actions">
<%= f.submit 'Add new dish' %>
</div>
<% end %>
but when i try so add a dish, i have this error
this is my dishes controller:
def new
#dish = Dish.new
end
def create
#dish = Dish.new(dish_params)
respond_to do |format|
if #dish.save
format.html { redirect_to #restaurant, notice: 'dish was successfully created.' }
format.json { render action: 'show', status: :created, location: :restaurant }
else
format.html { render action: 'show', location: :restaurant }
format.json { render json: #restaurant.errors, status: :unprocessable_entity }
end
end
end
private
def dish_params
params.require(:dish).permit(:avatar, :name, :description)
end
and this are my models:
class Restaurant < ApplicationRecord
has_many :dish, inverse_of: :restaurant
accepts_nested_attributes_for :dish
end
class Dish < ApplicationRecord
belongs_to :restaurant
end
im learning rails so maybe is a dumb error but im stuck
you should try to this to the controller action where the form is rendered
#dish = Dish.new
as you directly call the 'create' action rails is missing the instance variable Dish.new. Therefore the error message. Normally rails works like this:
def new
#dish = Dish.new
end
and than you call the create action on 'submit'.
However, as it looks like you call the form from the #show action just add this code there and you will be fine. May be not the best solution but it will work like that. Add to #show
#dish = Dish.new
Sorry mate. Yes #dish is correct and not :dishes.
OK.
My latest guess is that you want the restaurants to be able to create many dishes.
So you should set up your models accordingly:
#restaurants_model
has_many :dishes
#dishes_model
belongs_to :restaurant
next is that you add a column called restaurant_id to your dishes table
t.string :restaurant_id
than in your restaurant controller #show
def show
end
if you use the routes as normal, e.g.
resources :restaurants
resources :dishes
the show action of the restaurant controller should give you a url that looks like this: localhost:3000/restaurants/1
where the 1 is the id of the restaurant.
This id you want to save to the dish by adding a hidden field
=f.hidden_field :restaurant_id, value: #restaurant.id
you need to permit the restaurant_id in the dishes controller params, e.g. dish_params. Just add
:restaurant_id
and it should be fine. This way the id will be saved to the dishes table and you can later call it from there.
This gives you the chance to call #restaurant.dishes which will show all dishes of that restaurant.
If you just want to redirect back you can use redirect :back
Otherwise, you can try to integrate a helper to get the right restaurant using the hidden restaurant_id field
I have two models user and profile.
I want to save the username and password in user and other user profile details in profile.
Now,
The user model has:
has_one :profile
accepts_nested_attributes_for :profile
attr_accessible :email, :password
The profile model has
belongs_to :user
attr_accessible :bio, :birthday, :color
The user controller has
def new
#user = User.new
#profile = #user.build_profile
end
def create
#user = User.new(params[:user])
#profile = #user.create_profile(params[:profile])
if #user.save
redirect_to root_url, :notice => "user created successfully!"
else
render "new"
end
end
The view new.html.erb have fields for both user and profile.
However,when I run this web application it shows error:
Can't mass-assign protected attributes: profile
on debug it stuck at #user = User.new(params[:user]) in create action
so,what is wrong? I have also tried putting :profile_attributes in attr_accessible but it doesn't help!please help me to find out solution.
First off, as suggested by #nash, you should remove #profile = #user.create_profile(params[:profile]) from your create action. accepts_nested_attributes_for will automatically create your profile for you.
Check that your view is set up correctly for nested attributes. Should shouldn't be seeing anything in params[:profile]. The profile attributes need to come through in params[:user][:profile_attributes] for nested models to work correctly.
In summary, your create action should look like this:
def create
#user = User.new(params[:user])
if #user.save
redirect_to root_url, :notice => "user created successfully!"
else
render "new"
end
end
Your form view (typically _form.html.erb) should look something like this:
<%= form_for #user do |f| %>
Email: <%= f.text_field :email %>
Password: <%= f.password_field :password %>
<%= f.fields_for :profile do |profile_fields| %>
Bio: <%= profile_fields.text_field :bio %>
Birthday: <%= profile_fields.date_select :birthday %>
Color: <%= profile_fields.text_field :color %>
<% end %>
<%= f.submit "Save" %>
<% end %>
For more information, see this old but great tutorial by Ryan Daigle.
I've searched everywhere for a solution but haven't come up with any.
The part that works: My app allows customers to create an account using a nested form. The data collected creates records in four models - accounts, users, accounts_users (because a user can be associated with many accounts), and profile (to store the user's fname, lname, phone, etc).
That part that doesn't work: Once logged in, I want the users to be able to add more users to their account using the form below. I don't receive any errors upon submit but I am brought back to the same form with no additional records created. Any help would be awesome!
Here is the nested form...
<%= form_for #user, :validate => true do |f| %>
<fieldset>
<%= f.fields_for :profile do |p| %>
<div class="field">
<%= p.label :first_name %>
<%= p.text_field :first_name %>
</div>
<div class="field">
<%= p.label :last_name %>
<%= p.text_field :last_name %>
</div>
<div class="field">
<%= p.label :phone %>
<%= p.text_field :phone %>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit 'Create New User', :class => "btn btn-large btn-success" %>
<%= cancel %>
</div>
</fieldset>
The ApplicationController scopes everything to the current_account like so:
def current_account
#current_account ||= Account.find_by_subdomain(request.subdomain) if request.subdomain
end
The UsersController
def new
#user = User.new
#user.build_profile()
#current_account.accounts_users.build() #Edit2: This line was removed
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
def create
#user = User.new(params[:user])
#user.accounts_users.build(:account_id => current_account.id) #Edit2: This line was added
if #user.save
# Send Email and show 'success' message
flash[:success] = 'An email has been sent to the user'
else
# Render form again
render 'new'
end
end
Models look like this:
class Account < ActiveRecord::Base
attr_accessible :name, :subdomain, :users_attributes
has_many :accounts_users
has_many :users, :through => :accounts_users
accepts_nested_attributes_for :users
end
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :profile_attributes
has_many :accounts_users
has_many :accounts, :through => :accounts_users
has_one :profile
accepts_nested_attributes_for :profile
end
class AccountsUser < ActiveRecord::Base
belongs_to :account
belongs_to :user
end
class Profile < ActiveRecord::Base
belongs_to :user
attr_accessible :first_name, :last_name, :phone
end
Edit2: It turns out that I had required a password + password_comfirmation validation in the User model which prevented me from adding another user without these fields. I commented out these validations plus removed the line: current_account.accounts_users.build() in the 'new' action and added the line: #user.accounts_users.build(:account_id => current_account.id) in the 'create' action.
"I want the users to be able to add more users to their account using the form below." I assume you mean profiles (since your nested form is on profiles)?
If that's the case, I think your UsersController's create action isn't associating the profiles with users by using new.
Try this...
def new
#user = User.build
#profile = #user.profiles.build #build adds the profile to user's associated collection of profiles, but new doesn't
...
end
def create
#user = User.build(params[:user])
if #user.save
....
end
end
If you want the user to be associated with account, then you need to put the new and create actions in the AccountsController and do something similar to nest association of the users and profiles records.
Btw, the reason that it went back to new is because you render new at the end of the create, in case that's also part of the question. Hope that helps!
I've got a simple nested form working in my rails3 application. I'm trying to work out how to save a value from the parent model into the child when saving.
class Radcheck < ActiveRecord::Base
set_table_name 'radcheck'
attr_accessible :attribute_name, :username, :value, :op
belongs_to :raduser
end
class Raduser < ActiveRecord::Base
has_many :radcheck, :dependent => :destroy
accepts_nested_attributes_for :radcheck
end
And my form:
<%= form_for #raduser do |f| %>
<p>
<%= f.label :username %><br />
<%= f.text_field :username %>
</p>
<%= f.fields_for :radcheck do |builder| %>
<li>
<%= builder.label :attribute_name %><%= builder.text_field :attribute_name %>
</li>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
What I want to do is save the Raduser.username value in to the radcheck table on save. For each record.
I've tried putting something in the controller but that wasn't really working for us.
-- Update --
In my radcheck controller, I've tried this (as a test) but the values don't save.
def create
#radcheck = Radcheck.new(params[:radcheck])
#radcheck.username = '123'
respond_to do |format|
if #radcheck.save
format.html { redirect_to #radcheck, notice: 'Radcheck was successfully created.' }
format.json { render json: #radcheck, status: :created, radcheck: #radcheck }
else
format.html { render action: "new" }
format.json { render json: #radcheck.errors, status: :unprocessable_entity }
end
end
end
Have also tried in radusers but that gave error.
It looks like the controller code you posted is for the Radcheck controller, correct? If so, the form you have also posted will be using the create action of the RaduserController class, not the one from RadcheckController. This would explain why you aren't seeing the username '123' in the radcheck rows.
If the username field is the same between parent and child, a common way to sync these two up would be with an observer or a before_save callback. I'll outline the before_save method below:
class Radcheck < ActiveRecord::Base
set_table_name 'radcheck'
attr_accessible :attribute_name, :username, :value, :op
belongs_to :raduser
before_save :sync_usernames
private
def sync_usernames
self.username = self.raduser.username
end
end
I've tested this with Rails 3.1 and it works; however, I know I have run into issues accessing parent models within a child model in previous versions of Rails. In those cases, I had to put the before_save action on the parent model, and have it iterate over each child, setting the appropriate attributes that way.
I have these models:
class User < ActiveRecord::Base
has_one :city
accepts_nested_attributes_for :city
end
class City < ActiveRecord::Base
belongs_to :user
end
This controller action:
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
and this view:
<%= form_for :user,:url => users_path,:method => :post do |f| %>
<%= f.fields_for :city do |b| %>
<%= b.collection_select :id,City.all,:id,:name %>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I am trying to allow the user to select a city from the list of already added cities. I am trying to present him a select. The select part it works, but the generated html code for it, looks like this:
<select name="user[city][id]" id="user_city_id">
<option value="1">One</option>
<option value="2">Two</option>
</select>
Notice that it's name doesn't have attribute anywhere. So, when I try to save it, I get this error:
City(#37815120) expected, got ActiveSupport::HashWithIndifferentAccess(#32969916)
How can I fix this?
EDIT: there is some progress, I tried to change the fields_for to this:
<%= f.fields_for :city_attributes do |b| %>
<%= b.collection_select :id,City.all,:id,:name %>
<% end %>
and now, the html seems to generate correctly. But I get this error now:
Couldn't find City with ID=1 for User with ID=
I have no idea what to do next.
EDIT2: overriding the city_attributes= method seems to work:
def city_attributes=(attribs)
self.city = City.find(attribs[:id])
end
I don't know if it's the way to go, but it seems good.
Have a look at this question that seems similar to yours :
Rails 3: How does "accepts_nested_attributes_for" work?
Actually, since the Cities already exsit, I think there is no need for nested forms here.
Try Replacing
<%= f.fields_for :city_attributes do |b| %>
<%= b.collection_select :id,City.all,:id,:name %>
<% end %>
With
<%= f.collection_select :city, City.all,:id,:name %>
Updated afters comments
Could you change your relationship with (and update database scheme accordingly)
class User < ActiveRecord::Base
belongs_to :city
end
class City < ActiveRecord::Base
has_many :users
end
And then try using:
<%= f.collection_select :city_id, City.all,:id,:name %>
You could also do a
<%= f.collection_select :city_id, City.all, :id, :name %>
in your view and then add virtual attributes to your User model:
class User < ActiveRecord::Base
...
def city_id(c_id)
update_attribute(:city, City.find(c_id))
end
def city_id
city.id
end
end
This might not be very clean, since the associated City model is "saved" whenever assigning an ID to some_user.city_id. However, this solution keeps your controller and view nice and clean.
Note: you might also want to account for a blank ID being passed in to the setter method.
Try this
<%= f.select(:city_id, City.all.collect {|p| [ p.name, p.id ] }) %>