Trouble establishing a has_and_belongs_to_many relationship in Rails - ruby-on-rails

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

Related

Rails: ActiveRecord Association returns a nil relationship

I'm trying to print all the comments and their associated users for a post on a blog. The comments are passed into the view, and their users_id and posts_id are accessible, but I cannot access their users; comment.user returns nil.
Model:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
Migration:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :text
t.references :users
t.references :posts
t.timestamps
end
end
end
In the controller:
#post = Post.find(params[:id])
#comments = Comment.where(posts_id: params[:id]).order(:created_at)
In the view:
<% #comments.each do |c| %>
<% c.user.nil? %>
<p><%=c.text%></p>
<p><%=c.users_id%></p>
<p><%=c.posts_id%></p>
<% else %>
<p><%=c.text%></p>
<p><%=c.user.name%></p> #Here is where things would break if I didn't check "c.user.nil?"
<% end %>
<% end %>
The text and ids for the comments print as expected, but the user is still considered nil. Does anyone know how to access user from within the comment model in the view?
I believe the issue are the names of the foreign key columns in your database. When declaring a post association while explicitly stating the foreign key (you could do this), it's going to look for post_id on the model. So, you should either change your column in the DB to post_id, or change the association method call to this:
belongs_to :post, foreign_key: :posts_id
In your controller:
Change:
#comments = Comment.where(posts_id: params[:id]).order(:created_at)
To:
#comments = Comment.where(post_id: params[:id]).order(:created_at)
You should have a post_id column in your comments table in the database. Then, you can have access to the user for a given comment like this:
comment.user
and eventually:
comment.user.name
will work too!
Your migrations does not seems right to complete what #Jake Shorty said, I would say When doing :
t.references :users
Rails will actually transform it such as :
t.integer 'users_id'
which does not match with the default Rails conventions, so it cannot retrieve the associated user using user_id foreign key.
To fix it, you need to either change the migration, or change your model :
class Comment < ActiveRecord::Base
belongs_to :user, foreign_key: :users_id
end

Select many box for HABTM

I have two models; question and category which have a HABTM association between them. Now I want to have a form where I can edit the questions categories, however I don't know how. I started with this but I am lost, I am unsure on what to name the "name" attributes etc and how it is automatically edited/created with the question, how do I set this up?
<%= f.fields_for :categories do |categories_form| %>
<%= categories_form.select "category_ids", Category.all.collect { |c| [c.description, c.id] }, {}, {:multiple => true, :size => 9} %>
<% end %>
I managed to set up question(has_many) --> answer with fields_for and accepts_nested_attributes_for, but not this.
You should take a look at the following screencasts by Ryan Bates Nested Model Form Part 1 and Nested Model Form Part 2.
Migrations
You need to create the migrations for the tables
You need to create the migration for the middle table of the association
+ the middle table name that is created by the association is :categories_questions
or :questions_categories, in the second case you must define the name in models as shown in the link
Do I need to manually create a migration for a HABTM join table?
class CreateCategoriesQuestions < ActiveRecord::Migration
def self.up
create_table :categories_questions, :id => false do |t|
t.references :category
t.references :question
end
add_index :categories_questions, [:category_id, :question_id]
add_index :categories_questions, [:question_id, :category_id]
end
def self.down
drop_table :categories_questions
end
end
Question Model
class Question < ActiveRecord::Base
has_and_belongs_to_many :categories
end
Category Model
class Category < ActiveRecord::Base
has_and_belongs_to_many :questions
end
Controller Stuf
questions_controller.rb
def new
#question = Question.new
#question.categories.build #Build a categories_questions so as to use fields_for
end
Form Stuff
= f.fields_for :categories do |categories_fields|
= categories_fields.text_field :name
= categories_fields.text_field :description
At this point i must tell you ( i am new in ruby & rails ) that to create a new object here you can use jquery to append a html block name properly, or create helpers (that use javascript in the end) to add a new object and on save, save the association.
In the next link someone demonstrated the exact way .
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for#512-Setting-child-index-while-using-nested-attributes-mass-assignment

Can not call my associate table in my view

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.

how to use array variables inside action in controller

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)

How to Nest Models within a Model

Imagine I have two models
Film
-name
-description
-duration
-year_made
-rating
-actors
Actor
-name
-d_o_b
-biography
-films
Actors are nested in a Film and vice versa.
How do I represent this relationship in my Ruby models? Realistically I would have a third table mapping actor_id with film_id.
Whilst adding details to a film I would like to be able to create an actor on the fly(if an actor does not exist create a new one with the name supplied)
Thank you in advance.
ADDITION:
Just found a link to a similar question.
You're looking at a Has and Belongs to Many (HABTM) relationship between the two tables.
Read about HABTM relationship in the Rails guides here: http://edgeguides.rubyonrails.org/association_basics.html#has_and_belongs_to_many-association-reference
First you'll need to generate a migration which will look something like this:
class AddActorFilmTable < ActiveRecord::Migration
def self.up
create_table :actors_films, :id => false do |t|
t.integer :actor_id, :null => :false
t.integer :film_id, :null => :false
end
add_index :actors_films, [:actor_id, :film_id], :unique => true
end
def self.down
drop_table :actors_films
end
end
and then specify in your models:
class Actor < ActiveRecord::Base
has_and_belongs_to_many :films
end
class Film < ActiveRecord::Base
has_and_belongs_to_many :actors
end
This will allow you to use all of the additional Rails methods for this type of relationship. To use this in a form, you could follow RailsCast 17: HABTM Checkboxes - though it's old, it should still apply. Alternatively, you can use a gem like Simple Form to easily generate the associations for you like so:
form_for #actor do |f|
f.collection_check_boxes :film_ids, Film.all, :id, :name
end

Resources