Rails 3 database query has many through link - sql

I have a posts table and a tags table joined through a tagging table (Rails 3)
I would like to search for a keyword in the Posts *name* field and also any associated tags.
class Post < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :posts, :through => :taggings
class Tagging < ActiveRecord::Base
belongs_to :post
belongs_to :tag
var = "word"
Post.where("name LIKE ?","%#{var}%")
This will search the name field but I have no idea where to start to also search for Posts with a Tag matching "var"?
Do I have to do 2 separate searches one for Posts and one for Tags or can they be joined into one search somehow?

Post.includes(:tags).where(["posts.name LIKE (?) AND tags.tag LIKE (?)", "%#{var}%", "%#{var}%"])

This should work at least in Rails 3
Post.joins(:tags).where('tags.name LIKE ?', "%#{var}%")

Related

Extra fields for join tables

I am building an Web app using Rails 3.2.
I have three tables. Item, Task and Articles.
TASK
has_many :articles
has_many :items, :through => :articles
ITEM
has_many :articles
has_many :tasks, :through => :articles
ARTICLE
belongs_to :task
belongs_to :item
In the join table (Article) I got an extra field called amount.
How can I set this field when creating the relationship?
Right now I do it like this but it does not feel "right".
Article.create(item_id: self.item_id, task_id: self.id, amount: self.item_amount)

SQL - Rails Deep nested joins

First of all, I'm French, so sorry for my ugly english.
I have following models :
Shop
has_and_belongs_to_many :products
Product
has_and_belongs_to_many :shops
has_many :taggings
has_many :tags, through: :taggings
Tagging
belongs_to :tag
belongs_to :product
Tag
has_many :taggings
has_many :products, through: :taggings
I want to be able to do Shop.first.tags, so I want to get all shop's products' tags, in a single request if it's possible. If I could have explanations with, it will be fine :)
You have not specified a relationship for tags on your Shop model. You'll need to add that relationship in order to call #tags on a single object.
class Shop
# ...
has_many :tags, :through => :products
# ...
end

Union of 2 active record relation object in rails 3

I have a content model represented by class: content. Now users can rate content, review content or do both. I want to find all the content that a user have either rated, reviewed or rated and reviewed. The reviews table has a many-to-one association with the content table (meaning a content can be reviewed many times). A similar relationship exists between the ratings table and the content table.
I'm thinking I should do separate queries to find all rated content by a user, then all reviewed content by a user, then do a union. But I can't find out how to do a union that returns an active record relation. I need a relation because I want to paginate the results.
Thank you.
Ok, so first let's set up your models. From your explanation I'm thinking you'll want something like this:
class Content < ActiveRecord::Base
has_many :reviews
has_many :reviewing_users, :through => :reviews, :class_name => "User"
has_many :ratings
has_many :rating_users, :through => :ratings, :class_name => "User"
end
class User < ActiveRecord::Base
has_many :reviews
has_many :reviewed_contents, :through => :reviews, :class_name => "Content"
has_many :ratings
has_many :rated_contents, :through => :ratings, :class_name => "Content"
end
class Review < ActiveRecord::Base
belongs_to :content
belongs_to :user
end
class Rating < ActiveRecord::Base
belongs_to :content
belongs_to :user
end
And then for a given user you can find all the content that they've reviewed and/or rated with:
( user.reviewed_contents + user.rated_contents ).uniq
(user.reviewed_contents + user.rated_contents).uniq returns an array, not a relation, so beware. You can test this by attempting to call a class method on #posts (other than paginate).
You can still paginate though. just use #posts.paginate, as the will_paginate gem adds a paginate method to the array class.

Rails sort entries based on attributes of related model

