I have 3 models with has_many :through relation. Users, Events, and Galleries. In the method new and create in gallery_controller I need to get the event_id, however I get a nil event_id. But in the mozilla console and in the parameters, there exists the id. I don't what I am doing wrong?
I also want to know if the structure of new and create actions is ok ? I want add a gallery for a event before created and in the same time in the the current_user galleries, i have not can test it by the previous problem.
Thanks and cheers.
class Event < ActiveRecord::Base
has_many: galleries
has_many: users, through: : galleries, : source => : users, : dependent => : destroy
accepts_nested_attributes_for: users
accepts_nested_attributes_for: galleries
end
class User < ActiveRecord::Base
has_many :galleries
has_many :events, through: :galleries, :dependent => :destroy
accepts_nested_attributes_for :events
accepts_nested_attributes_for :galleriesenter code here
end
class Gallery < ActiveRecord::Base
has_many :pictures, :dependent => :destroy
belongs_to :event
belongs_to :user
end
Gallery_controller
def new
#event = Event.find(params[:event_id])
#galery = Gallery.new
respond_to do |format |
format.html# new.html.erb
format.json {
render json: #gallery
}
end
end
def create
#event = Event.find(params[:id])
#gallery = #event.galleries.build(params[:gallery])
#gallery.user = current_user
respond_to do |format |
if# gallery.save
if params[: images]# The magic is here;)
params[: images].each { | image | #gallery.pictures.create(image: image)
}
end
def gallery_params
params.require(:gallery).permit(:description,
:name,
:pictures,
:event_attributes => [],
:user_attributes => [],
)
end
form_ new gallery
<%= form_for [#event,#gallery], :html => { :class => 'form-horizontal', multipart: true } do |f| %>
<div class="control-group">
<%= f.label :name, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :name, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :description, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :description, :class => 'text_field' %>
</div>
</div>
<div class="control-group">
<%= f.label :pictures, :class => 'control-label' %>
<div class="controls">
<%= file_field_tag "images[]", type: :file, multiple: true %>
</div>
</div>
<div class="form-actions">
<%= f.submit :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
galleries_path, :class => 'btn btn-mini' %>
</div>
<% end %>
routes
resources :events do
resources :galleries
end
Image error
http://i.stack.imgur.com/mk1Ti.png
The error is because you have a typo in your new method.
This line
#galery = Gallery.new
should be
#gallery = Gallery.new
Furthermore your create method has some mistakes which needs fixing.
def create
#event = Event.find(params[:event_id])
#gallery = #event.galleries.build(gallery_params)
#gallery.user = current_user
respond_to do |format|
if #gallery.save
if params[:images]
params[:images].each { |image| #gallery.pictures.create(image: image)}
end
format.html { redirect_to #gallery, notice: 'Gallery was successfully created.' }
format.json { render json: #gallery, status: :created, location: #gallery }
else
format.html { render :new }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
And also your gallery_params needs tweaking
def gallery_params
params.require(:gallery).permit(:description,:name)
end
You don't want to include :event_attributes => [], :user_attributes => [] unless your form has nested fields for users and events which needs to be saved.
I think I found your problem. To me there seems more problems. But initially to solve your problem: you need add event_id to permit params methods:
params.require(:gallery).permit(:description,
:name,
:pictures,
:event_id, # this line should be here if your foreign key is event_id for gallery model.
:event_attributes => [],
:user_attributes => [],
)
Also your form doesn't content the right instance variable. Differences here:
<%= form_for [#event,#gallery], :html => { :class => 'form-horizontal', multipart: t # you wrote #gallery here but in your controller you wrote:
# controller's action:
#galery = Gallery.new
Suggested way to keep the foreign key hidden as well as you build from events also:
#event = Event.find(params[:event_id])
#gallery = #event.gallaries.build #note: 1.spelling and 2. building from #event object
Then in your form add the foreign key field as hidden:
<%= f.hidden_field :event_id %>
Related
I have two controllers - ItemsController and TradesController. I'm building a #trade inside the ItemsController #show action, which is sent to the TradesController #create action with a form.
class ItemsController < ApplicationController
def show
#item = Item.friendly.find(params[:id])
#trade = current_user.requested_trades.build
#approved_trades = #item.trades
respond_to do |format|
format.html
format.json { render :json => #items.to_json(:methods => [:image_url]) }
end
end
class TradesController < ApplicationController
def create
#trade = current_user.requested_trades.build(trade_params)
respond_to do |format|
if #trade.save
format.html { redirect_to #trade, notice: "Your request for trade has been submitted. You will be notified once it is approved or denied." }
format.json { render :index, status: :created, location: #trade }
else
format.html { redirect_to #trade, notice: "Pick another amount" }
end
end
end
private
def trade_params
params.require(:trade).permit(:trade_requester, :trade_recipient, :wanted_item, :collateral_item, :shares)
end
end
And then here's my Trade model
class Trade < ActiveRecord::Base
belongs_to :trade_requester, class_name: "User"
belongs_to :trade_recipient, class_name: "User"
belongs_to :wanted_item, class_name: "Item"
belongs_to :collateral_item, class_name: "Item"
end
Here's the form in my Item's show view:
<%= form_for(#trade) do |f| %>
<%= f.hidden_field :wanted_item, value: #item.id %>
<div class="field">
<%= f.text_field :shares, placeholder: "Pick a number between 1 and #{#item.shares}" %>
<%= f.submit "Trade", class: "button minty-button wide-button" %>
</div>
<% end %>
The above code for the ItemsController posts to the TradesController create action, but I'm getting an error that says ActiveRecord::AssociationTypeMismatch in TradesController#createItem(#70095717466760) expected, got String(#70095657672800)
Why is that expecting an Item? It seems that if #trade creation results in error, then it should redirect to #trade.
The quick solution is to change your hidden field from :wanted_item to :wanted_item_id:
<%= form_for(#trade) do |f| %>
<%= f.hidden_field :wanted_item_id, value: #item.id %>
<div class="field">
<%= f.text_field :shares, placeholder: "Pick a number between 1 and #{#item.shares}" %>
<%= f.submit "Trade", class: "button minty-button wide-button" %>
</div>
<% end %>
Also, make sure your trade_params method permits wanted_item_id:
def trade_params
params.require(:trade).permit(:trade_requester, :trade_recipient, :wanted_item_id, :collateral_item_id, :shares)
end
You may have a similar issue with :collateral_item in another form.
I am required to use nested forms on an assignment I am working on and I got stuck because my nested form attributes wont submit to database.
Here is what my controller looks like
def new
#booking = Booking.new
params[:no_of_passengers].to_i.times { #booking.passengers.build }
end
def create
#booking = Booking.new(booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to '/booking_confirmed', notice: 'Booking was successfully created.' }
format.json { render :show, status: :created, location: #booking }
else
format.html { render :new }
format.json { render json: #booking.errors, status: :unprocessable_entity }
end
end
end
private
def booking_params
params.permit(
:airline, :origin, :destination, :departure_date, :departure_time, :arrival_date,
:arrival_time, :flight_id, :price, :no_of_passengers, :user_id, :booking,
passenger_attributes: [
:id,:booking_id, :name, :email,:done,:_destroy
]
)
end
Here is the association between the models
class Booking < ActiveRecord::Base
has_many :passengers
accepts_nested_attributes_for :passengers, reject_if: lambda { |attributes| attributes['name'].blank? }
end
class Passenger < ActiveRecord::Base
belongs_to :bookings
end
And here is the form
<%= form_for #booking do |b| %>
<%= b.fields_for :passengers do |p| %>
<%= p.text_field :name, placeholder: "Passenger Name" %>
<%= p.text_field :email, placeholder: "Passenger Email" %>
<% end %>
<% end %>
I checked the passenger table using Passenger.all in rails console and it returns nothing.
What am I doing wrong?
After a pairing session with sunnyk, I was able to see the errors.
The first error was that my class Passenger has belongs_to :bookings instead of belongs_to :booking. This is a common error though. The Associations between these classes now looks like:
class Booking < ActiveRecord::Base
belongs_to :flight
has_many :passengers
accepts_nested_attributes_for :passengers, reject_if:
lambda {|attributes| attributes['name'].blank?}, :allow_destroy => true
end
class Passenger < ActiveRecord::Base
belongs_to :booking
end
class Flight < ActiveRecord::Base
has_many :bookings
has_many :passengers, through: :bookings
accepts_nested_attributes_for :passengers
accepts_nested_attributes_for :bookings
end
Next:
Instead of using the default value of no_of_passengers for building my nested form, I used the cocoon gem, which makes nested forms building and management easier. I also crated a new params method, in which I made the flight_id permitted, and then passed it as an argument for my booking instance in my new method alongside my current user. So now my new method looks like this.
def new
#booking = Booking.new(new_booking_params)
#booking.user = current_user if current_user
end
def new_booking_params
params.permit(:flight_id)
end
After that, I had to make another params method for my create method, so as to allow the parameters I want in the bookings table, this include the passengers_attributes. Now my create method looks like this.
def create
#booking = Booking.new(another_booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to '/booking_confirmed', notice: 'Booking was successfully created.' }
format.json { render :show, status: :created, location: #booking }
else
format.html { render :new }
format.json { render json: #booking.errors, status: :unprocessable_entity }
end
end
end
def another_booking_params
params.require(:booking).permit(:flight_id, :user_id, :no_of_passengers,
passengers_attributes:[:name, :email])
end
Lastly, I had to adjust my form to look like this.
<%= form_for(#booking, url: bookings_path) do |f| %>
<%= f.hidden_field(:flight_id)%>
<%= f.hidden_field(:user_id) %>
<%= f.hidden_field(:no_of_passengers)%>
<%= f.fields_for :passengers do |passenger| %>
<%= render 'passenger_fields', :f => passenger %>
<% end %>
<%= link_to_add_association 'Add Another passenger',f, :passengers, :class => 'btn btn-primary add' %>
<%= submit_tag "Book Now", class: "btn btn-primary book" %>
<% end %>
and passenger_fields partial looks like.
<div class="nested-fields form-inline">
<div class="form-group">
<%= f.text_field :name, :class => "form-control", placeholder: "Passenger Name" %>
</div>
<div class="form-group">
<label>-</label>
<%= f.text_field :email, :class => "form-control", placeholder: "Passenger Email" %>
</div>
<div class="links pull-right">
<%= link_to_remove_association "Delete", f, class: "btn btn-danger" %>
</div>
<hr>
</div>
All that did the trick. I hope this will help others to understand nested forms better
I am trying to automatically create a child record (Participant) when I create a parent (Project) record. I can create the parent (Project) fine and, on other forms, I can create the child (Participant). I cannot seem to create the child (Participant) at the same time as the parent.
I am on Rails 4, and so I've set my strong params carefully. I just don't understand what I'm doing wrong.
Parent Controller:
class ProjectsController < ApplicationController
def new_project
#title = params[:ti]
#project = Project.new
#project.participants.build
end
def create_project
#project = Project.new(project_params)
#template = Template.find(params[:t])
#project.participants.build
#title = params[:ti]
respond_to do |format|
if #project.save
#project.participants.save
format.html { redirect_to new_milestones_path(:p => #project.id), notice: 'Great! We saved your project details.' }
else
format.html { redirect_to new_project_path(t: #template.id, ti: #title)
}
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
def project_params
params.require(:project).permit( :id, :title, :starts, participants_attributes: [:id, :email, :title, :status, :project_id])
end
end
Models:
class Participant < ActiveRecord::Base
belongs_to :project, inverse_of: :participants
........
end
class Project < ActiveRecord::Base
has_many :participants, dependent: :destroy, inverse_of: :project
accepts_nested_attributes_for :participants, allow_destroy: true, reject_if: proc { |a| a["email"].blank? }
.........
end
Form:
<%= form_for #project, url: create_project_path(ti: #title), html: { :multipart => true, :class=> "form-horizontal", id: "basicForm" }do |f| %>
<%= f.fields_for :participants do |ff|%>
<%= ff.hidden_field :email, :value => current_user.email %>
<%= ff.hidden_field :title, :value => 'Organizer' %>
<%= ff.hidden_field :status, :value => 'accepted' %>
<% end %>
<%= f.text_field :title, :placeholder => 'Your Project Title'%>
<%= f.text_field :starts, :placeholder => 'mm/dd/yyyy'%>
<%= f.submit ' SAVE PROJECT' %>
<% end %>
UPDATE:
I added #project.participants.build as Samo suggested (and I've updated my code above), which makes the fields_for visible...but my project doesn't save...it redirects back to new_project_path.
I believe I see the issue. In your new_project action, try this:
def new_project
#title = params[:ti]
#project = Project.new
#project.participants.build
end
To elaborate: fields_for is not going to render anything if the association is blank/empty. You need to have at least one participant returned by #project.participants in order to see its fields. #project.participants.build will simply insert a new instance of the Participant class into the association.
As you're using Rails 4 in your application, you don't need to call accepts_nested_attributes_for, because you are already calling params.requirein your Controller.
After #participant = Participant.new you didn't call Participant.saveaction. You do call a #project.save inside your if condition and you should do that for your #participant too. You can call #project.save before redirecting to project_path. I'm not sure if that is a correct approach, but you can try if it works. :-)
I have a nested form based in a has_one relationship but it's not showing the fields.
What am I missing?
New action
def new
#doctor = Doctor.new
1.times { #doctor.build_schedule }
respond_to do |format|
format.html # new.html.erb
format.json { render json: #doctor }
end
end
_form
<%= simple_form_for(#doctor, :html => { class: "form-horizontal"}) do |f| %>
<%= f.input :name %>
<%= f.simple_fields_for :schedule do |builder| %>
<%= render 'days_checkboxes', :f => builder %>
<%= f.submit %>
<% end %>
Model
class Doctor < ActiveRecord::Base
has_one :schedule, dependent: :destroy
end
Do I have to set the build in other actions?
If there is no schedule associated object on your model instance, yes you will need to call build_schedule wherever you want to reference it. If you didn't do this, the form wouldn't render at all because it has nothing to display the fields for.
I'm working on a website that allows people who run bed and breakfast businesses to post their accommodations.
I would like to require that they include a "profile image" of the accommodation when they post it, but I also want to give them the option to add more images later (this will be developed after).
I thought the best thing to do would be to use the Paperclip gem and have a Accommodation and a Photo in my application, the later belonging to the first as an association.
A new Photo record is created when they create an Accommodation. It has both id and accommodation_id attributes. However, the image is never uploaded and none of the Paperclip attributes get set (image_file_name: nil, image_content_type: nil, image_file_size: nil), so I get Paperclip's "missing" photo.
Any ideas on this one? It's been keeping me stuck for a few days now.
Accommodation
models/accommodation.rb
class Accommodation < ActiveRecord::Base
validates_presence_of :title, :description, :photo, :thing, :location
attr_accessible :title, :description, :thing, :borough, :location, :spaces, :price
has_one :photo
end
controllers/accommodation_controller.erb
class AccommodationsController < ApplicationController
before_filter :login_required, :only => {:new, :edit}
uses_tiny_mce ( :options => {
:theme => 'advanced',
:theme_advanced_toolbar_location => 'top',
:theme_advanced_toolbar_align => 'left',
:theme_advanced_buttons1 => 'bold,italic,underline,bullist,numlist,separator,undo,redo',
:theme_advanced_buttons2 => '',
:theme_advanced_buttons3 => ''
})
def index
#accommodations = Accommodation.all
end
def show
#accommodation = Accommodation.find(params[:id])
end
def new
#accommodation = Accommodation.new
end
def create
#accommodation = Accommodation.new(params[:accommodation])
#accommodation.photo = Photo.new(params[:photo])
#accommodation.user_id = current_user.id
if #accommodation.save
flash[:notice] = "Successfully created your accommodation."
render :action => 'show'
else
render :action => 'new'
end
end
def edit
#accommodation = Accommodation.find(params[:id])
end
def update
#accommodation = Accommodation.find(params[:id])
if #accommodation.update_attributes(params[:accommodation])
flash[:notice] = "Successfully updated accommodation."
render :action => 'show'
else
render :action => 'edit'
end
end
def destroy
#accommodation = Accommodation.find(params[:id])
#accommodation.destroy
flash[:notice] = "Successfully destroyed accommodation."
redirect_to :inkeep
end
end
views/accommodations/_form.html.erb
<%= form_for #accommodation, :html => {:multipart => true} do |f| %>
<%= f.error_messages %>
<p>
Title<br />
<%= f.text_field :title, :size => 60 %>
</p>
<p>
Description<br />
<%= f.text_area :description, :rows => 17, :cols => 75, :class => "mceEditor" %>
</p>
<p>
Photo<br />
<%= f.file_field :photo %>
</p>
[... snip ...]
<p><%= f.submit %></p>
<% end %>
Photo
The controller and views are still the same as when Rails generated them.
models/photo.erb
class Photo < ActiveRecord::Base
attr_accessible :image_file_name, :image_content_type, :image_file_size
belongs_to :accommodation
has_attached_file :image,
:styles => {
:thumb=> "100x100#",
:small => "150x150>" }
end
To create an upload with paperclip, you need to use the name you provided for the has_attached_file line, on the model you defined it on. In your case, this will result in this view code:
<%= form_for #accommodation, :html => { :multipart => true } do |f| %>
<%= f.fields_for :photo do |photo_fields| %>
<p>
Photo<br />
<%= photo_fields.file_field :image %>
</p>
<% end %>
<% end %>
In the controller:
class AccommodationsController < ApplicationController
# also protect create and update actions!
before_filter :login_required, :only => [ :new, :create, :edit, :update ]
def new
# always make objects through their owner
#accommodation = current_user.accommodations.build
#accommodation.build_photo
end
def create
#accommodation = current_user.accommodations.build(params[:accommodation])
if #accommodation.save
# always redirect after successful save/update
redirect_to #accommodation
else
render :new
end
end
end
Tell Rails to handle the nested form:
class Accommodation
has_one :photo
accepts_nested_attributes :photo
attr_accessible :photo_attributes, :title, :description, :etc
end
And make sure to set the accessible attributes right in your photo model:
class Photo
attr_accessible :image # individual attributes such as image_file_name shouldn't be accessible
has_attached_file :image, :styles => "etc"
end
Be sure to watch your log files to spot things that are protected by attr_accessible, but still are in your form.