RoRでDBテーブルのカラムと一致しない項目がある入力フォームを実現する その5
実は、これまでフォーム専用クラスや特異メソッドを使ってフォーム専用の変数/属性/フィールドを用意してきたが、モデルにそれのアクセサー、必要があればそのバリデーションを書くだけで機能する。しかも、調べ切ったわけではないが、何の副作用もない*1。
例えば、scaffoldでschoolというモデルを作り、正式学校名official_nameという属性をもっていたとして、国や自治体の名前と「立」が後置膠着する管理団体名と学校名を分けて入力するフォームにしたとすると、次のようになる:
# モデル class School < ApplicationRecord validates :official_name, presence: true attr_accessor :administration, :name # このように加えるだけ validates :administration, presence: true validates :name, presence: true def self.get_official_name_from(params) params["administration"].to_s + params["name"].to_s end end
# ビュー <%= form_for(school) do |f| %> <% if school.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(school.errors.count, "error") %> prohibited this school from being saved:</h2> <ul> <% school.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%#= f.label :official_name %> <%#= f.text_field :official_name %> </div> <div class="field"> <%# このように何の問題もなく使える %> <%= f.label :administration %> <%= f.text_field :administration %> </div> <div class="field"> <%# このように何の問題もなく使える %> <%= f.label :name %> <%= f.text_field :name %> </div> <div class="actions"> <%= f.submit %> </div> <% end %>
# コントローラー class SchoolsController < ApplicationController # before_action :set_school, only: [:show, :edit, :update, :destroy] before_action :set_school, only: [:show, :destroy] # GET /schools # GET /schools.json def index @schools = School.all end # GET /schools/1 # GET /schools/1.json def show end # GET /schools/new def new @school = School.new end # GET /schools/1/edit def edit @school = School.find(params[:id]) @school.administration, @school.name = @school.official_name.to_s.split('立') @school.administration += '立' end # POST /schools # POST /schools.json def create # @school = School.new(school_params) @school = school_params.blank? ? School.new : School.new(school_params) @school.administration = params[:school][:administration] @school.name = params[:school][:name] respond_to do |format| if @school.save format.html { redirect_to @school, notice: 'School was successfully created.' } format.json { render :show, status: :created, location: @school } else format.html { render :new } format.json { render json: @school.errors, status: :unprocessable_entity } end end end # PATCH/PUT /schools/1 # PATCH/PUT /schools/1.json def update @school = School.find(params[:id]) @school.administration = params[:school][:administration] @school.name = params[:school][:name] respond_to do |format| if @school.update(school_params) format.html { redirect_to @school, notice: 'School was successfully updated.' } format.json { render :show, status: :ok, location: @school } else format.html { render :edit } format.json { render json: @school.errors, status: :unprocessable_entity } end end end # DELETE /schools/1 # DELETE /schools/1.json def destroy @school.destroy respond_to do |format| format.html { redirect_to schools_url, notice: 'School was successfully destroyed.' } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_school @school = School.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def school_params # params.require(:school).permit(:official_name) {"official_name" => School.get_official_name_from( params.require(:school).permit(:administration, :name) )} end end
# ja.yml ja: activerecord: models: school: "学校" attributes: school: official_name: "正式名称" administration: "管理区分" name: "学校名" errors: format: "%{attribute}%{message}" messages: blank: を入力してください
例によってscaffoldで生成したところはコメントアウトしている。また、new/create/edit/updateでフォーム用変数に情報を移すなどの処理をリファクタリングせずに態と際立たせている。
参考:
nextat.co.jp
*1:信用するな。モデルをコントローラー以外で使う場合、例えばERDなどの作成などには支障があるかもしれれない。