Rails using 'where' to search association - ruby-on-rails

I want to select all Notices that belong to the Character via the has_one association that have a nil supernotice. How do I code this?
notice.rb:
belongs_to :character
has_one :active_comment_relationship, class_name: "Commentrelationship",
foreign_key: "commenter_id",
dependent: :destroy
has_one :supernotice, through: :active_comment_relationship,
class_name: "Notice",
source: :commentee
accepts_nested_attributes_for :active_comment_relationship
has_many :passive_comment_relationships, class_name: "Commentrelationship",
foreign_key: "commentee_id",
dependent: :destroy
has_many :comments, through: :passive_comment_relationships,
class_name: "Notice",
source: :commenter
character.rb:
has_many :notices
def topNotices
self.notices.where(supernotice: nil) # doesn't work
end
Logs:
: SELECT "notices".* FROM "notices" WHERE "notices"."character_id" = $1 AND "notices"."commentee_id" IS NULL ORDER BY "notices"."created_at" DESC
Completed 500 Internal Server Error in 316ms (ActiveRecord: 12.0ms)
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column notices.commentee_id does not exist
LINE 1: ..."notices" WHERE "notices"."character_id" = $1 AND "notices"....
The logs show the error notices.commentee_id does not exist, but I've clearly stated in notice.rb that a notice has_one :supernotice through: :active_comment_relationship. Where am I going wrong?

You need to add:
has_many :notices
In your character model.

You have a problem in your association in the first place.
When you define associations between two models, you have to set up properly in both of the models.
In your notice.rb Model, you have belongs_to :character association, so you have to define the counterpart of this association in your character.rb Model as well. You have to define a has_many or has_one association in your Character model which in your case is has_many notices.
So, in your character.rb Model, you need to define this association:
has_many :notices
When you setup the associations in both the models properly, then you can expect to get the correct result using:
self.notices.where(supernotice: nil)
because now it knows how your Character model is associated with your Notice model.
I highly recommend you to read the Active Record Associations Guide

Related

has_one through association with condition

I have 3 relevant tables/models. I would like to retrieve the user which has organized a party, given a Party record, a join table with a boolean organized column, and a users table.
My best attempt so far (this one makes the most sense to me after much fiddling). I've omitted the irrelevant columns.
class Party
# party_id
has_many :parties_users
has_many :users, through: :parties_users, source: :user
has_one :organizer,
-> { where organizer: true },
through: :parties_users,
source: :user
class PartiesUser
# party_id
# user_id
# organized:bool
belongs_to :party
belongs_to :user
class User
# user_id
has_many : parties_users
The above setup raises the following error, which I honestly don't fully understand:
ActiveRecord::HasOneThroughCantAssociateThroughCollection (Cannot have a has_one :through association 'Party#organizer' where the :through association 'Party#parties_users' is a collection. Specify a has_one or belongs_to association in the :through option instead.)
I know I can do this via an instance method, but given the frequency types of use, my app would massively benefit from having this as an association.
As the error message says, you can't have a has_one through a has_many.
You could (instead) do it this way if the organizer flag is on the join record...
has_many :parties_users
has_many :users, through: :parties_users, source: :user
has_one :main_party_user, -> {where organizer: true}, class_name: 'PartiesUser'
has_one :organizer, through: :main_party_user, class_name: 'User'

Rails - has_many with different column names?

I have a data model - SeasonTeams with the following relation:
has_many :linked_team_constraints, class_name: 'LinkedTeamConstraints', dependent: :destroy
Then I have the LinkedTeamConstraints model as follows:
belongs_to :season_team, primary_key: :_id
Now when I call destroy on the SeasonTeam - thereby destroying the LinkedTeamConstraint - I get error column linked_team_constraints.season_team_id does not exist. I do have two OTHER columns labeled season_team_id_1 and season_team_id_2. How can I establish a has_many relationship such that linked_team_constraints.season_team_id_1 or ..._id_2 is queried instead of ...season_team_id.
Would it be something like:
has_many :linked_team_constraints... {where("_id == ?", linked_team_constraints.season_team_id_1 || linked_team_constraints.season_team_id_2)}?
You need to specify foreign_key for your relations and have separate relation for each key. Like:
has_many :linked_team_1_constraints, class_name: 'LinkedTeamConstraints', foreign_key: :season_team_id_1, dependent: :destroy
has_many :linked_team_2_constraints, class_name: 'LinkedTeamConstraints', foreign_key: :season_team_id_2, dependent: :destroy

Statement Invalid in Rails Admin

