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.
Related
Suppose I have a User model
user.rb
class User < ActiveRecord::Base
...
end
with attributes like: name, username, access
access is an enum that tells me if the user is "staff" or "customer"
To get the name and username of the logged in user, I can do:
current_user.name
current_user.username
And suppose I have a Staff model
staff.rb
class Staff < ActiveRecord::Base
belongs_to :user
end
with attributes like: salary, phone_number
And I also have a Customer model
customer.rb
class Customer < ActiveRecord::Base
belongs_to :user
end
with attributes like: address, phone_number
I want to be able to call this on my staff's controller:
current_user.staff.salary
And this on my customer's controller:
current_user.customer.address
WHAT I TRIED SO FAR
I overwrote sessions_controller.rb
def create
super
model_name = current_user.access.capitalize.constantize
spec = model_name.where(user_id: current_user.id).take
session[:spec] = spec
end
So I'm able to access it via session[:spec], but not via current_user. Any ideas?
Well to begin with, your User model should reference the staff or customer, even if they are to stay blank
class User
has_one :staff
has_one :address
Just by doing this, you should be able to use current_user.customer.address. However...
I suggest you add some convenient methods in ApplicationController or a module that you include
def staff_signed_in?
#staff_signed_in ||= (user_signed_in? and current_user.access == :staff)
end
def current_staff
#current_staff ||= (current_user.staff if staff_logged_in?)
end
# same for customer
# Note that I use instance variables so any database queries are executed only once !
Then you can simply call
<% if customer_signed_in? %>
<h2>Logged in as customer</h2>
<p>Address : <%= current_customer.address %>
<% end %>
EDIT : about your concerns concerning database hits
You gave the example of current_user.customer.cart.products
This is indeed quite a nested association. My suggestion above already reduces it by one level (ie current_customer == current_user.customer). Then you have to go through carts to reach products... it isn't so bad in my opinion.
If you need to call that often (current_customercustomer.cart) you can override the current_customer for a given controller and eager load the resources you know you will use use.
def UserShopController < ApplicationController
# Let's assume current_customer is defined in ApplicationController like I showed above
# UserShopController always uses the customer cart, so let's load it right at the beginning
...
private
# Override with eager loading
def current_customer
#current_customer ||= (current_user.customer.includes(:cart) if customer_logged_in?)
end
add has_one :customer to your user.rb
Your user model should be like below to accessing related model.
class User < ActiveRecord::Base
has_one :customer
end
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.
In Ruby on Rails I have a user models and a jobs model joined through a different model called applicants. I have a button for the users when they want to "remove their application for this job" but I don't know how to remove the specific user, and for that matter I don't know if I'm doing a good job at adding them either (I know atleast it works).
user.rb
class User < ActiveRecord::Base
...
has_many :applicants
has_many:jobs, through: :applicants
end
job.rb
class Job < ActiveRecord::Base
...
has_many :applicants
has_many:users, through: :applicants
end
applicant.rb
class Applicant < ActiveRecord::Base
belongs_to :job
belongs_to :user
end
when someone applies for a job my jobs controller is called:
class JobsController < ApplicationController
...
def addapply
#job = Job.find(params[:id])
applicant = Applicant.find_or_initialize_by(job_id: #job.id)
applicant.update(user_id: current_user.id)
redirect_to #job
end
...
end
Does that .update indicate that whatever is there will be replaced? I'm not sure if I'm doing that right.
When someone wants to remove their application I want it to go to my jobs controller again but I'm not sure what def to make, maybe something like this?
def removeapply
#job = Job.find(params[:id])
applicant = Applicant.find_or_initialize_by(job_id: #job.id)
applicant.update(user_id: current_user.id).destroy
redirect_to #job
end
does it ave to sort through the list of user_ids save them all to an array but the one I want to remove, delete the table then put them all back in? I'm unsure how this has_many works, let alone has_many :through sorry for the ignorance!
thanks!
Let's assume the user will want to remove their own application. You can do something like this:
class UsersController < ApplicationController
def show
#applicants = current_user.applicants # or #user.find(params[:id]), whatever you prefer
end
end
class ApplicantsController < ApplicationController
def destroy
current_user.applications.find(params[:id]).destroy
redirect_to :back # or whereever
end
end
And in your view:
- #applicants.each do |applicant|
= form_for applicant, method: :delete do |f|
= f.submit
Don't forget to set a route:
resources :applicants, only: :destroy
Some observations, I would probably name the association application instead of applicant. So has_many :applications, class_name: 'Applicant'.
In Rails 3.2 I have been looking for a way to traverse the associations of an object within the before_add callback.
So basically my use case is:
class User < ActiveRecord::Base
has_and_belongs_to_many :meetings
end
class Meeting < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :comments, :before_add => :set_owner_id
end
class Comment < ActiveRecord::Base
belongs_to :meeting
end
def set_owner_id(child)
child.owner_id = <<<THE USER ID for #user >>>
end
and I am creating a comment within the context of a user:
#user.meetings.first.comments.create
How do I traverse the associations from within the before_add callback to discover the id of #user? I want to set this at model level. I have been looking at proxy_association, but I may be missing something. Any ideas?
You should probably create the comment in the context of the meeting, no? Either way, you should handle this in the controller since you'll have no access to #user in your model.
#comment = Meeting.find(id).comments.create(owner_id: #user, ... )
But if you insist on your way, do this:
#comment = #user.meetings.first.comments.create(owner_id: #user.id)
So, I've read in some book about tip "Use model association", which encourages developers to use build methods instead of putting ids via setters.
Assume you have multiple has_many relationships in your model. What's best practise for creating model then ?
For example, let's say you have models Article, User and Group.
class Article < ActiveRecord::Base
belongs_to :user
belongs_to :subdomain
end
class User < ActiveRecord::Base
has_many :articles
end
class Subdomain < ActiveRecord::Base
has_many :articles
end
and ArticlesController:
class ArticlesController < ApplicationController
def create
# let's say we have methods current_user which returns current user and current_subdomain which gets current subdomain
# so, what I need here is a way to set subdomain_id to current_subdomain.id and user_id to current_user.id
#article = current_user.articles.build(params[:article])
#article.subdomain_id = current_subdomain.id
# or Dogbert's suggestion
#article.subdomain = current_subdomain
#article.save
end
end
Is there a cleaner way ?
Thanks!
This should be a little cleaner.
#article.subdomain = current_subdomain
The only thing I can think of is merging the subdomain with params:
#article = current_user.articles.build(params[:article].merge(:subdomain => current_subdomain))