self-joined model Comment that belongs to Posts - ruby-on-rails

I have two models. I want them behave like post with tree-structured comments.
Post:
class Post < ApplicationRecord
has_many :comments
end
Comment:
class Comment < ApplicationRecord
belongs_to :post
belongs_to :parent, class_name: 'Comment', optional: true
has_many :children, class_name: 'Comment', foreign_key: 'parent_id'
end
When I create a comment in console through
post = Post.create(title: 'Title', content: 'text')
comment = post.comments.create(content: 'text')
child = comment.children.create(content: 'text')
pp child
this is what I get:
[22] pry(main)> child = comment.children.create(content: 'text')
(0.2ms) begin transaction
(0.2ms) rollback transaction
=> #<Comment:0x00007f16ec59cc20
id: nil,
content: "text",
post_id: nil,
parent_id: 5,
created_at: nil,
updated_at: nil>
I experimented a bit without much success and self-join guides did not help. What code is lacking in my models?
upd.
child is not saved into the database. Error: ["Post must exist"]. But post exists. Post id is nit set when I run comment.children.new(content: 'text') How do I create an association like children belongs_to :post, through: :parents (pseudocode)

You have to mention what post the parent comment belongs to.
comment.children.new(content: "text", post: comment.post)
Or you might also do this in a callback on your model.
in models/comment.rb
before_save :set_post_id
private
def set_post_id
self.post_id = post.id || parent.post.id
end

Related

How do i select an association record that has yet to be saved in Rails?

I have the following models and relationships. I'm building a form and am wanting to initialize terms of the proposal for the form. How can I select a specific ProposalTerm by it's term_type_id to pass on to my fields_for block?
Proposal
class Proposal < ApplicationRecord
after_initialize :add_terms
has_many :terms, class_name: "ProposalTerm", dependent: :destroy
accepts_nested_attributes_for :terms
def add_terms
terms << ProposalTerm.first_or_initialize(type: TermType.signing_bonus)
end
end
ProposalTerm
class ProposalTerm < ApplicationRecord
include DisableInheritance
belongs_to :proposal
belongs_to :type, class_name: "TermType", foreign_key: "term_type_id"
def self.signing_bonus
find_by(type: TermType.signing_bonus)
end
end
My Attempt
>> #proposal.terms
=> #<ActiveRecord::Associations::CollectionProxy [#<ProposalTerm id: nil, season: nil, value: nil, is_guaranteed: false, term_type_id: 2, proposal_id: nil, created_at: nil, updated_at: nil>]>
>> #proposal.terms.where(term_type_id: 2)
=> #<ActiveRecord::AssociationRelation []>
I was able to figure out an answer. I had tried "select" but I was doing it incorrectly.
I had tried the following,
#proposal.terms.select(term_type_id: 2)
but that wasn't returning anything. I then did the following...
#proposal.terms.select { |t| t.term_type_id = 2 }
If you want to return just the first instance use "detect" ...
#proposal.terms.detect { |t| t.term_type_id = 2 } }

How to create association between unsaved records

I created 3 models as below, and used cocoon nested form to create associations between them.
class Unit < ApplicationRecord
has_many :mapping_categories, -> { distinct }, dependent: :destroy, inverse_of: :unit
accepts_nested_attributes_for :mapping_categories,
allow_destroy: true,
reject_if: :all_blank
end
class MappingCategory < ApplicationRecord
belongs_to :unit
has_many :mapping_items, -> { distinct }, dependent: :destroy, inverse_of: :mapping_category
accepts_nested_attributes_for :mapping_items,
allow_destroy: true
end
class MappingItem < ApplicationRecord
belongs_to :mapping_category
has_many :mapping_item_links
has_many :linked_mapping_items, through: :mapping_item_links, dependent: :destroy
end
Each mapping_item can have many other mapping_items through a joint table. In every mapping_item section in Unit form, this association is displayed as a select input.
When creating or updating Unit, there are many mapping_categories tabs in the Unit form, and there are many mapping_items sections in each mapping_category section.
For example, I have Mapping Category A and Mapping Category B. I want to add Mapping Item 1 to Mapping Category A and Mapping Item 2 to Mapping Category B. The question is: How to create the association between Mapping Item 1 and Mapping Item 2, as these two items are not saved yet?
Thanks in advance.
YOU CAN DO IT
You have to write right code
user = User.new(name: 'Jons', email: 'jons#qq.ww')
bank_account = BankAccount.new(number: 'JJ123456', user: user)
bank_account.save
in this way will be saved both raws and user and bank_account
in your case:
unit = Unit.new(mapping_categories: [mapping_category])
mapping_category = MappingCategory.new(mapping_items: [mapping_item])
mapping_item = MappingItem.new
unit.save
and if you wanna use nested_attributes, you just have to build hash with attributes
params = { mapping_categories: [mapping_items: [{.....}]}] }
Unit.create(params)
but you have to figure out with right nesting
From my understanding of your question... You can't. These items don't yet have ids and there for can't be associated with another model.
> contact = Contact.new(full_name: "Steve", email:"example#asdf.com")
=> #<Contact id: nil, full_name: "Steve", email: "example#asdf.com", created_at: nil, updated_at: nil>
> invoice = Invoice.new(contact_id: contact.id, invoice_type: "Something")
=> #<Invoice id: nil, contact_id: nil, invoice_type: "Something" created_at: nil, updated_at: nil>
> invoice.save
=> false

