Querying Parent Model with respect to current user's Child Model - ruby-on-rails

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.

Related

self-joined model Comment that belongs to Posts

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

Rails: why is calling `valid?` only validate some of associated record not all associated records

I have a model Order which is like
# app/models/order.rb
class Order< ApplicationRecord
has_one :detail
has_one :extra
..
end
I have two orders
order1 = Order.first
order1.detail #<OrderDetail:0x00 name: "abc", remark: 'test1'>
order1.extra #<OrderExtra:0x00 email: nil, recipent: nil>
order2 = Order.second
order1.detail #<OrderDetail:0x00 name: "abc", remark: 'test1'>
order1.extra #<OrderExtra:0x00 email: nil, recipent: "xyz">
When I call order1.valid? or order1.save! it will not check OrderExtra validation and returns true. But when I call order2.valid? or order2.save! it checks OrderExtra validation.
order1.save! # true
order2.save! # ActiveRecord Invalid OrderExtra
I want to know how rails checks if they want to check associated validation when call save! and the reason behind that.
Please let me know if any additional requirement needed on this.
use the validates_associated for enforcing associated model validations
class Book < ActiveRecord::Base
has_many :pages
belongs_to :library
validates_associated :pages, :library
end
This validation will not fail if the association hasn’t been assigned. If you want to ensure that the association is both present and guaranteed to be valid, you also need to use validates_presence_of.
class Library < ActiveRecord::Base
has_many :books
validates_presence_of :name
end

Update attributes on has_many through associations and working with the unsaved object

