Im struggling with a nested form in Rails 4, this is the first time that I made a form of that kind. I have read lots of documentation but Im not able to make it work. :-(
I have two models: Person and Disease. One person can have many diseases and one disease belongs to person. Looks quite simple. The form for Diseases is not saved in the database.
Person Model:
class Person < ActiveRecord::Base
belongs_to :user
has_many :diseases, :dependent => :destroy #if you delete a person you also delete all diseases related
has_many :appointments, :dependent => :destroy
validates_presence_of :name, :email
validates :name, :length => {:maximum => 50, :too_long => "name is too long"}
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, format: { :with => VALID_EMAIL_REGEX , message: "is invalid" }
accepts_nested_attributes_for :diseases
end
Disease Model:
class Disease < ActiveRecord::Base
belongs_to :person
has_many :treatments
validates_presence_of :name, :start
validates :name, :length => {:maximum => 50, :too_long => "is too long, you can use the description field"}
validate :start_must_be_before_end, :unless => [:chronical, :unfinished], :presence => true
validates :end, :presence => true, :unless => [:chronical, :unfinished], :presence => true
validates :description, :length => {:maximum => 5000, :too_long => "is too long"}
def start_must_be_before_end
if self[:end] < self[:start]
errors.add(:start, "must be before end time")
return false
else
return true
end
end
end
People Controller:
def create
current_user
#person = Person.new(person_params)
#person.user_id = #current_user.id
respond_to do |format|
if #person.save
format.html { redirect_to #person, notice: 'Person was successfully created.' }
format.json { render action: 'show', status: :created, location: #person }
else
format.html { render action: 'new' }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
def person_params
params.require(:person).permit(:name, :surname, :gender, :birthdate, :bloodtype, :user_id, :phone, :email, diseases_attributes: [:id, :description] )
end
Form:
<%= simple_form_for #person do |f| %>
<%= f.input :name %>
<%= f.input :email %>
<%= simple_fields_for :diseases do |my_disease| %>
<%= my_disease.input :description %>
<% end %>
<%= f.button :submit %>
<% end %>
Thanks a lot for your help.
The issue is with line <%= simple_fields_for :diseases do |my_disease| %>.It should be
<%= f.simple_fields_for :diseases do |my_disease| %>
This should work.
<%= simple_form_for #person do |f| %>
<%= f.input :name %>
<%= f.input :email %>
<%= f.simple_fields_for :diseases do |my_disease| %> #here
<%= my_disease.input :description %>
<% end %>
<%= f.button :submit %>
<% end %>
For more Info,see this API
Related
I am trying to validate presence of :picture on a user_items model in a nested form.
item.rb
validates :name, presence: true, uniqueness: true
validates :description, presence: true
has_many :user_items, inverse_of: :item
has_many :users, -> { uniq }, through: :user_items
belongs_to :user
accepts_nested_attributes_for :user_items
validates_associated :user_items
user_item.rb
belongs_to :user
belongs_to :item, inverse_of: :user_items
mount_uploader :picture, PictureUploader
validates :picture, presence: true
validate :picture_size
items_controller.rb
def new
#item = Item.new
#item.user_items.build
end
def create
#item = item.new item_params
if #item.save
redirect_to items_path, notice: "Thank you for your item request!"
else
render :new
end
end
private
def item_params
params.require(:item).permit(:name, :description, :tag_list, user_items_attributes: [:picture]).merge(created_by: current_user.id)
end
new.html.erb
<%= simple_form_for #item, html: { class: "create-item-form" } do |item_builder| %>
<div class="well">
<%= item_builder.input :name, required: false, error: false, label: "Item name" %>
<%= item_builder.input :description, as: :text, required: false, error: false, label: "Description of item" %>
<%= item_builder.input :tag_list, required: false, label: "Tags (these will help users find your item)" %>
<%= item_builder.simple_fields_for :user_items do |user_item_builder| %>
<%= user_item_builder.input :picture, as: :file, required: false, label: "Picture of you with this item" %>
<% end %>
</div>
<div class="clearfix">
<%= item_builder.submit 'Submit new item request', class: "btn btn-primary pull-right inherit-width" %>
</div>
<% end %>
This is not working. I submit a blank form and only the name and description validations trigger.
Update:
Oddly enough it seems the validation will work if I add a hidden_field to my form for the user_id:
<%= user_item_builder.input :user_id, as: :hidden, input_html: { value: current_user.id } %>
What is going on here?
Update2:
The validation also seems to work if I change the create action:
def create
#item = item.new item_params
#item.user_items.build
if #item.save
redirect_to items_path, notice: "Thank you for your item request!"
else
render :new
end
end
Why do I need to add #item.user_items.build? What's the correct way to do this?
I have models for Venues and Photos:
class Venue < ActiveRecord::Base
has_and_belongs_to_many :photos
validates :name, presence: true
validates :permalink, presence: true, uniqueness: true
def to_param
permalink
end
end
class Photo < ActiveRecord::Base
mount_uploader :image, PhotoUploader
has_and_belongs_to_many :venues
end
I'm using simple_form to make the following form:
<div class="content-box">
<%= simple_form_for [:admin, #venue] do |f| %>
<%= f.input :name %>
<%= f.input :permalink %>
<%= f.input :description, input_html: { cols: 100, rows: 6 }%>
<%= f.input :address %>
<%= f.input :city %>
<%= f.input :phone %>
<%= f.association :photos %>
<%= f.button :submit, class: 'small radius' %>
<% end %>
</div>
And here is my controller for the Edit and Update methods:
class Admin::VenuesController < ApplicationController
def edit
#venue = Venue.find_by_permalink!(params[:id])
#photos = #venue.photos.all
end
def update
#venue = Venue.find_by_permalink!(params[:id])
if #venue.update_attributes(venue_params)
redirect_to edit_admin_venue_path(#venue), notice: 'Venue was successfully updated.'
else
render action: "edit"
end
end
private
def venue_params
params.require(:venue).permit(:name, :permalink, :description, :address, :city, :phone, :photo_ids)
end
end
The problem is that when I update using the form, all of the attributes for the venue model update fine, but the photo_ids are not updated or stored. I'm guessing it's something simple, but I'm not sure what it is. I'm using Rails 4, btw.
You need to permit photo_ids as an Array because photo_ids is passed as an Array upon form submission. Currently, photo_ids are not getting updated as you didn't permit them as an Array.
Update the venue_params as below:
def venue_params
params.require(:venue).permit(:name, :permalink, :description, :address, :city, :phone, :photo_ids => [])
end
Notice: :photo_ids => [] and NOT :photo_ids
I'm trying to update my associated model, but it's not updating to the database and i'm not sure what to try next.
Model
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation,
:remember_me, :username, :login, :first_name,
:last_name, :home_phone, :cell_phone,
:work_phone, :birthday, :home_address,
:work_address, :position, :company, :user_details
has_one :user_details, :dependent => :destroy
accepts_nested_attributes_for :user_details
end
class UserDetails < ActiveRecord::Base
belongs_to :user
attr_accessible :home_phone, :position
end
Controller
# PUT /contacts/1/edit
# actually updates the users data
def update_user
#userProfile = User.find(params[:id])
respond_to do |format|
if #userProfile.update_attributes(params[:user])
format.html {
flash[:success] = "Information updated successfully"
render :edit
}
else
format.html {
flash[:error] = resource.errors.full_messages
render :edit
}
end
end
end
View
<%= form_for(#userProfile, :url => {:controller => "my_devise/contacts", :action => "update_user"}, :html => {:class => "form grid_6"}, :method => :put ) do |f| %>
<%= f.label :username, "Username" %>
<%= f.text_field :username, :required => "required" %>
<%= f.fields_for :user_details do |d| %>
<%= d.label :home_phone, "Home Phone" %>
<%= d.text_field :home_phone %>
<% end %>
<% end %>
<% end %>
Your attr_accessible should accept user_details_attributes, not just user_details.
Hi I'm currently making a web app for uploading pics. There are users, albums, and photos. I'm having trouble trying to figure out what's going on when I try to edit a user's profile information. It is telling me that there is an undefined method 'album' NoMethodError in UsersController#update , but I don't even call album in my #update action.
def update
respond_to do |format|
if current_user.update_attributes(params[:user])
format.html { redirect_to current_user, notice: 'User successfully updated.'}
format.json { render json: current_user, status: :created, location: current_user }
else
format.html { render action: 'edit' }
format.json { render json: current_user.errors, status: :unprocessable_entity }
end
end
end
the error is specifically on the 2nd and 3rd lines of my update action. Here's the error:
app/controllers/users_controller.rb:45:in 'block in update'
app/controllers/users_controller.rb:44:inupdate'`
anyone know what the heck is going on?
edit form for users
<%= form_for(#user) do |f| %>
<div class="formhold">
<div class="field">
<% if #user.profilepic? %>
<%= image_tag #user.profilepic.url %>
<% end %>
<%= f.label :profilepic, "Profile Picture" %>
<%= f.file_field :profilepic %>
</div>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div>
<%= f.submit 'Submit', :class => 'submitbutton' %>
</div>
<% if #user.errors.any? %>
<div id="error_exp">
<h2><%= pluralize(#user.errors.count, "error") %> occurred.</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<% end %>
<%= link_to "Back", user_path(#user) %>
user model
class User < ActiveRecord::Base
has_secure_password
attr_accessible :email, :name, :password, :password_confirmation, :profilepic
validates_presence_of :password, :on => :create
validates_format_of :name, :with => /[A-Za-z]+/, :on => :create
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
validates_length_of :password, :minimum => 5, :on => :create
validates :album, :uniqueness => true
has_many :album_users
has_many :albums, :through => :album_users
accepts_nested_attributes_for :albums
has_many :friendships, :dependent => :destroy
has_many :friends, :through => :friendships, :conditions => "status = 'accepted'"
has_many :requested_friends, :through => :friendships, :source => :friend, :conditions => "status = 'requested'", :order => :created_at
has_many :pending_friends, :through => :friendships, :source => :friend, :conditions => "status = 'pending'", :order => :created_at
has_attached_file :profilepic
before_save { |user| user.email = email.downcase }
def name_with_initial
"#{name}"
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Try commenting out:
validates :album, :uniqueness => true
It is assuming that :album is an attribute/method of user and failing. You should handle your unique validations for albums in albums model, depending on your business logic. You can validate them based on user_id or some other attribute with :scope => [:user_id]
I'm using rails 3.2 and I building a nested form. But things are not working as I expect. First of all, my model is a Company with has many Addresses. Here is the model
class Company
include Mongoid::Document
include Mongoid::Timestamps
field :name, :type => String
field :description, :type => String
field :order_minimun, :type => Float
belongs_to :user
has_many :addresses
validates_presence_of :name, :description, :order_minimun
validates_length_of :name, minimum:2, maximum: 30
validates_length_of :description, minimum:5, maximum: 140
validates :order_minimun, :numericality => { :only_integer => true, :greater_than_or_equal_to => 0 }
accepts_nested_attributes_for :addresses
validates_associated :addresses
end
class Address
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Spacial::Document
field :street, :type => String
field :number, :type => Integer
field :phone, :type => String
field :location, :type => Array, spacial: {lat: :latitude, lng: :longitude, return_array: true }
embeds_many :delivery_zones
belongs_to :company
belongs_to :city
has_many :openingTimeRange
validates_presence_of :street, :number
validates_length_of :street, minimum:1, maximum: 30
validates_length_of :number, minimum:1, maximum: 6
validates_length_of :phone, minimum:5, maximum: 60
attr_accessible :street, :number, :company_id, :city_id, :location, :phone, :delivery_zones, :latitude, :longitude
end
As you can see the Company model has:
accepts_nested_attributes_for :addresses
validates_associated :addresses
So, I think a can build a nested form. Here is the code of the form
<%= form_for [:admin,#company],:url =>admin_company_path(#company), :html => {:class => "form-horizontal"} do |f|%>
<legend><%= t '.legend' %></legend>
<%= group_input_field f, :name%>
<%= group_field_for f, :description do%>
<%= f.text_area :description, :rows => 5%>
<% end -%>
<%= group_input_field f, :order_minimun%>
<%= f.fields_for :addresses do |builder|%>
<%= render 'address_fields', :f=> builder%>
<% end %>
<div class="form-actions">
<%= f.submit :class => 'btn btn-primary btn-large', :disable_with => t('button.saving') %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
admin_companies_path, :class => 'btn btn-large btn-danger' %>
</div>
<% end %>
the _address_fields.html.erb
<%= group_input_field f, :street%>
<%= group_input_field f, :number%>
I have a simple helper to generate the form fields with bootstrap
def group_input_field(f,field, options={})
has_error = f.object.errors.has_key? field
klass = has_error ? "control-group error": "control-group"
content_tag(:div, :class => klass) do
f.label(field, :class => 'control-label')+
content_tag(:div, :class => 'controls') do
f.text_field(field, :class => 'input')+
show_error(f.object,field,has_error)+
show_help(options)
end
end
end
Finaly the controller:
class Admin::CompaniesController < ApplicationController
def new
#crea una nueva compaƱia
#company = Company.new
end
def edit
#company = Company.find params[:id]
end
def create
#company = Company.new(params[:company])
if #company.save
redirect_to :action => 'index'
else
render 'new'
end
end
def update
#company = Company.find(params[:id])
if #company.update_attributes(params[:company])
redirect_to :action => 'index'
else
render 'edit'
end
end
end
What is happening a couple of things. First, I have a company with two addresses, and I am able to edit the first one, any change on the second one is not persisted. Then the addresses fields are not validated (if i leave everything in blank when i open the form again the addresses were not save and I can see the original values). And when edit a field in any address, and any field value of the company is not valid, after the form is submited i can see the errors on the company model, but the address are displayed with the original values, so the edited values were lost.
Hope be clear.
Thanks in advance.
Well, I've found the answer. I was using the version of mongoid 3.0.4. I run the command bundle update mongoid and mongoid was updated to the version 3.0.6. And the problems were fixed.
Thanks. Hope it helps