How to join 1:N tables in rails and instead of multiple rows for record get one row with extra column containing array of IDs from joined table - ruby

I have 3 models
class Mission < ActiveRecord::Base
belongs_to :guild
end
class Guild < ActiveRecord::Base
has_many :missions
has_many :guild_coordinators, :dependent => :destroy
has_many :coordinators, :through=> :guild_coordinators, :class_name => "Associate"
end
class GuildCoordinator < ActiveRecord::Base
belongs_to :guild
belongs_to :coordinator, :class_name => "Associate"
end
If I do
Mission.joins(:guild => :guild_coordinators)
I get row for every guild -> guild coordinator association
Is it possible to get unique records for Missions with joined Guilds and in one column get IDs of all coordinators in an Array?
edit:
expected result is something like this:
#<ActiveRecord::Relation [#<Mission id: 13, fy: 2018, guild_id: 31, name: "test mission", status: 0, coordinators: [1,2,3,5,8]>
my database is postgres
as output I need Active Record relation for gem ajax-datatables-rails

With Postgresql you can use array_agg aggregate function:
Mission.
joins(guild: :guild_coordinators).
select('missions.*, array_agg(guild_coordinators.id) as coordinators').
group(:id)
And you get exactly ActiveRecord::Relation, which will contain(after call) Mission objects with additional field coordinators:Array.
The second option is to use .includes like my or #garrett-motzner comments show.

Related

removing duplicate entry for csv import in ruby

while importing csv row i need to check wether this is duplicate entry or new entry.
my csv looks like this,
company,location,region,service,price,duration,disabled
Google,Berlin,EU,Design with HTML/CSS,120,30,false
Google,San Francisco,US,Design with HTML/CSS,120,30,false
Google,San Francisco,US,Restful API design,1500,120,false
Apple,London,EU,Design with HTML/CSS,120,30,false
Google,Berlin,EU,Design with HTML/CSS,120,30,false
Google,San Francisco,US,Restful API design,1500,120,false
Also the row value should be imported in different table whose association is like this
A Company:
can have multiple regions: US, EU and each region multiple branches, i.e. London, Berlin. Define a hierarchy to represent this logic.
has many Services. If there are more branches, they will all share the same services
can be disabled
A Service:
has a duration
has a price
can be disabled
if the company is disabled, all services are disabled.
for which i have implemented association like this
class Company < ApplicationRecord
has_many :regions
has_many :services
has_many :locations, through: :regions
end
class Region < ApplicationRecord
belongs_to :company
has_many :locations
end
class Location < ApplicationRecord
belongs_to :region
belongs_to :company
end
class Service < ApplicationRecord
belongs_to :company
end
How will I import this?
I am doing something like this
namespace :import do
desc "Import data"
task company: :environment do
CSV.foreach('lib/data/companies_data.csv', headers:true) do |row|
company = Company.create(:name => row["company"])
region = company.regions.create(:name => row["region"])
if region.id and company.id
location = company.locations.create(:name =>row["location"],
:region_id => region.id)
service = company.services.create(:name => row["service"],
:price => row["price"], :duration =>
row["duration"], :disabled =>row["disabled"])
end
end
end
end
How do I check wether a row is already present in database as it already contains associated table.
You can use .first_or_create. This will only create new db records if there is not a match on the previous ActiveRecord Relation call:
Model.where(some_unique_field: row['Unique Column']).first_or_create(row)
You can also pass a block to first_or_create as listed in the docs if you want to apply any additional logic to the CSV row ie model.price = row['price'] + fee

Retrieve records count from has_many relation

I have the following models
Bookrack.rb
has_many :faculties
Faculty.rb
belongs_to :bookrack
has_many :books
Book.rb
belongs_to :faculty
has_many :barcodes
Barcode.rb
belongs_to :book
I have a controller name bookrack controller. I can extract record of barcode in Bookrack using loop and empty array. Is there any simpler method to extract barcode record from Bookrack.
Did you try this (for example for counting barcodes of the first book in the first faculty in the first bookrack):
b = Bookrack.first.faculties.first.books.first.barcodes.count
This should probably help..
Bookrack.rb
has_many :faculties
has_many :books, through: :faculties
has_many :barcodes, through: :books
Then you can do,
Bookrack.find(1).barcodes.count
Also to note that, has_many relations has to be plural, your model seems inappropriate..or correct it if it's a typo..
With model relationships like:
# app/models/bookrack.rb
has_many :faculties
# app/models/faculty.rb
belongs_to :bookrack
has_many :books
# app/models/book.rb
belongs_to :faculty
has_many :barcodes
# app/models/barcode.rb
belongs_to :book
You could use joins in a "nested" way:
Bookrack.joins(faculties: [books: :barcodes]).count
# => SELECT COUNT(*) FROM "bookracks"
# INNER JOIN "faculties" ON "faculties"."bookrack_id" = "bookracks"."id"
# INNER JOIN "books" ON "books"."faculty_id" = "faculties"."id"
# INNER JOIN "barcodes" ON "barcodes"."book_id" = "books"."id"
# => 1

