Show contents of table but limit to those matching signed-in user - ruby-on-rails

In my quiz game Rails project, I have a table for "Participations" that stores information on the user, the quiz category, and the quiz score after a user completes the test.
class CreateParticipations < ActiveRecord::Migration
def change
create_table :participations do |t|
t.references :user
t.string :category
t.boolean :finished, default: false
t.integer :current_question_index, default: 0
t.integer :score, default: 0
t.timestamps
end
end
end
In my user.rb, I specify an association that a user has_many :participations, which allows a user to play multiple quizzes while storing categories/scores in the table.
If I want to show a user a table of his results (so return all Participations results, but only for those that match the user) in a view, can I call that without generating a new controller?

You can just do like below
#in the controller action
#user_participations = Participation.where(user_id: current_user.id)
and just call #user_participations in the view.

#config/routes.rb
resources :users do
resources :participations, path: "results", only: :index #-> url.com/users/:user_id/results
end
#app/controllers/participations_controller.rb
class ParticipationsController < ApplicationController
def index
#user = User.find params[:user_id]
#participations = #user.participations
end
end
#app/views/participations/index.html.erb
<% #participations.each do |participation| %>
<%= participation.score %>
<% end %>
--
If you have your associations set up correctly, you should be using the associative method (#user.participations), which will basically do what Pavan has suggested:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :participations
end
#app/models/participation.rb
class Participation < ActiveRecord::Base
belongs_to :user
end

Users controller
has_many :participations
def participations
#user = User.find(params[:id])
#participations = #users.participations.build(params[:participation_id])
end
end
ParticipationsController
belongs_to :user
In your routes file you can create the route by
GET 'users/:id/participations', to: 'users#participations', as: 'user_participations'
That will give you a user_participations_path route
So if you wanted to link to it you could add
<%= link_to 'Show user games!', user_participations_path(#participations.user) %>
Then in views/users/participations
<% #participations.each do |participation| %>
<%= participation.inspect %>
<% end %>
Let me know how you go!
EDIT
Please not that the has_many and belongs_to declarations should be in the user.rb and participation.rb models respectively. Thanks to #richpeck for picking up the mistake.

Related

Database query - Rails

I'm trying to write a database query and i've been scratching my head at what i'm doing wrong.
Here's the relevant model
class User
has_one :store
has_many :products, through: :store
enum gender: %i[menswear womenswear unisex]
def menswear
self.gender == 'menswear'
end
def womenswear
self.gender == 'womenswear'
end
end
class Product
belongs_to :store
end
and controller is
class UsersController
def index
#male = User.menswear
#female = User.womenswear
#products = Product.all.order('created_at DESC')
end
end
View
<% #male.products.in_groups_of(3, false).each do |group| %>
<% for product in group %>
<%= link_to product_path(product) do %>
<%= image_tag product.secimage_url(:index).to_s, class: "image hide" %>
<%= image_tag product.image_url(:index).to_s, class: "image" %>
<% end %>
<% end %>
<% end %>
Did the same for womenswear aswell.
But i'm getting a NoMethodError
undefined method 'products' for #<User::ActiveRecord_Relation:0x007fbd25e42ac0>
EDIT: I might have been unclear initially, i want the view to show the products sorted by menswear and womenswear, Please help! Thanks
Any pointers on what i'm doing wrong would be appreciated.
Thanks in advance!
Migration
I added gender to Users later and not in the original migration
def up
add_column :users, :gender, :integer
add_index :users, :gender
end
def down
remove_column :users, :gender
end
UPDATE: I changed the users controller to this
def index
#partners = User.partner
#male = User.find_by(gender: 0)
#female = User.find_by(gender: 1)
end
It works but only returns the first instance in both the male and female. I need it to return all of it!
You are mixing has_one :through association with has_many :through association here. You can check proper documentation here. You are having has_one :store and trying to make association has_many :products, through: :store, which is not possible.
Either make it has_one through like:
has_one :store
has_one :product, through: :store
Or for your case make it has_many :through like:
has_many :stores
has_many :product, through: :stores
Or you can remove through from has_many :products, through: :store and make it has_many :products
Choosing solution is upto your requirement.
Try replacing:
def index
#partners = User.partner
#male = User.find_by(gender: 0)
#female = User.find_by(gender: 1)
end
with
def index
#partners = User.partner
#male = User.where(gender: 0)
#female = User.where(gender: 1)
end

Record not saving to join table in has_many through relationship

In this Rails app, Users write Stories. Users can create Collections to group their Stories. However, they are allowed to publish Stories that don't belong to any Collection.
When creating a Story, I want the join table Story_Collections to save the Collection/Story ID pairs but it isn't working. Any help is appreciated! :)
Here's what I have
collection.rb
class Collection < ActiveRecord::Base
belongs_to :user
has_many :story_collections
has_many :stories, through: :story_collections
end
story.rb
class Story < ActiveRecord::Base
belongs_to :user
has_many :story_collections
has_many :collections, through: :story_collections
has_many :photos
end
story_collection.rb
class StoryCollection < ActiveRecord::Base
belongs_to :story
belongs_to :collection
end
In views/stories/new.html.erb
<%= f.select :collection_ids, Collection.all.pluck(:name, :id), {}, { multiple: true, class: "selectize" } %>
Creating the collections in collections_controller.rb
class CollectionsController < ApplicationController
def create
#collection = current_user.collections.build(collection_params)
if #collection.save
render json: #collection
else
render json: {errors: #collection.errors.full_messages}
end
end
private
def collection_params
params.require(:collection).permit(:name, :description)
end
end
Creating the stories
class StoriesController < ApplicationController
def new
#story = Story.new
authorize #story
end
def create
#story = current_user.stories.build(story_params)
authorize #story
end
private
def story_params
params.require(:story).permit(:title, :description, category_ids: [],
photos_attributes: [:id, :file_name, :file_name_cache, :_destroy])
end
end
The Story and Collection tables are saving correctly, only the join table is not. Here's the schema for the join table.
create_table "story_collections", force: :cascade do |t|
t.integer "story_id"
t.integer "collection_id"
t.datetime "created_at"
t.datetime "updated_at"
end
You are missing strong-params permitting the parameter story[collection_ids]
def story_params
params.require(:story).permit(
:title,
:description,
collection_ids: [], # you need to whitelist this, so the value gets set
category_ids: [],
photos_attributes: [
:id,
:file_name,
:file_name_cache,
:_destroy
]
)
end

Rails has_many :through association: Updating all 3 models at the same time

This question follows up on Rails has_many :through association: save instance into join table and I am restating things here for more clarity.
In our Rails app, there are 3 models:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
end
And here are the corresponding migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.integer :total_calendar_count
t.integer :owned_calendar_count
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
Here is what we are trying to accomplish:
When a logged in user (current_user) creates a calendar, we should:
Create a new #calendar and save it to the Calendar table
Assign the "Creator" role to the user (current_user) for this newly created calendar through the Role column in the Administration table
Increment the total_calendar_count and the owner_calendar_count columns of the User table
In order to do that, we think we need to work on calendars#create.
In the CalendarsController, we already have the following code:
def create
#calendar = current_user.calendars.create(calendar_params)
if #calendar.save
flash[:success] = "Calendar created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
And we collect data from users through the following _calendar_form.html.erb form:
<%= form_for(#calendar) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_field :name, placeholder: "Your new calendar name" %>
</div>
<%= f.submit "Create", class: "btn btn-primary" %>
<% end %>
We are considering updating the controller as follows:
def create
#calendar = current_user.calendars.create(calendar_params)
#current_user.total_calendar_count += 1
#current_user.owned_calendar_count += 1
current_user.administrations << #calendar.id
#calendar.administration.role = 'Creator'
if #calendar.save
flash[:success] = "Calendar created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
ActiveRecord::AssociationTypeMismatch in CalendarsController#create
Administration(#70307724710480) expected, got Fixnum(#70307679752800)
unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
raise ActiveRecord::AssociationTypeMismatch, message
end
end
app/controllers/calendars_controller.rb:7:in `create'
How can we make it work?
This line is actually causing the error: current_user.administrations << #calendar.id.
current.administrations expects an object of type Administration while you are passing a Fixnum into it.
You can accomplish the same functionality in the following way:
current_user.administrations.create(calendar_id: #calendar.id)
Edit:
As OP asked in comments that it is a good practice or not. See, there is rule that says that controllers should be skinny, and models should be fatty. Well, it means you should try to write minimum code, and all the logic and fetching of objects should be there in models. But that isn't the case in your code scenario. You should move your code into model, and then call that into your controller.
Here's how:
class User < ActiveRecord::Base
def add_calendar_and_role(calendar_id, role)
self.administrations.find_by(calendar_id: calendar_id).update(role: role)
end
end
This way, your code reduces to just:
current_user.add_calendar_and_role(#calendar.id, 'Creator')
And on the same way, you can further refactor your controller code.

Ruby on Rails - Associating a service with a shop. How to integrate this relationship in the 'create' method

I'm creating an app where I have a 'Shop' model, which is associated with a 'Service' model (For example, a shop might offer different services which can be added by the shop owner only - such as an oil change, new tires, window tinting etc.)
I believe I have the relationship sorted:
class Service < ActiveRecord::Base
belongs_to :shop
end
and
class Shop < ActiveRecord::Base
belongs_to :user
has_many :services
end
However I'm not sure how to add this association in the 'Service' create method so that 'service' belongs only to 'shop'
I'd also like to display all related services on the Shop 'show' page.
I'm in the 'Show' view of Shop, and I want to add a service, how can I pass the shop_id into the service and what files would I have to change?
The create method is just the bog standard scaffold one:
def create
#service = Service.new(service_params)
#service.save
respond_with(#service)
end
And the database looks like this:
Shops:
class CreateShops < ActiveRecord::Migration
def change
create_table :shops do |t|
t.string :name
t.text :description
t.string :phone
t.string :email
t.string :website
t.integer :user_id
t.timestamps
end
end
end
and Services:
class CreateServices < ActiveRecord::Migration
def change
create_table :services do |t|
t.string :category
t.integer :shop_id
t.timestamps
end
end
end
Any help would be very appreciated. I guess i'm just not getting how to pass the relationship information correctly.
Cheers!
First you will need a set of nested routes for shops & services:
# config/routes.rb
resources :shops do
resources :services
end
This creates routes for services nested under shop:
Prefix Verb URI Pattern Controller#Action
shop_services GET /shops/:shop_id/services(.:format) services#index
POST /shops/:shop_id/services(.:format) services#create
new_shop_service GET /shops/:shop_id/services/new(.:format) services#new
edit_shop_service GET /shops/:shop_id/services/:id/edit(.:format) services#edit
shop_service GET /shops/:shop_id/services/:id(.:format) services#show
PATCH /shops/:shop_id/services/:id(.:format) services#update
PUT /shops/:shop_id/services/:id(.:format) services#update
DELETE /shops/:shop_id/services/:id(.:format) services#destroy
shops GET /shops(.:format) shops#index
POST /shops(.:format) shops#create
new_shop GET /shops/new(.:format) shops#new
edit_shop GET /shops/:id/edit(.:format) shops#edit
shop GET /shops/:id(.:format) shops#show
PATCH /shops/:id(.:format) shops#update
PUT /shops/:id(.:format) shops#update
DELETE /shops/:id(.:format) shops#destroy
If look at the parameters for the services routes you can see that there is a :shop_id parameter.
We can use this in our ServicesController to set the shop before each action:
class ServicesController < ApplicationController
before_action :set_shop
# GET /shops/:shop_id/services/new
def new
#service = #shop.services.new
end
# POST /shops/:shop_id/services
def create
#service = #shop.services.new(service_params)
# ... #todo validate and save
end
private
def service_params
params.require(:service).permit(:category)
end
def set_shop
#shop = Shop.find(params[:shop_id])
end
end
In your views you need to change your forms somewhat so that form_for uses the correct path.
<!-- views/services/new.html.erb -->
<h1>Add a service to <%= #service.shop.name %></h1>
<%= form_for([#service.shop, #service]) do |f| %>
<div>
<%= f.label :category %>
<%= f.text_field :category %>
</div>
<%= f.submit %>
<% end %>

How to make a many to many association in rails

I am trying to make a forum application with Rails 4. I want users to have many forums and so I know I need a many-to-many relationship. I have a form to save the title and the description of the new forum. I Have 3 tables so far, users, forums, and forums_users. Everything works great when I create a new form and it gets added to the forums database. My question is how do I get the information to go to the forums_users table? Because right now when I submit my form, it does not add the information to the association table.
Here is my migration file for forums.
def up
create_table :forums do |t|
t.string :title
t.text :description
t.string :logo
t.boolean :is_active, default: true
t.timestamps
end
add_index :forums, :title
create_table :forums_users, id: false do |t|
t.belongs_to :forum, index: true
t.belongs_to :user, index: true
end
end
def down
drop_table :forums
drop_table :forums_users
end
These are my models.
class Forum < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :forums
end
Here is my create method in the Forum Controller
def create
#forum = Forum.new(forum_params)
#forum.save
respond_to do |format|
format.html{redirect_to admin_path, notice: 'New forum was successfully created.'}
end
end
private
def forum_params
params.require(:forum).permit(:title, :description, :logo, :is_active)
end
And here is the form you submit.
= simple_form_for(:forum, url: {action: :create, controller: :forums}) do |f|
= f.error_notification
.form-inputs
= f.input :title, autofocus: true
= f.input :description, as: :text
.form-actions
= f.button :submit
Thank you in advance.
If you want to get the data from your join table forum_users then use has_many :through
class Forum < ActiveRecord::Base
has_many :users, through: :forum_users
end
class User < ActiveRecord::Base
has_many :forums, through: :forum_user
end
class ForumUser < ActiveRecord::Base
belongs_to :user
belongs_to :forum
end
Now you can access/fetch the forum_users table data using UserForum Model
Create the forum using a reference to the current user, for example:
#forum = current_user.forums.create(forum_params)

Resources