I have 3 models:
event
vendor
vendor_relationship
Every event has multiple vendors through that relationship.
Now I want to create a form at /events/1/add_vendors which creates the relationship AND creates the vendor model.
How would I go about doing this?
Thanks for the help!
ensure that you're Event model looks something like this:
attr_accessible :vendor_relationships, :vendor_relationships_attributes
has_many :vendor_relationships
has_many :vendors, :through => :vendor_relationships
accepts_nested_attributes_for :vendor_relationships
your VendorRelationship model looks something like this:
attr_accessible :vendors, :vendors_attributes
has_many :vendors
accepts_nested_attributes_for :vendors
in your EventController:
#event = Event.new(params[:event])
and in your create view, something like:
<% form_for Event.new do |f| %>
<%= f.text_field, :field_for_the_event %>
<% f.fields_for :vendor_relationships do |rf| %>
<%= rf.text_field, :price_maybe? %>
<% rf.fields_for :vendor do |vf| %>
<%= vf.text_field, :name_and_so_on %>
<% end %>
<% end %>
<% end %>
That's one way. Another, probably better user experience would be to allow for selection of vendor from existing vendors or create new. Create new would ajax creation to the VendorController, and on creation, return the vendor's information to the form. Saving the relationship would ajax a call to create the vendor_relationship and display the result.
Hope that sends you down the right direction.
# routes.rb
resources :events do
resources :vendors, :path_names => { :new => 'add_vendors' }
end
# vendors_controller.rb
before_filter :load_event
before_filter :load_vendor, :only => [:edit, :update, :destroy]
def load_vendor
#vendor = (#event ? #event.vendors : Vendor).find(params[:id])
end
def load_event
#event = params[:event_id].present? ? Event.find(params[:event_id]) : nil
end
def new
#vendor = #event ? #event.vendors.build : Vendor.new
...
end
def create
#vendor = #event ? #event.vendors.build(params[:vendor]) : Vendor.new(params[:vendor])
...
end
def edit
...
end
def update
...
end
def destroy
...
end
# View form
<%= form_for([#event, #vendor]) do |f| %>
...
<% end %>
Related
Working with nested routes and associations. I have a partial which creates a tenant, but after the creation it stays with the form rendered and the url changes to /tenants. Desired behavior is that it needs to redirect_to the show page. Routes are as follows:
Rails.application.routes.draw do
devise_for :landlords
authenticated :landlord do
root "properties#index", as: "authenticated_root"
end
resources :tenants
resources :properties do
resources :units
end
root 'static#home'
end
So far the properties and units work (and the landlord) Issue is with Tenants. Originally I had Tenants nested under units, but had issues there as well. Partial looks like this:
<%= form_for #tenant do |f| %>
<%= f.label "Tenant Name:" %>
<%= f.text_field :name %>
<%= f.label "Move-in Date:" %>
<%= f.date_field :move_in_date %>
<%= f.label "Back Rent Amount:" %>
$<%= f.text_field :back_rent %>
<%= f.button :Submit %>
<% end %>
<%= link_to "Cancel", root_path %>
Tenants Controller looks like this:
before_action :authenticate_landlord!
#before_action :set_unit, only: [:new, :create]
before_action :set_tenant, except: [:new, :create]
def new
#tenant = Tenant.new
end
def create
#tenant = Tenant.new(tenant_params)
if #tenant.save
redirect_to(#tenant)
else
render 'new'
end
end
def show
end
def edit
end
def update
if #tenant.update(tenant_params)
redirect_to unit_tenant_path(#tenant)
else
render 'edit'
end
end
def destroy
end
private
def set_property
#property = Property.find(params[:property_id])
end
def set_unit
#unit = Unit.find(params[:unit_id])
end
def set_tenant
#tenant = Tenant.find(params[:id])
end
def tenant_params
params.require(:tenant).permit(:name, :move_in_date, :is_late, :back_rent, :unit_id)
end
end
Models have associations:
class Tenant < ApplicationRecord
belongs_to :unit, inverse_of: :tenants
end
class Unit < ApplicationRecord
belongs_to :property, inverse_of: :units
has_many :tenants, inverse_of: :unit
end
Lastly the show#tenants in rake routes is:
tenant GET /tenants/:id(.:format) tenants#show
I have extensively searched for this topic, but haven't had any success. Any help is appreciated. Rails 5.1
The route you are showing near the end of your question:
tenant GET /tenants/:id(.:format) tenants#show
is not the tenants index; it is the individual tenants/show route. You can tell this because it includes :id, which means it will show you a specific tenant having that id.
Try running rake routes again. The index route should look like this:
tenants GET /tenants(.:format) tenants#index
If you want to return to the tenants index after creating or updating a Tenant record, then you need to specify that path in your TenantsController. In both the #create and #update actions, your redirect line (after if #tenant.save and if #tenant.update, respectively) should read:
redirect_to tenants_path
That will take you to the TenantsController, #index action.
In the alternative, if you want to return to the individual tenant show page, then instead change both of those redirects in the TenantsController in both the #create and #update actions to:
redirect_to tenant_path(#tenant)
That will take you to the TenantsController, #show action for the current #tenant.
I am learning and building my first app with Ruby on Rails by cloning and adjusting an existing project. I got stuck in the writing a controller and hope that someone has a tip to help me out.
Context
I am building a trainings platform: Users can design a training, these trainings can be given on several dates (I call these trainingsessions 'thrills'), (other) users can subscribe to these thrills with reservations.
Conceptualization of models
Complication
I got the users, trainings and reservations model running now but I want to add the thrills model in between (I know this is not the easiest way, but I followed a training that did not include the thrills model). I setup the model and a simple view where a trainer should be able to add thrills to an existing training (in trainings/edit). Unfortunately after a day of trying I have not managed to do so, I keep getting:
NoMethodError in ThrillsController#create
NoMethodError (undefined method `thrills' for nil:NilClass): (line 16)
My TrainingsController for def edit looks like
def edit
if current_user.id == #training.user.id
#photos = #training.photos
#thrill = #training.thrills.create(thrill_params)
#thrills = #training.thrills
else
redirect_to root_path, notice: "Je hebt hier helaas geen toegang tot"
end
end
def update
if #training.update(training_params)
if params[:images]
params[:images].each do |image|
#training.photos.create(image: image)
end
end
#thrill = #training.thrills.create(thrill_params)
#thrills = #training.thrills
#photos = #training.photos
redirect_to edit_training_path(#training), notice: "Updated..."
else
render:edit
end
end
And my ThrillsController looks like
class ThrillsController < ApplicationController
def create
#thrill = #training.thrills.create(thrill_params)
redirect_to #thrill.training, notice: "Je thrill is aangemaakt!"
end
private
def thrill_params
params.require(:thrill).permit(:trilldate, :thrillhr, :thrillmin, :training_id)
end
end
And my form to add a thrill in views/thrills/_form.html.erb which is rendered in views/trainings/edit.html.erb
<%= form_for([#training.thrills.new]) do |f| %>
<div class="form-group">
<%= f.text_field :thrillhr, placeholder: "Uur", class: "form-control" %>
</div>
<%= f.hidden_field :training_id, value: #training.id %>
<div class="actions">
<%= f.submit "Create", class: "btn btn-primary" %>
</div>
<% end %>
The full code of the app can be found here https://github.com/chrisrutte/musc
Question
I'm obviously doing something simple wrong here, so I hope someone can provide me the hint how to save new thrills in my Thrills model.
If any more information is needed, don't hestitate to ask me.
Thanks in advance!
First lets look at what the models for this kind of setup would look like:
class Training < ActiveRecord::Base
has_many :thrills
has_many :users, through: :thrills
end
class Thrills < ActiveRecord::Base
belongs_to :training
has_many :reservations
has_many :users, through: :reservations
end
class Reservation < ActiveRecord::Base
belongs_to :user
belongs_to :thrill
has_one :training, though: :thrill
end
class User < ActiveRecord::Base
has_many :reservations
has_many :thrills, through: :reservations
has_many :trainings, through: :thrills
end
Make sure you read the Rails guide on associations as this is quite complex.
Notice that Reservation works as a join model.
Reservation also has an indirect relation to Training. This is to avoid having duplicate foreign keys on several levels. The same applies to the User model. The main reason is that ActiveRecord will only write the foreign key in once place!
When setting up the routes you will most likely want to use nesting:
resources :trainings, shallow: true do
resources :thrills
end
We can then setup the controller.
class ThrillsController < ApplicationController
before_action :set_training!, only: [:new, :create, :index]
before_action :set_thrill!, only: [:show, :edit, :update, :destroy]
# GET /trainings/:training_id/thrills/new
def new
#thrill = #training.thrills.new
end
# POST /trainings/:training_id/thrills
def create
#thrill = #training.thrills.new(thrill_params)
if #thrill.save
redirect_to #thrill
else
render :new
end
end
# GET /trainings/:training_id/thrills
def index
#thrills = #training.thrills
end
# this is not nested.
# GET /thrills/:id
def show
end
# this is not nested.
# GET /thrills/:id/edit
def edit
end
# this is not nested.
# PUT|PATCH /thrills/:id
def update
if #thrill.update(thrill_params)
redirect_to #thrill
else
render :edit
end
end
# ...
private
def set_training!
#training = Training.find(params[:training_id])
end
def set_thrill!
#thill = Thrill.joins(:training).find(params[:id])
#training = #thill.training
end
def thrill_params
params.require(:thrill)
.permit(:trilldate, :thrillhr, :thrillmin, :training_id)
end
end
And lets setup the form:
<%= form_for( [#training, #thrill] ) do |f| %>
<div class="form-group">
<%= f.text_field :thrillhr, placeholder: "Uur", class: "form-control" %>
</div>
<div class="actions">
<%= f.submit "Create", class: "btn btn-primary" %>
</div>
<% end %>
Since we want to pass the training_id though the form action attribute and not in the form body we use form_for( [#training, #thrill] ) which will give us the path /trainings/6/thrills for example.
However for our edit form we want /thrills/1 instead. So lets fix that:
<%= form_for( #thrill.new_record? ? [#training, #thrill] : #thrill ) do |f| %>
# ...
<% end %>
I have three models: User, Publisher and Interest all with many to many relationships linked through three join models but only 2 out of 3 join models record the id's of their 2 parent models. my UsersPublisher model does not link User to Publisher.
My Interestscontroller proccesses a form (see code) through which I ask the user to provide Interest and Publisher. The latter gets processed via the fields_for method which allows you to pass Publisher attributes via the InterestsController. the UsersPublisher join model records the user_id but the publisher_id is nil.
I've tried putting #users_publishers in both the new and create methods of Publishers- and InterestsController. My latest attempt of using after_action in the InterestsController (see code) has also failed. I've also tried the after_action way in the PublishersController
Your helped is highly appreciated!
The UsersPublisher join model
class UsersPublisher < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
end
InterestsController
class InterestsController < ApplicationController
before_action :find_user
after_action :upublisher, only: [:new]
def index
#interests = policy_scope(Interest)
end
def show
#interest = Interest.find(params[:id])
end
def new
#interest = Interest.new
#interest.publishers.build
authorize #interest
end
def create
#interest = Interest.new(interest_params)
#users_interests = UsersInterest.create(user: current_user, interest: #interest)
authorize #interest
if #interest.save
respond_to do |format|
format.js
format.html {redirect_to root_path}
end
flash[:notice] = 'Thank you, we will be in touch soon'
else
respond_to do |format|
format.js { render }
format.html { render :new }
end
end
end
def edit
#interest = Interest.find(params[:id])
authorize #interest
end
def update
#interest = Interest.find(params[:id])
#interest.update(interest_params)
if #interest.save
flash[:notice] = 'Your interest has been added'
else
flash[:notice] = 'Oops something went wrong'
end
end
private
def interest_params
params.require(:interest).permit(:name, publishers_attributes: [:publisher,:id, :feed])
end
def find_user
#user = current_user
end
def upublisher
#users_publishers = UsersPublisher.create(publisher: #publisher, user: current_user)
end
end
Form
<%= form_for [#user, #interest] do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :publishers do |ff| %>
<%= ff.label :publisher %>
<%= ff.text_field :publisher %>
<%= ff.label :feed %>
<%= ff.text_field :feed %>
<%end%>
<%= f.submit "Submit" %>
<%end%>
Since you're using fields_for, you'll want to make sure you have accepts_nested_attributes_for:
class UsersPublisher < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
accepts_nested_attributes_for :publisher
end
This should fix your issue (if it's as you outlined).
Your question is pretty broad, so I don't know whether the above will work. Below are my notes...
From the looks of it, your structure is very complicated; you should work to make it as simple as possible. In the case of creating "interests", you may wish to get rid of the form completely:
#config/routes.rb
resources :publishers do
resources :interests, path: "interest", only: [:create, :destroy] #-> url.com/publishers/:publisher_id/interest
end
#app/controllers/interests_controller.rb
class InterestsController < ApplicationController
before_action :set_publisher
def create
current_user.interests.create publisher: #publisher
end
def destroy
#interest = current_user.interests.find_by publisher_id: #publisher.id
current_user.interests.delete #interest
end
private
def set_publisher
#publisher = UserPublisher.find params[:publisher_id]
end
end
You'd be able to use the above as follows:
<%= link_to "Add Interest", publisher_interest_path(#publisher), method: :post %>
<%= link_to "Remove Interest", publisher_interest_path(#publisher), method: :delete %>
Thinking about it properly, you've got a pretty bad structure.
I'd do something like this:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :interests
has_many :publishers, through: :interests
end
#app/models/interest.rb
class Interest < ActiveRecord::Base
belongs_to :user
belongs_to :publisher
accepts_nested_attributes_for :publisher
end
#app/models/publisher.rb
class Publisher < ActiveRecord::Base
has_many :interests,
has_many :users, through: :interests
end
This should give you the ability to create interests for any number of users and publishers. If you create a publisher for a specific user, you can use accepts_nested_attributes_for to pass the appropriate data:
#config/routes.rb
resources :users do
resources :interest, only: [:new, :create, :destroy] #-> url.com/users/:user_id/interests/new
end
#app/controllers/interests_controller.rb
class InterestsController < ApplicationController
def new
#user = User.find params[:user_id]
#interest = #user.interests.new
#interest.publisher.build
end
def create
#user = User.find params[:user_id]
#interest = #user.interests.new interest_params
end
private
def interest_params
params.require(:interest).permit(:user, :publisher)
end
end
#app/views/interests/new.html.erb
<%= form_for [#user, #interest] do |f| %>
<%= f.fields_for :publisher do |p| %>
<%= p.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
So, essentially, I have two models, Ticket
class Ticket < ActiveRecord::Base
belongs_to :event
end
And Event:
class Event < ActiveRecord::Base
belongs_to :club
has_many :tickets
accepts_nested_attributes_for :tickets
end
They are associated with each other, and I have done the necessary migrations.
In the events/show view, it shows the event and then at the end I have a link to create a ticket, with this event's name passed as an id:
<%= link_to 'Add tickets', new_ticket_path(:id => #event.name) %>
This renders properly in the new ticket page, I have tested it in the new ticket view with <%= params[:id] %> and it comes up correctly.
The tickets_controller's create method is as follows:
def create
#ticket = Ticket.new(ticket_params)
#ticket.event_id = params[:id]
...
end
But when testing back in the events/show view
<% #tickets = Ticket.all %>
<% #tickets.each do |ticket| %>
<p><%= ticket.event_id %>--</p>
<% end %>
All of the event_id's come up empty.
Any suggestions?
The problem is you're passing the :id param to the new method, and then expecting it to persist to the create method
Params are HTTP-based values -- they don't last longer than a single request. You'll have to either use a nested resource, or set the event_id attribute in your new form:
Route
#config/routes.rb
resources :events do
resources :tickets #-> /events/:event_id/tickets/new
end
This will keep the params[:event_id] param consistent (as it's being passed in the URL), allowing you to use it in your create action like so:
def create
#ticket = Ticket.new(ticket_params)
#ticket.event_id = params[:event_id]
...
end
This is the conventional way to do this
Attribute
If you want to just test your code, you should set the event_id param in your new action form:
<%= form_for [#event, #ticket] do |f| %>
<%= f.hidden :event_id, params[:id] %> #-> sets params[:ticket][:event_id]
<% end %>
This will allow you to set the event_id param in your strong_params method in your create action:
def new
#event = Event.find params[:event_id]
#ticket = Ticket.new
end
def create
#ticket = Ticket.new(ticket_params)
#ticket.save
end
private
def ticket_params
params.require(:ticket).permit(:event_id)
end
I have two models one Topic and Topic_Content.
With the following code
Route
resources :topics do
resources :topic_contents
end
Topic
class Topic < ActiveRecord::Base
has_one :topic_content
accepts_nested_attributes_for :topic_content
end
TopicContent
class TopicContent < ActiveRecord::Base
belongs_to :topics
end
Controller
class TopicsController < ApplicationController
def new
#topic = Topic.new
end
def create
# render text: params[:topic].inspect
#topic = Topic.new(topic_params)
#topic.save
end
private
def topic_params
params.require(:topic).permit(:title, topic_content_attributes: [:text])
end
end
View
<%= form_for #topic do |f| %>
<%= f.label 'Topic:' %>
<%= f.text_field :title %>
<%= f.fields_for :topic_contents do |tf| %>
<%= tf.label :text %>
<%= tf.text_area :text %>
<% end %>
<%= f.submit %>
<% end %>
The title will be saved correct in the topic table but the topic_content(text) wouldn't saved in the database, and I couldn't find the problem.
I'm not a Rails expert, but I'm certain you need to build the association in your controller.
In your new and edit actions you need to have:
def new
#topic = Topic.new
#topic_content = #topic.build_topic_content
end
Because this is a has_one/belongs_to you need to have it look that way. If it was a many association you'd build it with something like #topic_content = #topic.topic_contents.build.
I'm pretty sure it's just a matter of building the association in the right controller, which, I believe, for you, is the topic controller.
Your view should be as follow:
f.fields_for :topic_content do |content_fields|
^