This has something to do with my last quesion about unsaved objects, but now it is more about a specific problem how to use rails.
The models I have are:
class User < ActiveRecord::Base
has_many :project_participations
has_many :projects, through: :project_participations, inverse_of: :users
end
class ProjectParticipation < ActiveRecord::Base
belongs_to :user
belongs_to :project
enum role: { member: 0, manager: 1 }
end
class Project < ActiveRecord::Base
has_many :project_participations
has_many :users, through: :project_participations, inverse_of: :projects
accepts_nested_attributes_for :project_participations
end
With this models, when I create a new project I can do it by a form (fields_for etc) and then I can call update_attributes in the controller. So if I have users in the database already, I can do this:
u = Users.create # save one user in database (so we have at least one saved user)
p = Project.new
# add the user to the project as a manager
# the attributes could come from a form with `.fields_for :project_participations`
p.update_attributes(project_participations_attributes: [{user_id: u.id, role: 1}])
=> true
This works fine until I want to do something with the users of a project. For example I want add a validations that there must be at least one user for a project:
class Project < ActiveRecord::Base
...
validates :users, presence: true # there must be at least one user in a project
...
end
This now gives:
u = Users.create
p = Project.new
p.update_attributes(project_participations_attributes: [{user_id: u.id, role: 1}])
=> false
p.errors
=> #<ActiveModel::Errors:... #base=#<Project id: nil>, #messages={:users=>["can't be blank"]}>
p.users
=> #<ActiveRecord::Associations::CollectionProxy []>
p.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: 1, project_id: nil>]>
So on unsaved projects the .users is empty. This already bugs me (see my last quesion about unsaved objects). But in this case I can of course now work around this by doing validates :project_participations, presence: true instead of validates :users, presence: true and it should mean the same.
But this would mean I should never use the .users method (in any helper, model, view, ...) unless I am totally sure that I work with a saved object. Which in fact renders the .users method unusable (like it does with the validation of user`s presence).
If I call update_attributes like this, the validations works and it saves:
p.update_attributes(users: [u])
With this it creates the project_participation by itself so p.users works as expected. But here I cannot set any data like role for project_participation of that user.
So my questions are: Can I make the .users method work whether or not the object is saved (I think not)? But then, how can I add users to a unsaved project as a manager/member and work with the unsaved project?
I hope my problem is clear.
I think I understand you question, and you're correct in assuming that you cannot use the .users method whether or not the project model is saved. The reason for this is that in defining an association in Project (ie. has_many :users, through: :project_participations, inverse_of: :projects) you're telling rails to read the users attribute out of the database via the project_participations join table and when you haven't saved the project you have nothing to read out of the database.
In order to add a User to your project in a particular role you will need to create a new ProjectParticipation model which you will then associate to your project. If you then remove the users association and write your own users method you should be able to access your collection of users regardless of whether or not the project has been saved.
class Project < ActiveRecord::Base
has_many :project_participations
...
def users
project_participations.collect { |pp| pp.user }
end
end
Then something like:
u = Users.create
p = Project.new
pp = ProjectParticipation.new({user: u, project: p, role: 1})
p.project_participations << pp
p.users
Hopefully that helps.

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.

Multiple has_many_polymorphs in one model

I'm trying to define multiple polymorphic relations (has_many_polymorphs plugin) from a single parent to same children.
Note has many viewers
Note has many editors
Viewers could be either Users or Groups
Editors could be either Users or Groups
Permission is the association model with note_id, viewer_id, viewer_type, editor_id, editor_type fields
Everything works out as expect as long as I have only one has_many_polymorphs relation defined in Note model
class User < ActiveRecord::Base; end
class Group < ActiveRecord::Base; end
class Note < ActiveRecord::Base
has_many_polymorphs :viewers, :through => :permissions, :from => [:users, :groups]
end
class Permission < ActiveRecord::Base
belongs_to :note
belongs_to :viewer, :polymorphic => true
end
Note.first.viewers << User.first # => [#<User id: 1, ....>]
Note.first.viewers << Group.first # => [#<User id: 1, ....>, #<Group ...>]
Note.first.viewers.first # => #<User ....>
Note.first.viewers.second # => #<Group ....>
Now, problems start to appear when I add the second relation
class Note < ActiveRecord::Base
has_many_polymorphs :viewers, :through => :permissions, :from => [:users, :groups]
has_many_polymorphs :editors, :through => :permissions, :from => [:users, :groups]
end
class Permission < ActiveRecord::Base
belongs_to :note
belongs_to :viewer, :polymorphic => true
belongs_to :editor, :polymorphic => true
end
Note.first.viewers << User.first # => [#<User id: ....>]
# >>>>>>>>
Note.first.editors << User.first
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.constantize
... vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/base.rb:18:in `instantiate'
I've tried refining the definition of has_many_polymorphs but it didn't work. Not even with an STI model for ViewPermission < Permission, and EditPermission < Permission.
Any thoughts / workarounds / issue pointers are appreciated.
Rails 2.3.0
Dont you need to add
has_many :permissions
to your Note.
FYI. I used has_many_polymorphs once but then dropped it, it wasn't working as expected.
Can you post the schema that you are using for Permission? My guess is the root of the problem lies there, you need to have multiple type, id pairs in the schema since you have two different belongs_to in the definition.
Edit:
I see you have posted the question on github as well. Not sure if you tried using the Double sided polymorphism. You probably have... like I said, I was not impressed by this plugin, as it brought in some instability when I used it.
== Double-sided polymorphism
Double-sided relationships are defined on the join model:
class Devouring < ActiveRecord::Base
belongs_to :guest, :polymorphic => true
belongs_to :eaten, :polymorphic => true
acts_as_double_polymorphic_join(
:guests =>[:dogs, :cats],
:eatens => [:cats, :birds]
)
end
Now, dogs and cats can eat birds and cats. Birds can't eat anything (they aren't <tt>guests</tt>) and dogs can't be
eaten by anything (since they aren't <tt>eatens</tt>). The keys stand for what the models are, not what they do.
#Tamer
I was getting the same error. The problem was that has_many_polymorphs creates the record in the join table using mass association and was failing. I added attr_accessible :note_id, :editor_id, and :editor_type to my Permission class and it worked afterwards. (Note: I substituted your model names for mine.)
I haven't looked too much into it, but I'd be curious if there's a way to alter this behavior. I'm fairly new to this framework and letting anything sensitive (like an Order-Payment association) be mass-assigned seems like asking to shoot myself in the foot. Let me know if this fixed your problem, and if you figure anything else out.
Best,
Steve

Resources