Rails query for instances outside association condition

Product model association:
class Product < ActiveRecord::Base
has_many :likes
has_many :liking_users, through: :likes, source: :user
end
I want to find all Products which the current user has not liked.
Basically, all products except Product.joins(:liking_users).where(users: {id: current_user.id})
Can you think of an efficient query for this?
Product.joins(:liking_users).where.not(users: {id: current_user.id})

has_and_belongs_to_many model creation needed?

I have orders and items table. I also have a third table called orders_items. Which I learned on creating from the following link (2nd graph) http://www.tutorialspoint.com/ruby-on-rails/rails-models.htm
models/order.rb
class Order < ActiveRecord::Base
has_and_belongs_to_many :items, through: :item_order
end
models/item.rb
class Item < ActiveRecord::Base
has_and_belongs_to_many :orders, through: :item_order
end
[orders_items] table has the following:
integer :order_id
integer :item_id
Do I have to create a models/order_item.rb file to add:
belongs_to :order
belongs_to :item
If so what is the correct naming format that it should be?
Would the name for the model file [order_item.rb] correct to distinguish which table it refers to?
models/order_item.rb ??
class OrdersItem ??? < ActiveRecord::Base
belongs_to :order
belongs_to :item
end
From the API
The join table should not have a primary key or a model associated
with it. You must manually generate the join table with a migration
such as this
class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration
def change
create_table :developers_projects, id: false do |t|
t.integer :developer_id
t.integer :project_id
end
end
end
Specifies a many-to-many relationship with another class. This
associates two classes via an intermediate join table. Unless the join
table is explicitly specified as an option, it is guessed using the
lexical order of the class names. So a join between Developer and
Project will give the default join table name of “developers_projects”
because “D” precedes “P” alphabetically
In your case the join table name should be items_orders.
Your model must be named OrderItem. And you don't need belongs_to in this class. The file name (order_item.rb) is correct.
I think you need this relationship to fulfill your needs, except if orders is an item too
class Order < ActiveRecord::Base
has_many :items
end
and
class Item < ActiveRecord::Base
belongs_to :order
end

Ruby on Rails: Associations when a user likes a song

I'm trying to figure out the best way to setup my database and models for the following scenario.
A user can like an infinite number of songs.
A song can be liked once by an infinite number of users.
I have these tables:
songs, users, likes etc... Following RoR conventions.
The table named likes has these foreign keys: user_id, song_id. And also a field named 'time' to save a timestamp when the song was liked.
I'm not sure of how to do this, I would like to be able to use code like this in my controllers:
User.find(1).likes.all
This should not return from the likes table, but join the songs table and output all the songs that the user likes.
What are the best practises to achieve this in Ruby on Rails following their conventions?
Unless you need to act specifically on the likes table data, the model itself is probably not necessary. The relationship is easy:
class User < ActiveRecord::Base
has_and_belongs_to_many :songs
end
class Song < ActiveRecord::Base
has_and_belongs_to_many :users
end
This will join through the currently non-existent song_users table. But since you want it to join through likes you can change each one to this:
has_and_belongs_to_many :songs, :join_table => 'likes'
If you want to be able to call User.find(1).likes and get songs, then change the user's version to this:
class User < ActiveRecord::Base
has_and_belongs_to_many :likes, :join_table => 'likes', :class_name => 'Song'
end
And you could change the songs version to something like this:
class Song < ActiveRecord::Base
has_and_belongs_to_many :liked_by, :join_table => 'likes', :class_name => 'User'
end
This will give you Song.find(1).liked_by.all and give you the users (You could keep it to users if you wanted using the first version)
More details on habtm relationships can be found here: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
Edit to add
If you want to act on the join table for whatever reason (you find yourself needing methods specifically on the join), you can include the model by doing it this way:
class User < ActiveRecord::Base
has_many :songs, :through => :likes
has_many :likes
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :song
end
class Song < ActiveRecord::Base
has_many :users, :through => :likes
has_many :likes
end
This will let you do User.find(1).songs.all, but User.find(1).likes.all will give you the join data

Resources