Querying Parent Model with respect to current user's Child Model

Accordingly to devise documentation, related records to current_user is always available, despite what I've read previously. For instance: current_user.comments current_user.profile_images
What bugs me really is this:
Post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :postsettings, inverse_of: :post
accepts_nested_attributes_for :postsettings
User.rb
class User < ApplicationRecord
has_many :posts
has_many :postsettings
Postsettings.rb
class Postsetting < ApplicationRecord
belongs_to :post, required: false
belongs_to :user, required: false
In Posts controller I have this:
#post_delete = current_user.postsettings.includes(:postsettings).where.not
(postsettings: {user_id: current_user.id, delete_post: false})
Which works, but is not producing the desired outcome because I need to query ALL Posts where current_user.POSTSETTINGS.delete_post is true or false.
So I've been at this for a couple of days now, and I managed to come up with this:
#post_delete = Post.includes(current_user.postsettings).where(
postsettings: {user_id: current_user.id, delete_post: false})
This produces an error message which I have not seen before.
ActiveRecord::ConfigurationError in Posts#index
#<Postindex id: 284, read: nil,
created_at: "2017-04-15 11:38:02",
updated_at: "2017-04-15 11:38:02", post_id: 96, user_id: 1,
delete_post: false>
Which indicates as far as I can see that the query finds all that it needs to find. But it won't actually function. Please help me.... I'm dying over here.
Post.joins(:postsettings)
.where(postsettings: { user: current_user } )
.where(postsettings: { delete_post: [true, false] })
.joins creates a INNER JOIN which means that only rows from posts with matches in postsettings will be returned.
The answer given by max is good, for convenience though, you can set your user model as follows. Add this line to your user model.
has_many :posts_with_settings, through: :postsettings, source: :post
Now you should be able to call current_user.posts_with_settings to give you all the posts with postsettings set for current_user. From there you can filter as you wish, e.g,
current_user.posts_with_settings.where(postsettings: {delete_post: true})
For more info on :through option see here.

How to set up this belongs_to association in Rails / ActiveRecord?

I have User and Review models. A review can have an author and a subject, both pointing to a User:
class Review < ApplicationRecord
belongs_to :subject, class_name: 'User', optional: true
belongs_to :author, class_name: 'User', optional: true
end
class CreateReviews < ActiveRecord::Migration[5.0]
def change
create_table :reviews do |t|
t.references :subject
t.references :author
end
end
end
This works fine and now I can assign two separate User objects to the Review object to represent who wrote the review against whom.
The user though, doesn't "know" how many reviews he's associated with either as a subject or the author. I added has_and_belongs_to_many :users on reviews and vice-versa, and though doable, isn't exactly what I want.
How do I set up the associations to be able to do the following:
review.author = some_other_user
review.subject = user2
another_review.author = some_other_user
another_review.subject = user2
user2.a_subject_in.count
#=> 2
user2.a_subject_in
#=> [#<Review>, #<Review>]
some_other_user.an_author_in.count
#=> 2
In other words, how do I see how many times a User has been saved as an author or subject for a model with belongs_to?
IF you want to use has_many association on users side, you need to define two separate has_many relations like
class User < ApplicationRecord
has_many :reviews, foreign_key: :author_id
has_many :subject_reviews, class_name: 'Review', foreign_key: :subject_id
end
Now with this you can simply use
irb(main):033:0> s.reviews
Review Load (0.2ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."author_id" = ? [["author_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Review id: 1, comment: "random", subject_id: 2, author_id: 1, created_at: "2016-07-12 01:16:23", updated_at: "2016-07-12 01:16:23">]>
irb(main):034:0> s.subject_reviews
Review Load (0.2ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."subject_id" = ? [["subject_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy []>
Comment: subject_reviews is not a good name :), change it to your requirements.
I think you're looking for this query:
class User
def referenced_in
# this fetches you all the reviews that a user was referenced
Review.where("reviews.author_id = :user_id OR reviews.subject_id = :user_id", user_id: id).distinct
end
end
User.first.referenced_in #should give you all records a user was referenced

rails basic polymorphism

i'm trying to create a polymorphic relationship between votes can be submitted by users and apply to articles. my code
class Vote < ActiveRecord::Base
attr_accessible :value, :voteable_id, :voteable_type
belongs_to :voteable, :polymorphic => true
end
class User < ActiveRecord::Base
has_many :votes, :as => :voteable
end
class Article < ActiveRecord::Base
has_many :votes, :as => :voteable
end
<Vote id: 1, value: 1, created_at: "2012-07-27 03:13:14", updated_at: "2012-07-27 03:13:14", voteable_id: nil, voteable_type: nil>
From looking at the rails documentation via http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
I feel that my code is set correctly but i'm having a bit of trouble triggering it correctly, ie, how do I actually create a vote object with a properly define relationship to either an article or user?
Is votable_type is string?
Next example should work correctly..
#user.votes.new :value => 1
#user.save
.
I was able to make this work, I was setting the voteable_type attribute incorrectly.

Resources