Rails 3.1 distinct find and missing attributes - activerecord

class State < ActiveRecord::Base
has_many :cities
end
class City < ActiveRecord::Base
belongs_to :state
has_many :companies
end
class Company < ActiveRecord::Base
belongs_to :city
end
I'm trying to list all states, and their respective cities, that contain at least one company registered. My first try was the following query:
states = State.joins(:cities => :companies).includes(:cities)
Which works, but I end up getting duplicates if a state has more than one city with companies in it. I then changed the query to:
states = State.joins(:cities => :companies).includes(:cities).select("distinct(states.id)")
This query almost works. I have access to the cities (states[0].cities), and there are no duplicates, but if I try to access an attribute from the State object, I get the following error:
ruby-1.9.2-p290 :056 >states[0].name
ActiveModel::MissingAttributeError: missing attribute: name
How can I solve this?
Thanks in advance

Your select statement overrides the default (SELECT * FROM ... becomes SELECT distinct(state.id) FROM...) so the results don't include the columns of your state table (where the attributes are inferred from). Try changing your select method to the following:
.select("distinct(states.id), states.*")

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

Activerecord/Datamapper - Have one child belong to many parents

How would you set up an activerecord/datamapper association for the following scenario:
A user creates a "bookshelf" which has many books(a book object just has an isbn that is used to query an api, and has_many review objects associated with it). Let's say Jack creates a "bookshelf" with a book object. Then, lets say that Jill creates a "bookshelf" with the same book object(it has the same id and the same reviews). The book object has the following code as of now:
class Book < ActiveRecord::Base
has_many :reviews
end
Then, when you view the page for a book (you click the link to it from the "bookshelf" created by Jack) you should see the same book object when you clicked the link to it from Jill's "bookshelf" (e.g. both "bookshelves" have a link to /books/23 because they have the same book object).
I have not been able to figure this out with the has_many association because that requires me to make a new book each time a user adds a book to their "bookshelf." I have trouble understanding the has_and_belongs_to_many relationship, is that what should be used here? I was not able to find any similar questions on SO, so any help is greatly appreciated.
I am using Rails 4 with Ruby 2.1.
Here is a drawing of what I would like to accomplish:
Drawing
Yes, you would have to define many-to-many relationship between a Bookshelf and a Book. There are two ways to achieve this in Rails:
Option 1) Use has_and_belongs_to_many
See guide
According to official documentation has_and_belongs_to_many association:
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.
So, your classes should look like this:
class Bookshelf < ActiveRecord::Base
has_and_belongs_to_many :books
end
class Book < ActiveRecord::Base
has_and_belongs_to_many :bookshelves
has_many :reviews
end
Add a join table generation to your migrations:
class CreateBooksBookshelvesJoinTable < ActiveRecord::Migration
def change
create_table :books_bookshelves, id: false do |t|
t.belongs_to :book, index: true
t.belongs_to :bookshelf, index: true
end
end
end
This will create a books_bookshelves table in your database. The table will have no primary key. There would be two foreign keys to your models Book and Bookshelf.
So, if you call self.books in the context of an user's bookshelf, you will get a list of books in the bookshelf. Vice versa, calling self.bookshelves in the context of a book will return a set of bookshelves the book belongs to.
The problem with this approach is that every time you add a new book to the bookshelf a new record is created in the database. If you are okay with that, there is no easier option than using has_and_belongs_to_many association. Otherwise, I recommend you to go with the Option #2.
Option 2) Use has_many :through
Another option is to use has_many, :through association (see guide). You would have to define one more model to do that, but it might come handy in some use cases (see below for an example).
Your classes should look like this:
class Bookshelf < ActiveRecord::Base
has_many :books, through: :books_bookshelves
has_many :books_bookshelves
end
class Book < ActiveRecord::Base
has_many :bookshelves, through: :books_bookshelves
has_many :books_bookshelves
has_many :reviews
end
class BooksBookshelf < ActiveRecord::Base
belongs_to :book
belongs_to :bookshelf
end
Probably the best thing about using has_many :through association is that it allows you to add custom columns to the join table (e.g. add column count to keep track how many books of the same type are there in the bookshelf).
The migration would look pretty much the same as the one we used in Option 1, except for the fact we are adding an unique constraint on the foreign keys (please note that adding the constraint is optional):
class CreateBooksBookshelvesJoinTable < ActiveRecord::Migration
def change
create_table :books_bookshelves, id: false do |t|
t.belongs_to :book, index: true
t.belongs_to :bookshelf, index: true
# add your custom columns here
end
add_index :books_bookshelves, [:book_id, :bookshelf_id], unique: true # to make sure you won't create duplicate records
end
end
By going with this approach, adding a new would be a bit more complicated as you would have to make sure you are not inserting duplicate records in the join table. (However, you may remove the unique constraint from the migration, to achieve exactly the same kind of behavior as you would get with has_and_belongs_to_many.)

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

Rails 3 scope only select certain attributes for a has_many relationship

This looks like it should be something pretty easy but I can't seem to get it to work. I have a model with a has_many relationship and I'd like a scope on the parent that allows me to select only certain attributes for each.
An example:
class Bakery < ActiveRecord::Base
has_many :pastries
scope :summary, select([:id, :name, 'some option calling pastries.summary'])
class Pastry < ActiveRecord::Base
belongs_to :bakery
scope :summary, select([:id, :image_url])
I'd like to be able to call something like Bakery.first.summary and get a Bakery model with only the id and name populated and for each pastry in it's pastries array to only have the id and image_url attributes populated.
You could do this, but it won't affect the SQL queries that are made as a result (assuming you're trying to optimise the underlying query?):
class Pastry
...
def summary
{
:id => self.id,
:image_url => self.image_url
}
end
end
class Bakery
...
def summary
pastries.collect {|i| i.summary }
end
end
This would then give you an array of hashes, not model instances.
ActiveRecord doesn't behave how you're expecting with models - it will fetch whatever data it thinks you need. You could look at using the Sequel gem instead, or executing a raw SQL query such as:
Pastry.find_by_sql("SELECT id, name from ...")
But this could give you unexpected behaviour.

Ruby on Rails 3: How can I sort ActiveRecords by an attribute of another table?

I need to query a database table and get the rows ordered by a count of an association. Is there a Rails (like Active Record Query) way to do this?
My models and their associations are as follows:
class User < ActiveRecord::Base
has_one :business
end
class Business < ActiveRecord::Base
has_many :postulations
end
class Postulation < ActiveRecord::Base
belongs_to :business
end
I need to get a number of Users ordered by the amount of Postulations that their Business has. Is there a clean way to do this or do I just have to query with find_by_sql?
Thank you.
User.includes(:business => :postulations).group("users.id").order("count(postulations.id) desc").limit(20)
This will probably work

Resources