I have a strange scenario: I would like to order all posts P by the time at which P's creator and the current user became friends.
The result would be a list of posts with newer friends' posts at the top and older friends' posts at the bottom. We can assume all users are friends of all other users, but each friendship has varying creation times.
I have Post, User, and Friendship models. Posts belong to users and users have many friendships.
class User < ActiveRecord::Base
has_many :friendships
has_many :friends, :through => :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_friendships, :source => :user
end
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => "User"
end
class Post < ActiveRecord::Base
belongs_to :user
end
What's the best way to do this? Thanks!
You can order on the associated table by doing a nested join, like so:
#posts = Post.joins(:user => :friendships).order("friendships.friended_at")
Turn Sean Hill's answer into a scope with:
class Post < ActiveRecord::Base
belongs_to :user
scope :ordered, -> {
joins(:user => :friendships).order("friendships.friended_at")
}
end
Just an addendum to this, as a thank you, Sean Hill's answer also works, slightly modified, to sort results using has_many through relationships with fields_for in Rails 4.
So if Messages belong_to both Conversations and Users and you first want a list of Conversations on the User page—perhaps before updating or modifying some message attribute—but sorted by an attribute on Conversations (e.g. "title"), not Messages, you can go from User through Messages to Conversations and Conversation attributes.
In the view:
<% fields_for :messages, #user.messages.joins(:conversation).order("title ASC") do %>
or in the User.rb model:
has_many :messages, -> {joins(:conversation).order("title ASC")}

Joining 3 tables in Ruby on Rails 3

I am trying to extract all Posts for a given User in the bellow relationships. Not sure whether I got them right, so I'll better explain. A User has Ownerships and Memberships in some Groups. A User can be either a Member or an Owner of the Group, but not both. Every Post has an id of the user and of the group. I think the problem is due to the relationships noted below. How can I get around it? One more thing. I have to also find all posts that were posted by other users in the user's groups. In other words, I have to pass through groups.
/-- Owner ---\
User -- -- Group -- Post
| \-- Member --/ |
|_______________________________|
class User < ActiveRecord::Base
has_many :posts, :dependent => :destroy
has_many :ownerships, :foreign_key => "user_id", :dependent => :destroy
has_many :memberships, :foreign_key => "user_id", :dependent => :destroy
# Problem with these two? I think so.
has_many :groups, :through => :memberships, :source => :user
has_many :groups, :through => :ownerships, :source => :user
class Ownership < ActiveRecord::Base
belongs_to :users, :class_name => "User"
belongs_to :groups, :class_name => "Group"
has_many :posts, :through => :groups, :source => :posts
class Membership < ActiveRecord::Base
belongs_to :users, :class_name => "User"
belongs_to :groups, :class_name => "Group"
has_many :posts, :through => :groups, :source => :posts
class Group < ActiveRecord::Base
has_many :posts, :dependent => :destroy
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :groups
The errors is coming from the line:
_groups = user.groups
The error as following:
Could not find the source association(s) :user in model Ownership. Try 'has_many :groups, :through => :ownerships, :source => '. Is it one of :users, :groups, or :postings?
First up: you're getting that error you're seeing because you've defined the associations in the Membership and Ownership table as this:
belongs_to :users
When they should belong to only one user, i.e. singular user:
belongs_to :user
But even then you will run into problems!
I think having a Membership model and an Ownership model are what will trip you up next. I don't understand what the purpose of having an Ownership model provides, other than signifying ownership of a group, which could be done by a field on the memberships table's records called owner for instance. It's over-engineering.
The problem with the Rails code you've got there is that you're defining that you have many posts through one association and then you're telling it that you have many posts through another association. In effect, you're doing this:
def posts
# find posts for the groups that I own
end
def posts
# find posts for the groups I belong to
end
It is not mistake here that there are two identically-named methods. This is exactly what you are doing by defining two has_many associations with the same name.
So hopefully now you can see why having an Ownership and a Membership model is the path to madness.
I would really recommend that you just have a Membership model that has a boolean attribute declaring an owner for a group. This would also mean that, if you wanted to, you could have new owners for a group in a very easy fashion: just flip the boolean. No need to create another record in another table.
One Membership model to rule them all.