问题
I have models in my Rails 5 app for User, Proposal and Potential.
Users create Proposals which they themselves and others can then create comments on.
The associations between models are:
User
has_many :proposals, dependent: :destroy
has_many :potentials
Proposal
belongs_to :user
has_many :potentials, inverse_of: :proposal
accepts_nested_attributes_for :potentials, reject_if: :all_blank, allow_destroy: true
Potential
belongs_to :proposal, inverse_of: :potentials
belongs_to :user
In my routes file, I have two resources for potentials. I'm not sure if I've gone off piste with this bit- I cant find an example of how to do this otherwise. I have both:
resources :potentials
as well as:
resources :proposals do
resources :potentials
The reason I have done this is so that when the user is creating a proposal, they can use a nested fields form to add potential attributes. When another user sees that proposal, they get a new form to add a :potential set of attributes (they don't do it via the proposal form).
In my potentials view folder, I have views for new which renders form for as well as potential_fields_for which is incorporated in my proposals form (only the proposal creator can use the nested fields).
The new/render form has:
<%= simple_form_for [ @proposal, @potential ] do |f| %>
f
The proposal form has:
<%= f.simple_fields_for :potentials do |f| %>
<%= f.error_notification %>
<%= render 'potentials/potential_fields', f: f %>
<% end %>
<%= link_to_add_association 'Add another novel aspect', f, :potentials, partial: 'potentials/potential_fields' %>
</div>
In my proposals controller, I'm trying to find a way to exclude 3rd party created :potentials from the fields displayed in the proposal form.
def edit
@proposal.potentials_build unless @proposal.potentials || @proposal.potential.user_id != current_user.id
I don't want the proposal creator to be able to edit those fields from the proposal form but even if I don't touch them, the user id on the 3rd party potential gets updated to the proposal creator id when I update the proposal form (without updating those specific 3rd party potential attributes).
I tried to change the edit action in the proposals controller be excluding potentials created by a user that is not the current user id. Only the proposal creator can edit the proposal, so I expect that this will exclude instances of proposal.potential that have a user id other than the proposal.user_id.
That doesnt work.
Is there a way that I can limit the proposal#edit action to only those potential instances that are not created by the proposal creator?
TARYN'S SUGGESTION
I tried to adopt Taryn's thoughts about this.
In my proposal.rb, I made 2 scopes:
scope :owner_potentials, ->{ where(user_id: potential.user_id ) }
scope :third_party_potentials, ->{ where(user_id: != potential.user_id) }
I am not confident that this is the correct way to write a scope. Although I cant find a reference to how to write them, on previous occasions when I have tried to learn how to compose them, I have received advice to set them out in this format:
scope :owner_potentials, ->(user){ where(user_id: potential.user_id ) }
scope :third_party_potentials, ->(user){ where(user_id: != potential.user_id) }
I tried this way as well, but I get the same error as I do if I don't include "(user)". I don't understand what that inclusion does or should do.
In my proposal controller, edit action, I have then tried each of the following:
# @proposal.potentials_build unless @proposal.potentials || @proposal.potential.user_id != current_user.id
# @proposal.owner_potentials.build unless @proposal.owner_potentials
#@potentials = @proposal.owner_potentials || @proposal.owner_potentials.build
The first one was my original attempt. It didnt work and prompted me to write this post.
The second one is the way I think it should be written.
The third is just incorporating Taryn's idea directly to see if that's how it should be written (although I think that was more of general way of describing what to do).
None of these work. In the case of the 2nd version of this attempt, I get an error that says:
NoMethodError at /proposals/17/edit
undefined method `owner_potentials' for #<Proposal:0x007f84f6ca1700>
I think the reason why this isnt working is that the scope is run on the class as a table, not the specific instance to be edited. I think this post explains that.
Proposals have many potentials, so the idea is to check all of the potentials belonging to the specific proposal to see whether any of those potentials have the user id that is the same as the user id on the proposal instance. How can I do that?
I cant write:
Proposal.owner_potentials.build unless Proposal.owner_potentials
in the proposal controller edit action because the set_proposal method is picking out the correct proposal for edit to apply on.
Can I use a scope in a controller edit action where there is a has_many relationship that is being tested by the scope?
NEXT ATTEMPT
My next thought is to try to define the scopes in the Potential model so that the scope can run on the class.
I tried:
scope :owner_potentials, ->{ where('user_id == potential.proposal.user_id' ) }
scope :third_party_potentials, ->{ where('user_id != potential.proposal.user_id') }
I spent hours on codementor trying to learn scopes and my takeaway from that session is the syntax i used above is incorrect - but the way I was shown to write them (which is at the top of the post, gives an error with the undefined variable). I don't know how to figure out how to learn to write a scope.
Anyway- next I tried changing the edit action in my Proposal controller to:
@proposal.potentials.owner_potentials.build unless @proposal.potentials.owner_potentials
Now, I get no errors when I save this and try it, but when I try to edit the proposal, I can still edit potentials that have been created by third parties. I don't understand whether Im not writing the scope effectively, or if this solution isnt going to work for another reason.
回答1:
I would consider adding a custom scoped association eg owners_potentials
/third_party_potentials
that limits potentials to those created by the owner/ by somebody other than the owner. You can then use these scopes whenever you need them.
eg
has_many :potentials, inverse_of: :proposal
has_many :owner_potentials, ->{ where("potentials.user_id = proposals.creator_id") }
has_many :third_party_potentials, ->{ where("potentials.user_id != proposals.creator_id") }
Then you could do something like:
def edit
@potentials = @proposal.owner_potentials || @proposal.owner_potentials.build
Then in the form, be specific about using the one you've specified:
<%= f.simple_fields_for @potentials do |f| %>
Note: code has not been tested or sanity-checked, it's here to give an idea of the kind of thing you can do - getting it to actually work is left as an exercise for the reader ;)
来源:https://stackoverflow.com/questions/41755032/rails-5-exclude-specific-instances-from-edit-path-on-parent-controller