Rails 4, strong_parameters, parametry tablicowe

Może bardziej zaawansowani szybko połapali się jak wykorzystywać strong_parameters ale nieźle się nagłowiłem zanim doszedłem jak zapisywać parametry tablicowe pochodzące z formularzy więc ku pamięci i dla mniej zaawansowanych (takich jak ja :slight_smile: )

Cały kod w uproszczeniu, tylko tyle by przedstawić problem.

Mam trzy modele

[code=ruby]class Library < ActiveRecord::Base

has_many :articles_libraries, dependent: :delete_all
has_many :articles, through: :articles_libraries
end

class Article < ActiveRecord::Base

has_many :articles_libraries, dependent: :delete_all
has_many :libraries, through: :articles_libraries
end

class ArticlesLibrary < ActiveRecord::Base
belongs_to :article
belongs_to :library
end[/code]
I formularz wprowadzania danych dla artykułu:

[code=ruby]<%= form_for(@article) do |f| %>
<% if @article.errors.any? %>


<%= t(“activerecord.errors.template.header”, count: @article.errors.count) %>


<%= t “activerecord.errors.template.body” %>
      <ul>
        <% @article.errors.full_messages.each do |msg| %>
            <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
<% end %>

<div class="field">
  <%= f.label :title %><br/>
  <%= f.text_field :title %>
</div>
<div class="field">
  <%= f.label :library_ids %><br/>
  <%= f.select :library_ids, Library.all.map{|l| [l.name,l.id]}, {}, {multiple: true} %>
</div>
<div class="field">
  <%= f.label :content %><br/>
  <%= f.text_area :content %>
</div>
<div class="actions">
  <%= f.submit %>
</div>

<% end %>[/code]
W wcześniejszych wersjach RoR wystarczyło w modelu Article dopisać jedną linię:

[code=ruby]class Article < ActiveRecord::Base

has_many :articles_libraries, dependent: :delete_all
has_many :libraries, through: :articles_libraries

attr_accessible :title, :content, :library_ids
end[/code]
Strong_parameters przenosi tą funkcjonalność do kontrolera czyli nie dodajemy attr_accessilble w modelu natomiast w kontrolerze mamy (metoda article_params ma pełnić funkcję analogiczną do attr_accessible):

[code=ruby]class ArticlesController < ApplicationController
(…)
def create
@article = current_user.articles.build(article_params)
(…)
end

def update
respond_to do |format|
if @article.update(article_params)
format.html { redirect_to @article, notice: t(‘articles.flash.update’) }
else
format.html { render action: ‘edit’ }
end
end
end
(…)
private
# Never trust parameters from the scary internet, only allow the white list through.
def article_params
params.require(:article).permit(:title, :content, :library_ids )
end
end[/code]
Niestety to nie działa :frowning: . Możemy sobie w formularzu zmieniać biblioteki, do których ma należeć artykuł a efektów nie widać.

Przyczyną jest to, że strong_parameters obsługuje jedynie dane skalarne a params[:article][:library_ids] jest tablicą. Trzeba zmienić zmienić metodę article_params

class ArticlesController < ApplicationController (...) private # Never trust parameters from the scary internet, only allow the white list through. def article_params params.require(:article).permit(:title, :content, library_ids: [] ) end end
Powodzenia w przechodzeniu na Rails 4.

https://github.com/rails/strong_parameters - jest to opisane prawie na samym początku.

Swoją drogą - chętnie poznam wasze zdanie na temat strong parameters. Rozumiem, że jest to bardzo ważna kwestia bezpieczeństwa, zastanawiam się jednak czy sposób ich definiowania nie jest daleki od optymalnego. Może jednak warto było zrobić coś na kształt Merba (gdzie parametry były brane z argumentów metody - akcji) albo Grape’a (gdzie jest DSL do opisywania akceptowanych i domyślnych parametrów wywołania).

Nie wiem czy jest gorszy od innych możliwych, ale na pewno jest sto razy lepszy niż attr_accessible na poziomie modelu. Biorę dwa :wink: