I want to add an RSVP button to an Event view. For the RSVP controller, how do I retrieve the id of the Event that the view of which the user has opened, with a check that it is indeed an Event view? Should I create a private method like current_event in the application controller?
class RsvpsController < ApplicationController
def create
#rsvp = Rsvp.new(rsvp_params)
##rsvp.event_id = ???
end
end
UPDATE with additional info
my models looks like this
class Rsvp < ApplicationRecord
belongs_to :event
belongs_to :user
end
class Event < ApplicationRecord
has_many :rsvps
has_many :users, through: :rsvps
end
class User < ApplicationRecord
has_many :rsvps
has_many :events, through: :rsvps
end
You really haven't provided enough information about your application. How does an event relate to a RSVP. I'm going to make a few assumptions, but think that it will help you overall. Let's say your application will have many Events. An Event has many RSVP and a RSVP belongs to an event.
Typically, you will have something like this within your routes
Rails.application.routes.draw do
resources :events do
resources :rsvps
end
end
If you run rake routes, you may see something like this
Prefix Verb URI Pattern Controller#Action
event_rsvps GET /events/:event_id/rsvps(.:format) rsvps#index
POST /events/:event_id/rsvps(.:format) rsvps#create
new_event_rsvp GET /events/:event_id/rsvps/new(.:format) rsvps#new
edit_event_rsvp GET /events/:event_id/rsvps/:id/edit(.:format) rsvps#edit
event_rsvp GET /events/:event_id/rsvps/:id(.:format) rsvps#show
PATCH /events/:event_id/rsvps/:id(.:format) rsvps#update
PUT /events/:event_id/rsvps/:id(.:format) rsvps#update
DELETE /events/:event_id/rsvps/:id(.:format) rsvps#destroy
So your button should point to the rsvps#new controller and action. Once your user submits the form, it will call the rsvps controller and the create action.
From here, you can access your event with something like #event = Event.find(params[:event_id])
Since an event has_many :rsvps and a rsvp belongs_to :event, you can create your rsvp with something like
def create
#event = Event.find(params[:event_id])
#rsvp = #event.rsvps.create(rsvp_params) # strong params
if #rsvp.save
...
else
...
end
end
Bonus tip: Within your RSVP's form, you will need to change it to something like this since we have nested RSVP under Events.
<%= form_for [#event, #rsvp] do |f| %>
...
<% end %>
Related
So I am making a chat app, and I want users to be able to leave chat rooms. This would be done by posting a delete request to a route like /users/:id/chat_rooms/leave/:chat_room_id.
The Users model has has_many :chat_rooms, through: chat_room_users while ChatRooms has has_many :users, through chat_room_users. The UsersController has a leave action, which I want to call using this request on this url.
I want to create a link to this url on a view that I have. I already have a variable #user for the current user and #chosen for the current chat room available on the view. So how would I do a link_to and route for this setup? I have delete /users/:id/chat_rooms/leave/:chat_room_id in the routes.rb file, but how would I do the link_to?
Thank you.
You're overcomplicating it.
DELETE /chat_rooms/:chat_room_id/leave
Instead of passing the user id via the URL you should instead get it through the session or a token (if its an API app).
Rule of thumb: resources should never be nested more than 1 level
deep. A collection may need to be scoped by its parent, but a specific
member can always be accessed directly by an id, and shouldn’t need
scoping (unless the id is not unique, for some reason).
http://weblog.jamisbuck.org/2007/2/5/nesting-resources
This is just a loose example of how to solve this:
# routes.rb
resources :chat_rooms do
member do
post :join
delete :leave
end
end
class User
has_many :chat_room_users
has_many :chat_rooms, though: :chats
end
class ChatRoomUser
belongs_to :user
belongs_to :chatroom
end
class ChatRoom
has_many :chat_room_users
has_many :users, though: :chats
end
Putting this in UsersController is pretty questionable. I would instead place it in ChatroomsController.
class ChatroomsController
# ...
# POST /chat_rooms/:chat_room_id/join
def join
#chat_room = ChatRoom.find(params[:id])
#chat = current_user.chat_room_users.new(chat_room: #chat_room)
if #chat_room.create
# ...
else
# ...
end
end
# DELETE /chat_rooms/:chat_room_id/leave
def leave
#chat_room = ChatRoom.find(params[:id])
#chat = current_user.chat_room_users.find_by(chat_room: #chat_room)
#chat.destroy
end
end
<%= button_to 'Join', join_chat_room_path(#chat_room), method: :post %>
<%= button_to 'Leave', leave_chat_room_path(#chat_room), method: :delete %>
newbie here...
I am trying to create an events registration page where anybody can register for an event without logging into the system.
My problem is trying to figure out how to tie the registration info to the specific event. I've created all the associations but can't figure how to tell the db that the person is registering for a specific event.
Here are my associations:
class Event < ActiveRecord::Base
belongs_to :user
has_many :event_regs
has_many :regs, through: :event_regs
class Reg < ActiveRecord::Base
has_many :event_regs
class Reg < ActiveRecord::Base
has_many :event_regs
Thanks in advance
Newbie here
Welcome!
Here's what you'll need:
#app/models/event.rb
class Event < ActiveRecord::Base
has_many :registrations
end
#app/models/registration.rb
class Registration < ActiveRecord::Base
belongs_to :event
end
This will allow you to use the following:
#config/routes.rb
resources :events do #-> url.com/events/:id
resources :registrations #-> url.com/events/:event_id/registrations/
end
#app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
def new
#event = Event.find params[:event_id]
#registration = #event.registration.new
end
def create
#event = Event.find params[:event_id]
#registration = #event.registration.new registration_params
end
private
def registration_params
params.require(:registration).permit(:all, :your, :params)
end
end
This will create a new registration record in your db, associating it with the Event record you've accessed through the route.
--
From this setup, you'll be able to use the following:
#app/controllers/events_controller.rb
class EventsController < ApplicationController
def show
#event = Event.find params[:id]
end
end
#app/views/events/show.html.erb
<% #event.registrations.each do |registration| %>
# -> output registration object here
<% end %>
Foreign Keys
In order to understand how this works, you'll be best looking at something called foreign keys...
This is a relational database principle which allows you to associate two or more records in different database tables.
Since Rails is designed to work with relational databases, each association you use will require the use of a "foreign key" in some respect.
In your case, I would recommend using a has_many/belongs_to relationship:
You'll need to make sure you add the event_id column to your registrations database.
I have a controller for "Productions" and if you go to localhost/productions you see an index page, you can click show and view the show page for that particular productions.
Each production has a unique ID like 036ea872f9011a7c, I want my users to be able to add items to a production like follows:
localhost/productions/036ea872f9011a7c/fixtures/add
localhost/productions/036ea872f9011a7c/positions/add
localhost/productions/036ea872f9011a7c/dimmers/add
localhost/productions/036ea872f9011a7c/channels/add
localhost/productions/036ea872f9011a7c/etc/add
You should build a route with the necessary parameters like this:
Suppose we have tasks that we assign to a project
model project.rb:
class Project < ActiveRecord::Base
has_many :tasks, through: :project_task
end
model task.rb
class Task < ActiveRecord::Base
has_many :projects, through: :project_task
end
routes.rb
...
resources :projects do
member do
get 'affect_task/:task_id', action: 'affect_task', as: :affect_task
end
end
projects/show.haml
= link_to "task_name", affect_task_project_path(task_id: #task_id, project_id: #project_id)
controller.rb
...
def affect_task
...
CollaboratorTask.create(task_id: params[:task_id], project_id: params[:project_id])
...
end
...
Of course this is an example so you understand..
I'm trying to add a 'Collections' model to group Posts so that any user can add any Post they like to any Collection they've created. The Posts will have already been created by a different user. We are just letting other users group these posts in their own Collections. Basically like bookmarking.
What is the cleanest, and most Rails-ey-way of doing this?
I've created the model and run through the migration and what not. Also I've already created proper views for Collection.
rails g model Collection title:string user_id:integer
collections_controller.rb
class CollectionsController < ApplicationController
def index
#collections = current_user.collections.all
end
def show
#collection = Collection.all
end
def new
#collection = Collection.new
end
def create
#collection = current_user.collections.build(collection_params)
if #collection.save
redirect_to #collection, notice: 'saved'
else
render action: 'new'
end
end
def update
end
private
def collection_params
params.require(:collection).permit(:title)
end
end
collection.rb
class Collection < ActiveRecord::Base
belongs_to :user
has_many :posts
validates :title, presence: true
end
post.rb
has_many :collections
It seems like has_many or has_and_belongs_to_many associations are not correct? Should I be creating another model to act as an intermediary to then use
has_many :collections :through :collectionList?
If my association is wrong, can you explain what I need to change to make this work?
Also the next part in this is since this is not being created when the Post or Collection is created, I'm not sure the best way to handle this in the view. What is the best way to handle this, keeping my view/controller as clean as possible? I just want to be able to have a button on the Post#Show page that when clicked, allows users to add that post to a Collection of their own.
In such case you should use or has_and_belongs_to_many or has_many :through association. The second one is recommended, because it allows more flexibility. So now you should:
Create new model PostsCollections
rails g model PostsCollections post_id:integer collection_id:integer
and migrate it
Set correct model associations:
Something like:
class Post < ActiveRecord::Base
has_many :posts_collections
has_many :categories, through: :posts_collections
end
class Collection < ActiveRecord::Base
has_many :posts_collections
has_many :posts, through: :posts_collections
end
class PostsCollections < ActiveRecord::Base
belongs_to :post
belongs_to :collection
end
Then you'll be able to use
#collection.first.posts << #post
And it will add #post to #collection's posts
To add a post to a collection from view
Add a new route to your routes.rb, something like:
resources :collections do # you should have this part already
post :add_post, on: :member
end
In your Collections controller add:
def add_post
#post = Post.find(params[:post_id])
#collection = Collection.find(params[:id])
#collection.posts << #post
respond_to do |format|
format.js
end
end
As for views, you'll have to create a form to show a collection select and button to add it. That form should make POST method request to add_post_collection_path(#collection) with :post_id parameter.
You can read more explanations of how rails associations work in Michael Hartl's tutorial, because that subject is very wide, and can't be explained with short answer.
I have a class called Quote which has_many :line_items, as: :line_itemable (line_items are polymorphic). A quote must have at least one line_item upon creation, so in my Quote creation form I have a section dedicated to adding line items. My routes look like this:
resources :quotes, shallow: true do
resources :line_items
end
which means my routes look like this:
POST /quotes/:quote_id/line_items(.:format) line_items#create
new_quote_line_item GET /quotes/:quote_id/line_items/new(.:format) line_items#new
In the line items section of the quote form I have a button that, when clicked, links to the new_quote_line_item controller action to render a line_item creation modal. My issue is that since the quote hasn't been created yet it doesn't have :quote_id to use in the path. How can I go about achieving this the Rails Way™? I was considering using ajax but I'm not sure if that is overkill for this situation. Thanks for your help!
You should user accepts_nested_attributes_for method in your model to accept attributes for LineItem and fields_for helper
Your model should looks like:
class Quote < ActiveRecord::Base
accepts_nested_attributes_for :line_item
...
end
And you template like:
form_for #quote do |f|
f.fields_for :line_items do |f2|
...
end
...
end
Ajax
You wouldn't need ajax functionality for this - Ajax only allows you to pull data from the server asynchronously, which essentially means you don't have to reload the page.
--
Nested Attributes
What you're looking for, as alluded to by atomAltera sounds like accepts_nested_attributes_for - which allows you to create dependent models from the parent
It sounds to me that you'll need to create a quote before you try and populate line_items, which is actually quite simple using ActiveRecord:
#app/models/quote.rb
Class Quote < ActiveRecord::Base
has_many :line_items
accepts_nested_attributes_for :line_items
end
#app/controllers/quotes_controller.rb
Class QuotesController < ApplicationController
def new
#quote = Quote.new
#quote.line_items.build
end
def create
#quote = Quote.new(quote_params)
#quote.save
end
private
def quote_params
params.require(:quote).permit(:quote, :attributes, :new, line_items_attributes: [:line, :items, :attributes])
end
end
--
If you need any further information, please let me know!!