I want to fetch all posts posted by those users who have gone to same college as the current users...So inside my welcome controller i have written following code..
class WelcomesController < ApplicationController
def index
#col = Education.select(:college_id).where(:user_id => #current_user)
#user = Education.select(:user_id).where(:college_id => #col)
#welcome = Welcome.where(:user_id => #user)
end
end
Following is my shema code for welcome and education model:
create_table "welcomes", :force => true do |t|
t.text "message"
t.integer "user_id"
end
create_table "educations", :force => true do |t|
t.integer "college_id"
t.integer "user_id"
end
#col = Education.select(:college_id).where(:user_id => #current_user)....this line returns college ids associated with current logged in user.This is working perfectly on my console which is returning following output..
[#<Education college_id: 1>, #<Education college_id: 2>]
but i dont know how to use this output in my next line,so i have written this statement which should return all the users whose college id is the output of prevous statement
#user = Education.select(:user_id).where(:college_id => #col)
and my last line should return all the posts posted by those users whose ids are inside the #user array:
#welcome = Welcome.where(:user_id => #user)
but this is not working.When i run my project i cant see any output on my page and on console i am getting following output :
SELECT welcomes.* FROM welcomes WHERE (welcomes.user_id IN (NULL))
which means its not getting any user ids..
How can i solve this ...
You can try this:
#col = Education.select(:college_id).where(:user_id => #current_user.id).all
#users = Education.select(:user_id).where(:college_id => #col.collect(&:college_id)).all
#welcome = Welcome.where(:user_id => #users.collect(&:user_id)).all
The best way I see to accomplish this is to set up a has_many_and_belongs_to_many relationship between your User and Education models. (Each Education will have many Users and each User may have multiple Eductions.) You will need to create a joining table in your database to support this type of relationship - see the Rails Guide for more information on this.
I would set up your models in this manner:
class User < ActiveRecord::Base
has_one :welcome
has_and_belongs_to_many :educations
end
class Education < ActiveRecord::Base
has_and_belongs_to_many :users
end
class Welcome < ActiveRecord::Base
belongs_to :user
end
The join table for the has_many_and_belongs_to_many join table migration (be sure to double check this code, not sure I got this exactly right):
def self.up
create_table 'education_user', :id => false do |t|
t.column :education_id, :integer
t.column :user_id, :integer
end
end
Your controller code is now much simpler and looks like this:
#welcomes = #current_user.eductions.users.welcome.all
In your view:
<% #welcomes.each do |welcome| %>
<p><%= welcome.message %></p>
<% end %>
One of the more powerful features of Ruby on Rails is the model relationships. They are a little more work up front, but if you take the time to set them up correctly they can make your life much easier, as is evidenced by the simplified #welcomes query above.
I'd recommend you to make relation between User and Collage
class User < ActiveRecord::Base
has_many :educations
has_many :colleges, :through => :educations
has_many :posts
scope :by_college_id, lambda {|cid| where("exists (select educations.id from educations where educations.user_id = users.id AND educations.college_id in (?) limit 1)", Array.wrap(cid)) }
def college_mates
self.class.by_college_id(self.college_ids).where("users.id != ?", id)
end :through => :educations
end
class Education < ActiveRecord::Base
belongs_to :user
belongs_to :college
end
So now in your controller you can write
class WelcomesController < ApplicationController
def index
#posts = #current_user.college_mates.includes(:posts).map(&:posts).flatten
# or
#posts = Post.where(user_id: #current_user.college_mates.map(&:id))
end
end
Second variant generates 3 sql-requests, first variant - only two. But this is same work with data, I think time will be also same. Usually controllers contain only few lines of code, all logic written in models. Ideally controller should contain only Post.by_college_mates_for(#curren_user.id)
Related
I have tried every possible suggestion I could find on here to make this work but still can't get it to happen.
I have two models: districts and former_offices -- a district can have many former offices and a former office has a district.
Districts:
class District < ApplicationRecord
has_and_belongs_to_many :former_office
end
Former Office:
class FormerOffice < ApplicationRecord
has_and_belongs_to_many :districts
end
Join Model:
class DistrictFormerOffice < ApplicationRecord
belongs_to :district
belongs_to :former_office
end
Here is my form setup for creating a new former office:
<%= bootstrap_form_for(#former_office, label_errors: true) do |f| %>
<%= f.select(:district_ids, District.all.collect { |v| ["#{v.jurisdiction.name} - #{v.name} (District #{v.district}) #{v.term_expires}", v.id ] }, { :include_blank => true}) %></div>
<% end %>
Former Offices controller:
def former_office_params
params.require(:former_office).permit(district_ids: [])
end
Migration:
class CreateJoinTableDistrictFormerOffices < ActiveRecord::Migration[5.2]
def change
create_table :districts_former_offices, id: false do |t|
t.bigint :district_id
t.bigint :former_office_id
end
add_index :districts_former_offices, :district_id
add_index :districts_former_offices, :former_office_id
end
end
I am able to create the Former Office without any errors. All the other fields populate just find, except for "districts." When I pull up the object in the console and type former_office.districts, it comes up as "nil." So it appears either data isn't being added to the database, or is added in the wrong manner.
In the views, if I set it to display #former_office.districts, it just shows up blank.
Let me know if you need further information from my end. I sense this is an easy fix...but just can't figure it out.
I don't think you can add "district_ids" directly into your FormerOffice model. Instead you need to load all the districts from the district model, like #districts = District.find(former_office[:district_ids)), and then do something like #former_office.districts = #districts
I need some help here. I have a community model with belongs to an account. I use devise for the authentication stuff. Now the issue is when I try to submit/create a community I get this error 'undefined method `account_id=' for #Community:0x00007febf2e806f8 Did you mean? account='
controller
def create
#community = Community.new comunity_values
#community.account_id = current_account.id
if #community.save
redirect_to community_path
else
render :new
end
end
private
def comunity_values
params.require(:community).permit(:name, :url,:rules)
end
end
model
class Community <ApplicationRecord
belongs_to :account
validates_presence_of :url, :name , :rules
end
migrations
class CreateCommunities < ActiveRecord::Migration[6.0]
def change
create_table :communities do |t|
t.references :account
t.string :name
t.string :url
t.text :rules
t.string :total_members
t.timestamps
end
end
end
In model account you have to put the reference to communities model too. Something like:
has_many :community
Other think you have to know, is that you don't need to specify the id when you create using relations, you could use in a more readable way:
#community.account = current_account
#community.save
And I think that you are inverting the order of things, because one account has many communities so... You could do that as follows:
current_account.community.create!(comunity_values)
Hope this helps
I'd like to know how to solve this problem in my model/migrations, with correct referential integrity/uniqueness constraints.
I have a user table with two types of user: support_worker and service_user (like teacher and pupil). A support_worker can provide support for many service_users. I used to have separate tables for these respective user types, but for simplicity it makes more sense to have both user types in a single 'user' table (for Devise).
I'll have another table called support_allocation which records the relationship between a support_worker and the service_user(s) they support - this support_allocation has other information stored about it (like a budget; time/money). So this table needs to map one user_id to another user_id. I imagine the table structure will look something like this: SupportAllocation (id, support_worker_id, service_user_id)
So far, my migrations look like this (I've used Devise gem to create the user table so this amends it):
class ChangeUsers < ActiveRecord::Migration[5.2]
def change
change_table :users do |t|
t.string :user_type # support_worker or service_user
t.string :given_name
t.string :family_name
t.string :customer_reference # only for service_users
t.date :date_of_birth # only for service_users
t.string :job_roles # only for support_workers
end
end
class CreateSupportAllocations < ActiveRecord::Migration[5.2]
def change
create_table :support_allocations do |t|
t.boolean :active, default: true
# This next bit is guesswork
t.integer support_worker_id # support_worker's user_id
t.integer service_user_id # service_user's user_id
t.timestamps
end
end
end
Here's where I get confused... I need to create a join, but this will only do it on user_id, whereas the relationship is defined by the two user_id columns (as shown and named above). I'm not sure if this a compound key or if a single foreign key (or two) will suffice.
Here's my migration work-in-progress:
class AddJoins < ActiveRecord::Migration[5.2]
def change
change_table :support_allocations do |t|
t.belongs_to :user, index: true
end
end
end
I'd like to know how to achieve this. For the record, I'm using ActiveAdmin for my app. Thank you for your help.
I don't think you need the AddJoins migration. Add 2 associations in your CreateSupportAllocations model like so:
belongs_to :support_worker, :foreign_key => :support_worker_id, :class_name => User
belongs_to :service_user, :foreign_key => :service_user_id, :class_name => User
In your activeadmin form you can set the collections for the select, for example
(in app/admin/support_allocations.rb)
form do |f|
f.inputs do
# your inputs
f.input :support_worker, :as => :select, :collection => User.where(:user_type => 'support_worker')
f.input :service_user, :as => :select, :collection => User.where(:user_type => 'service_user')
end
f.actions
end
# added after comments
index do
selectable_column
column :support_worker
actions
end
Add a to_s method in you user model like so:
def to_s
"#{self.full_name}"
end
Thanks for all your help. I added the suggested associations to my SupportAllocation model. For the record, I also had to add the following associations to my User model to make the join work fully, in both directions:
has_many :occurances_as_support_worker, :class_name => 'SupportAllocation', :foreign_key => 'support_worker_id'
has_many :occurances_as_service_user, :class_name => 'SupportAllocation', :foreign_key => 'service_user_id'
I used the example given here to work this out.
When accessing attributes specific to a type of user (i.e. using the join over support_worker_id OR service_user_id), on the index page. I use code like this:
column 'Service user', :full_name, :sortable => 'service_users.family_name' do |support_allocation|
#ServiceUser.find(support_allocation.service_user_id).full_name
support_allocation.service_user.full_name
end
column 'Support worker', :full_name, :sortable => 'support_workers.family_name' do |support_allocation|
support_allocation.support_worker.full_name
end
Can not call my associate table in my view. have tried this. it is an application that only adds to the players. after the press "start game" and then he should come to the result view where the results of all players. then I will of course have the name of the "players" table and then the binding results from the "results" table. now in quire, I enter bidningen in the background as long as
view:
<% #playersname.each do |p|%>
<ul>
<li><%= p.name %></li>
<li><%= p.results.try(:result) %></li>
</ul>
<%end%>
Controller:
class ResultsController < ApplicationController
def index
#playersname = Player.all
end
end
Model:
class Result < ActiveRecord::Base
# attr_accessible :title, :body
has_many :players
end
migration:
class CreateResults < ActiveRecord::Migration
def change
create_table :results do |t|
t.string "result", :limit => 40
t.string "cal", :limit => 40
t.string "sum",:limit => 300
t.timestamps
end
end
end
The CreateResults migration miss the player_id column.
Do belongs_to :player in your Result class and correct the migration (or do another one).
[Edit] I wasn't clear enough : I think you inverted the relationship logic.
You actually did the "one result has several players".
I suppose you want that a player has several results ?
class Player
has_many :results
end
class Result
belongs_to :player
end
So you can do :
#myplayer = Player.all.first
#myplayer.results #it is an array of player's results
#myplayerresult = #myplayer.results.first
puts #myplayerresult.result
If you want a one-to-one relationship, consider replacing has_many :results by has_one :result and so you can do #myplayer.result to get your result.
I have a Genre model, and I want both videos to have many genres and profiles to have many genres. I also want genres to have many videos and genres to have many profiles. I understand the polymorphic and join table stuff, so I'm wondering if my code below will work as I intend it to. Also, I'd appreciate any advice on how to access things in my controller and views.
This is what I envision that the join table should look like (I don't think I need an elaborate :has :through association because all I need in the join table are the associations and nothing else, so the table won't have a model):
genres_videos_profiles:
-----------------------------------------------------
id | genre_id | genre_element_id | genre_element_type
Here's my genre.rb:
has_and_belongs_to_many :genre_element, :polymorphic => true
Here's video.rb:
has_and_belongs_to_many :genres, :as => :genre_element
Here's profile.rb:
has_and_belongs_to_many :genres, :as => :genre_element
Will this work as I intend it to? I'd like some feedback.
As far as I know HABTM associations can´t be polymorphic, I couldn´t find an example like yours in the API documentation. If you want only join tables, your code could look like this:
class Genre
has_and_belongs_to_many :videos
has_and_belongs_to_many :profiles
end
class Video
has_and_belongs_to_many :genres
end
class Profile
has_and_belongs_to_many :genres
end
And access it like Mike already wrote:
#genre.profiles
#profile.genres
#genre.videos
#video.genres
Migrations (for join tables only):
class CreateGenresVideosJoinTable < ActiveRecord::Migration
def self.up
create_table :genres_videos, {:id => false, :force => true} do |t|
t.integer :genre_id
t.integer :video_id
t.timestamps
end
end
def self.down
drop_table :genres_videos
end
end
class CreateGenresProfilesJoinTable < ActiveRecord::Migration
def self.up
create_table :genres_profiles, {:id => false, :force => true} do |t|
t.integer :genre_id
t.integer :profile_id
t.timestamps
end
end
def self.down
drop_table :genres_profiles
end
end
I think that has_and_belongs_to_many can be a bit difficult to follow when it comes to polymorphic (if it even works). So if you want to do the polymorhpic thing, then you can't use any "through" syntax:
class Genre < ActiveRecord::Base
has_many :genres_videos_profiles
end
class GenresVideosProfile
belongs_to :genre
belongs_to :genre_element, :polymorphic => true
scope :videos, where(:genre_element_type => "Video")
scope :profiles, where(:genre_element_type => "Profile")
end
And then you use it like:
# All genre elements
#genre.genres_videos_profiles.each do |gvp|
puts gvp.genre_element.inspect
end
# Only video genre elements
#genre.genres_videos_profiles.videos.each do |gvp|
puts gvp.genre_element.inspect
end
Check out that: http://blog.hasmanythrough.com/2006/4/3/polymorphic-through
For me it was perfect and clean!