I'm using rails_admin gem. It works fine when I open the admin panel from localhost but then I get this error whenever i tried to show the user inside rails admin panel.
ActiveRecord::StatementInvalid in RailsAdmin::Main#show
PG::UndefinedColumn: ERROR: column relationships.user_id does not exist
LINE 1: ...LECT "relationships".* FROM "relationships" WHERE "relations...
^
: SELECT "relationships".* FROM "relationships" WHERE "relationships"."user_id" = $1
I realised this error might come from my User model
class User < ActiveRecord::Base
has_many :relationships
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
end
relationship.rb
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
end
I don't have any idea at all why at fist the error said the relationship.user_id does not exist. How can I fix this? Thanks in advance.
This is telling you that the relationships table underneath the Relationship model does not have a user_id column.
When you use the has_many association in the User model, then you need to have a user_id column in the relationships table per: http://guides.rubyonrails.org/association_basics.html#the-has-many-association
Examine the relationships table in the database to see the structure of the table. It would seem you need a migration to add the user_id column to that table.

Get attribute value from the join in a many-to-many relationship

I have a many-to-many relation between User and "Link".
The join model is called LinkAddress and besides for saving the IDs of the other two models, it has an attribute called address - information it collects at creation.
How can I access the address attribute for a certain link in a request scenario like the following: User.first.links.first.address ?
Models:
class User < ActiveRecord::Base
has_many :link_addresses, dependent: :destroy
has_many :links, through: :link_addresses
accepts_nested_attributes_for :link_addresses, allow_destroy: true
end
class LinkAddress < ActiveRecord::Base
belongs_to :user
belongs_to :link
end
class Link < ActiveRecord::Base
has_many :link_addresses, dependent: :destroy
has_many :users, through: :link_addresses
end
You could access it through User since it's a has_many ... :through relation:
User.first.link_addresses.first.address
Or, if you'd like to go through links then:
User.first.links.first.link_addresses.first.address
SQL Aliases
I had this exact question: Rails Scoping For has_many :through To Access Extra Data
Here's the answer I got:
#Images
has_many :image_messages, :class_name => 'ImageMessage'
has_many :images, -> { select("#{Image.table_name}.*, #{ImageMessage.table_name}.caption AS caption") }, :class_name => 'Image', :through => :image_messages, dependent: :destroy
This uses SQL Aliases which I found at this RailsCast (at around 6:40 in). It allows us to call #user.image.caption (even though .caption is in the join model)
Your Code
For your query, I'd use this:
class User < ActiveRecord::Base
has_many :link_addresses, dependent: :destroy
has_many :links, -> { select("#{Link.table_name}.*, #{LinkAddress.table_name}.address AS address") }, through: :link_addresses
accepts_nested_attributes_for :link_addresses, allow_destroy: true
end
This will allow you to write #user.links.first.address, and gracefully handles an absence of the address record

STI with has_one through not working

My Models:
Content
RelatedList
RelatedGroupList < RelatedList # STI
ContentListing
on Content, I have
has_many :content_listings # all the below relations are joined using this which has content_id and related_list_id columns
has_one :related_group_list, through: :content_listings, source: 'RelatedList'
has_one :related_people_list, through: :content_listings, source: 'RelatedList'
has_one :related_website_list, through: :content_listings, source: 'RelatedList'
Basically, I want to get 'content.related_group_list' which should get the record for related group list.
However, I get this error:
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :RelatedList in model ContentListing. Try 'has_many :related_group_list, :through => :content_listings, :source => <name>'. Is it one of :content or :related_list?
I checked my ContentListing model, with this line:
belongs_to :related_list
what am I missing in my ContentListing model?
Edit 1:
after I posted this question, I read some other articles on associations and changed the line
has_one :related_group_list, through: :content_listings, source: 'RelatedList'
to
has_one :related_group_list, through: :content_listings, source: :related_list
It now gives me the following error:
ActiveRecord::HasOneThroughCantAssociateThroughCollection: Cannot have a has_one :through association 'Content#related_group_list' where the :through association 'Content#content_listings' is a collection. Specify a has_one or belongs_to association in the :through option instead.
I want my
has_one :related_group_list, through: :content_listings, source: :related_list
automatically fetch me only those related_list whose type is RelatedGroupList and then join through my ContentListing. Is it possible?
Here you can not set has_one relationship 'related_group_list' You can set has_many.
Because Content has many content_listings and each content_listing has one related_list.
It means each content can have many related_group_list not just one.
So if you want to get content.related_group_lists then you can do
In ContentListing model -:
class ContentListing < ActiveRecord::Base
belongs_to :related_list
belongs_to :related_group_list, class_name: 'RelatedList',
foreign_key: 'related_list_id'
belongs_to :content
end
In Content model -:
class Content < ActiveRecord::Base
has_many :content_listings
has_many :related_lists, through: :content_listings
has_many :related_group_lists, through: :content_listings
end

Resources