Which model relationship should be used here? - ruby-on-rails

Table: Raw_Data
Fields: id
name
Table: Actual_Data
Fields: id
raw_data_id
name
A use can either add data manually to Actual_Data (in which case it's raw_data_id is NULL) or they can select a Raw_Data to insert into Actual_Data (in which case it's raw_data_id is set).
Is the following relationships correct in this case?
For Raw_Data:
-none-
For Actual_Data:
has_one :raw_data

You are right the relationship should be the same as you said,
class ActualDate < ActiveRecord::Base
has_one :raw_data
end
class RawData < ActiveRecord::Base
belong_to :actual_data
end
As per the rails convention, the foreign key should belongs to the table which has belongs_to association, so here actual_data_id should be in raw_datas table.
Edit:
We don't need raw_data_id in actual_datas table.

I guess the better option is belongs_to.
Raw_Data has many or has one Actual_Data
Actual_Data belongs to Raw Data.
You just have to remember to check if raw_data is nil before doing any operations.
manually_created = ActualData.create(:name => "Something");
builded = #RawData.build_actual_data(:name => "Something else");
The first will have the column set to null, and the second exaple, will set raw_data_id to #RawData.id value.

Related

Rails/Postgresql: Foreign keys stored in an array not returning all associated rows

I have two models
Product { id, name, tags_id[] }
Tag { id, name }
tags_id is an array that stores the ids of Tags.
class Product < ActiveRecord::Base
has_many :tags, foreign_key: 'id'
end
Let's say I have a product with two tags(1,2)
When I query for the product, it only loads one tag, the first one.
Product.includes(:tags).all
Here is how the query looks like:
SELECT "tags".* FROM "tags" WHERE "tags"."id" IN (1)
Is there an option I need to pass to has_many of Product to receive both the tags? Is this even possible?
Thanks for the help.
Try tag_ids, not tags_id column.
As I know it, you can't do that. Just place standard product_id column for tags, and remove tag_ids column.
Your current setup would not work.
You need to check has_array_of gem from here.

Rails : How to query a field in a associated table when the foreign_key now following the convention

Tables
claims belongs_to towns
Model
The foreign key isn't following the convention as it is much clearer and I should have multiple relations to the same table in the future.
class Claim < ActiveRecord::Base
belongs_to :risk_town, class_name: "Town"
end
Query
When I try to get claims filtered by town postal code, I use this query :
Claim.joins(:risk_town).where(risk_town:{postal_code:70000})
Got this error
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: risk_town.postal_code
SELECT "claims".* FROM "claims" INNER JOIN "towns" ON "towns"."id" = "claims"."risk_town_id" WHERE "risk_town"."postal_code" = ? [["postal_code", "70000"]]
I don't understand why rails doesn't alias "towns" to match my relation name "risk_town".
What are the workaround is this case ?
Thanks
Claim.joins(:risk_town).where(towns:{postal_code:70000})
Table name is towns not risk_town
Inside joins you specify association, inside where you specify table name

How to query an ActiveRecord table using conditions based on another table?

I have two models - Blog and BlogTag. Blog has_many :blog_tags and BlogTag belongs_to :blog:
blog.rb
has_many :blog_tags
blog_Tag.rb
belongs_to :blog
I want to query the database to select all blogs that have tags matching in the blog_tags table based on what a user enters in a form field. Something like this:
Blog.where(blog_tags contain [array of tags])
Is there a way to do this with ActiveRecord?
Assuming BlogTag has a column name.
Blog.joins(:blog_tags).where(blog_tags: { name: [array of tags] }).uniq
This would still return Blogs, but only blogs with blog tags whose name is in the array.
Another approach to this is by passing a BlogTag relation into a Blog scope.
Blog.where(id: BlogTag.where(name: tags).select(:blog_id))
So instead of a JOIN, ActiveRecord will construct a subquery
SELECT * FROM blogs WHERE id IN (
SELECT blog_id FROM blog_tags WHERE name IN ('tag1', 'tag2', 'tag3')
)

