-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Define a context for a nested association #1554
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
@willcosgrove Thanks for the issue!
could you maybe give some more complete code examples? I'm not sure I understand the data model. If you can sketch out the model classes and serializer classes that would be helpful. Are you just asking if you can |
Yeah definitely. So say I have these serializers: class BallotSerializer < ActiveModel::Serializer
attributes :id, :name, :owner_id, :created_at, :updated_at
has_many :candidates
class CandidateSerializer < ActiveModel::Serializer
attributes :id, :name, :vote_count
def vote_count
# current_ballot is what I want to be able to access, but I'm at a loss for how
object.votes.where(ballot_id: current_ballot.id).count
end
end
end Does that help explain the problem? From within the |
@willcosgrove there is something that is not clear here. Does a
def vote_count
object.votes.where(ballot_id: object.ballot.id).count
end
@bf4 Another solution here would be to have access to the parent serializer within an association. The only issue I see is that it would only work (nicely) for serializers that would not be serialized outside of the scope of their parent serializer. If the association use a standalone serializer (not embedded within a parent serializer), an class CandidateSerializer < ActiveModel::Serializer
attributes :id, :name
attribute :vote_count, if: :has_parent? do
object.votes.where(ballot_id: parent.object.id).count
end
def has_parent?
!parent.nil?
end
end
class BallotSerializer < ActiveModel::Serializer
attributes :id, :name, :owner_id, :created_at, :updated_at
has_many :candidates
end |
@groyoh Wow, thanks for all your help! I'll try to respond to each thing in order.
It does not, because a candidate can have multiple ballots. So you can't call
This sounds like my best bet currently. I don't really like that it has to be specified in the controller though. I would prefer to specify it inside the serializer, when I define the relationship: has_many :candidates, ballot: object The one glaring problem there is that I don't have access to the Having to define it at the controller level seems confusing render json: @ballot, ballot: @ballot My current work-around is to change the
This would be my preferred way of dealing with it. Thanks again everyone for all your help! I've gotten everything I need from this issue, but I'll leave it open in case there is interest in further discussing the |
@willcosgrove thanks for the explanation. It's nice to have such a nice issue description and details.
From your example, I also figured out that it would also be possible to add a new method to the association DSL in order to pass some arbitrary options like so: has_many :candidates do
instance_options ballot: object
end @bf4 @willcosgrove how do you feel about such solution? I think that this is something that might actually add some values to the association without messing up with the serializer class itself. |
@groyoh that looks great to me! |
I think the problem is having a recursive dependency. How could you count ballot.candidates.map {|c| c.votes(ballot) } Right? On Sat, Mar 5, 2016 at 12:22 PM Will Cosgrove [email protected]
|
@willcosgrove I believe you can now do what you want now that #1633 has been merged |
Oh sweet, I will check it out. I'll go ahead and close this issue then. Thanks for your help! |
Hey, sorry to reopen this issue. I've gotten a chance to play around with the stuff added in #1633 and it looks great, but I can't find a way to set the instance options of the serializer. I can set the reflection options, but those don't get copied over to the instance options of the serializer. def serializer_options(subject, parent_serializer_options, reflection_options)
serializer = reflection_options.fetch(:serializer, nil)
serializer_options = parent_serializer_options.except(:serializer)
serializer_options[:serializer] = serializer if serializer
serializer_options[:serializer_context_class] = subject.class
serializer_options
end Source: So that means when I'm defining a reflection: has_many :candidates do |serializer|
# Would love to do this but cannot, instance_options is not an available method
# instance_options ballot: serializer.object
# Can do this, but it doesn't do anything helpful to me, currently
self.options = {ballot: serializer.object}
end Hopefully I haven't missed something simple that would let me do this 😁 . Let me know if I can provide further clarification on what I'm looking for. Thanks! |
@bf4 this also isn't working for me. I can do the following: has_many :assessments
def assessments
object.assessments.where(vendor_id: instance_options[:vendor_id])
end but when I try to simplify and do: has_many :assessments do |object|
object.assessments.where(vendor_id: instance_options[:vendor_id])
end I get |
There no yielded object so the blockparam is nil. Aredocs wrong somewhere?
B mobile phone
… On Jan 27, 2017, at 12:06 PM, Stefan Wrobel ***@***.***> wrote:
@bf4 this also isn't working for me. I can do the following:
has_many :assessments
def assessments
object.assessments.where(vendor_id: instance_options[:vendor_id])
end
but when I try to simplify and do:
has_many :assessments do |object|
object.assessments.where(vendor_id: instance_options[:vendor_id])
end
I get undefined method 'assessments' for #<VendorProjectSerializer>
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
I don't understand your question |
You wrote
has_many :assessments do |object|
But should have
has_many :assessments do
And I asked if you wrote the wrong way, w block param, due to docs
B mobile phone
… On Jan 27, 2017, at 6:38 PM, Stefan Wrobel ***@***.***> wrote:
I don't understand your question
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
I suppose upon re-reading the docs, they are clear that the block param is the serializer itself, not the object. When I try has_many :assessments do
object.assessments.where(vendor_id: instance_options[:vendor_id])
end I get |
@swrobel so, we can add that as a private method in the Reflection class, or right now you can add the 'serializer' block param call |
Came across this quirk myself today too. Would be nice to look at a permanent solution to get |
@willcosgrove has_many :candidates do |serializer|
# Set instance option for parent serializer, the option will be passed to the child serializer
serializer.send(:instance_options)[:ballot] = serializer.object
# Find the records for the association
serializer.object.ballots
end It would be usable addition though if instance options could be set for only the nested serializers. |
Not sure if this is any better but I ended up doing the following
|
Any update on this? BWT If you don't want to mutate original collection, just return has_many :candidates do |serializer|
# Set instance option for parent serializer, the option will be passed to the child serializer
serializer.send(:instance_options)[:ballot] = serializer.object
:nil
end |
This is very neat. why |
Expected behavior vs actual behavior
From the serializer of an association, I would like to access the object of the parent serializer.
Steps to reproduce
Say we have three models,
Ballot
,Candidate
, andVote
.Candidate
s andBallot
s have a many to many relationship.Vote
s belong to aBallot
and aCandidate
. I'm writing aBallot
serializer thathas_many :candidates
. I'm then making use of the subclassed serializer to have a customCandidateSerializer
. Inside theCandidateSerializer
I want an attribute that represents the number of votes for thatCandidate
on thatBallot
. But to write that method, I need to know which ballot this is being serialized for. Is this possible somehow?I have managed to hack it by removing the association on the
BallotSerializer
and just defining acandidates
attribute that returns an array of hashes, and build thecandidate
that way. But having the child serializer seems a lot cleaner. Is there anyway for me to pass that context of which ballot I'm serializing this candidate for?Environment
ActiveModelSerializers Version (commit ref if not on tag): a6c6979
Output of
ruby -e "puts RUBY_DESCRIPTION"
: ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]OS Type & Version: OS X 10.11.3
Integrated application and version (e.g., Rails, Grape, etc): Rails 5.0.0.beta2
Additonal helpful information
I am currently using the default
attributes
adapter.Let me know if there's anything I can post to make the above situation more clear. Thanks for your help!
The text was updated successfully, but these errors were encountered: