Rails - Strong parameters with empty arrays

后端 未结 5 1728
星月不相逢
星月不相逢 2021-02-01 13:37

I\'m sending an array of association ids, say foo_ids to my controller. To permit an array of values, I use:

params.permit(foo_ids: [])
相关标签:
5条回答
  • 2021-02-01 14:36

    The temporary solution I've come down to is:

    params[:foo_ids] ||= [] if params.has_key?(:foo_ids)
    params.permit(foo_ids: [])
    

    Here, foo_ids is set to an empty array only if is passed. If it is not passed in the request, it is ignored.

    I'm still hoping to find a better solution to this, as this sort of thing will be quite common in the project I'm working on - please do suggest better ideas if you have any.

    0 讨论(0)
  • 2021-02-01 14:37

    This solution won't work for all cases:

    params.require(:photo).permit(:tags, tags: [])
    

    For example, if you are using MongoDB and you have a tag_ids array, which stores the ids in a has_many collection, your tag_ids attribute MUST be an array if you specify "type: Array" for the attribute in your model. Consequently, it won't work to send tag_ids with a nil value even if you do this:

     params.require(:photo).permit(:tag_ids, tag_ids: [])
    

    Mongoid, the official Ruby adapter for MongoDB, will complain the value of tag_ids must be an array.

    The solution is you can indeed send an empty array via your form! And it doesn't need to be a json request. You can simply use remote: true on your form and send it via type: :js. How to do it? Simple. Just add a hidden input in your form and set its value to an empty string:

    <%= form_for @user, remote: true, html: { class: 'form' } do |f| %>
      <%= select_tag("#{f.object_name}[tag_ids][]", options_for_select(Tag.all.collect {|t| [t.name, c.id]}, selected: f.object.tag_ids), { class: 'form-control', multiple: 'multiple' }) %>
      <%= hidden_field_tag "#{f.object_name}[tag_ids][]", '' %>
      <%= f.submit class: 'btn ink-reaction btn-raised btn-primary' %>
    <% end %>
    

    This here is the key:

    <%= hidden_field_tag "#{f.object_name}[tag_ids][]", '' %>
    

    Your attribute will be stored as an empty array in your database. Note I only tested this with Mongoid, but I assume it carries the same functionality in ActiveRecord.

    0 讨论(0)
  • 2021-02-01 14:38

    This is quite late, but I just had this problem myself. I solved it by including both the scalar version and array version in the permit statement, like so:

    params.require(:photo).permit(:tags, tags: [])
    

    FYI - it has to have both in the same permit statement - if you chain them it'll get thrown out for some reason.

    EDIT: I just noticed that an empty array submitted via this method will be turned into nil - I've now got a bunch of fields that should be empty arrays that are nil. So the solution I posted doesn't actually work for me.

    EDIT the second: Thought I had already added this, but this problem is related to Rails performing deep_munge on params hashes. This comment explains how to fix it: https://stackoverflow.com/a/25428800/130592

    0 讨论(0)
  • 2021-02-01 14:38

    I ran into the same issue and found a similar solution as Donato, albeit while constructing a multipart FormData in JS. The trick was to put an empty string in the array.

    const formData = new FormData()
    formData.append('dish[tag_ids][]', '')
    

    On the controller side, params arrives with "dish"=>{"tag_ids"=>[""]}, which dish.update interprets as "remove all tags".

    0 讨论(0)
  • 2021-02-01 14:40

    I had the same problem recently, but none of the answers here worked for me. This is my solution. If you have javascript handling HTTP requests, this may work for you, too.

    In your client side:

    if (photo.tags.length === 0){
      photo.tags = ["null"]
    }
    

    And on your PhotosController

    def photo_params
      p = params.require(:photo).permit(tags: [])
      p["tags"].reject! { |tag| tag == "null" }
      p
    end
    
    0 讨论(0)
提交回复
热议问题