Fetch COUNT(column) as an integer in a query with group by in Rails 3

I have 2 models Category and Article related like this:
class Category < ActiveRecord::Base
has_many :articles
end
class Article < ActiveRecord::Base
belongs_to :category
def self.count_articles_per_category
select('category_id, COUNT(*) AS total').group(:category_id)
end
end
I'm accessing count_articles_per_category like this
Article.count_articles_per_category
which will return articles that have 2 columns: category_id and total.
My problem is that total column is a string. So the question is: is there a method to fetch that column as an integer?
PS: I tried to do a cast in the database for COUNT(*) and that doesn't help.
I try to avoid doing something like this:
articles = Article.count_articles_per_category
articles.map do |article|
article.total = article.total.to_i
article
end
No, there is no support in ActiveRecord to automatically cast datatypes (which are always transferred as strings to the database).
The way ActiveRecord works when retrieving items is:
for each attribute in the ActiveRecord model, check the column type, and cast the data to that type.
for extra columns, it does not know what data type it should cast it to.
Extra columns includes columns from other tables, or expressions.
You can use a different query, like:
Article.group(:category_id).count
Article.count(:group => :category_id)
These return a hash of :category_id => count. So you might get something like {6=>2, 4=>2, 5=>1, 2=>1, 9=>1, 1=>1, 3=>1}.
Using the count method works because it implicitly lets ActiveRecord know that it is an integer type.
Article.group(:category_id).count might give you something you can use. This will return a hash where each key represents the category_id and each value represents the corresponding count as an integer.

ActiveRecord find categories which contain at least one item

Support I have two models for items and categories, in a many-to-many relation
class Item < ActiveRecord::Base
has_and_belongs_to_many :categories
class Category < ActiveRecord::Base
has_and_belongs_to_many :items
Now I want to filter out categories which contain at least one items, what will be the best way to do this?
I would like to echo #Delba's answer and expand on it because it's correct - what #huan son is suggesting with the count column is completely unnecessary, if you have your indexes set up correctly.
I would add that you probably want to use .uniq, as it's a many-to-many you only want DISTINCT categories to come back:
Category.joins(:items).uniq
Using the joins query will let you more easily work conditions into your count of items too, giving much more flexibility. For example you might not want to count items where enabled = false:
Category.joins(:items).where(:items => { :enabled => true }).uniq
This would generate the following SQL, using inner joins which are EXTREMELY fast:
SELECT `categories`.* FROM `categories` INNER JOIN `categories_items` ON `categories_items`.`category_id` = `categories`.`id` INNER JOIN `items` ON `items`.`id` = `categories_items`.`item_id` WHERE `items`.`enabled` = 1
Good luck,
Stu
Category.joins(:items)
More details here: http://guides.rubyonrails.org/active_record_querying.html#joining-tables
please notice, what the other guys answererd is NOT performant!
the most performant solution:
better to work with a counter_cache and save the items_count in the model!
scope :with_items, where("items_count > 0")
has_and_belongs_to_many :categories, :after_add=>:update_count, :after_remove=>:update_count
def update_count(category)
category.items_count = category.items.count
category.save
end
for normal "belongs_to" relation you just write
belongs_to :parent, :counter_cache=>true
and in the parent_model you have an field items_count (items is the pluralized has_many class name)
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
in a has_and_belongs_to_many relation you have to write it as your own as above
scope :has_item, where("#{table_name}.id IN (SELECT categories_items.category_id FROM categories_items")
This will return all categories which have an entry in the join table because, ostensibly, a category shouldn't have an entry there if it does not have an item. You could add a AND categories_items.item_id IS NOT NULL to the subselect condition just to be sure.
In case you're not aware, table_name is a method which returns the table name of ActiveRecord class calling it. In this case it would be "categories".

Resources