Как объединить строковые поля в одно поле с разделителем в приложении rails 4?

#ruby-on-rails #postgresql #ruby-on-rails-4 #concatenation

#ruby-на-рельсах #postgresql #ruby-on-rails-4 #конкатенация #ruby-on-rails

Вопрос:

У меня есть несколько флажков в форме, в которых я сохраняю результаты каждого поля в соответствующих временных полях, которые я указал в модели с атрибутом attr_accessor: . Я хочу объединить содержимое этих полей в одно поле с разделителем-запятой и сохранить его в базе данных.

Моя полная форма выглядит примерно так :

 <%= form_for @company do |f| %>
  <% if @company.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@company.errors.count, "error") %> prohibited
      this company from being saved:</h2>
    <ul>
    <% @company.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
  <% end %>


  <p>

    <div class="row">
        <div class='col-sm-6'>
            <div class="form-group">
               <p>
    <%= f.label :name %><br>
    <%= f.text_field :name,class:'form-control' %>
  </p>
<p>
    <%= f.label :grade %><br>

    <%= f.select(:grade, options_for_select([['Dream', 'dream'], ['A  ', 'a  '], ['A ', 'a '],['A', 'a']])) %>
  </p>

  <p>
    <%= f.label :beCutoff %><br>
    <%= f.text_field :beCutoff,class:'form-control' %>


    </p>
    <%= f.label :branchesAllowed %><br>
 <%= f.check_box :coe, {}, "COE", "" %><%= label_tag :COE %><br>

  <%= f.check_box :ece,  {}, "ECE", ""%><%= label_tag :ECE %><br>
  <%= f.check_box  :ice, {}, "ICE", ""%><%= label_tag :ICE %><br>
  <%= f.check_box  :it,  {}, "IT", ""%><%= label_tag :IT %><br>
  <%= f.check_box :mpae, {}, "MPAE", ""%><%= label_tag :MPAE %><br>
  <%= f.check_box  :bt, {}, "BT", ""%><%= label_tag :BT %><br>
  <%= f.check_box  :is, {}, "IS", ""%><%= label_tag :IS %><br>
  <%= f.check_box  :sp,  {}, "SP", ""%><%= label_tag :SP %><br>
  <%= f.check_box  :pc,  {}, "PC", ""%><%= label_tag :PC %><br>
    <p>
    <%= f.label :backsAllowed %><br>
    <%= f.text_field :backsAllowed,class:'form-control' %>
  </p>
<p>
    <%= f.label :details %><br>
    <%= f.text_field :details,class:'form-control' %>
  </p>
  <p>
    <%= f.label :package %><br>
    <%= f.text_field :package,class:'form-control' %>
  </p>
   <p>
    <%= f.label :xiiCutoff %><br>
    <%= f.text_field :xiiCutoff,class:'form-control' %>
  </p>
    <p>
    <%= f.label :xCutoff %><br>
    <%= f.text_field :xCutoff,class:'form-control' %>
  </p>
  <%= f.label :deadline %><br>

                <div class='input-group date' id='datetimepicker1'>

                    <%= f.text_field :deadline ,class:'form-control',:readonly=>true%>
                    <span class="input-group-addon" ><span class="glyphicon glyphicon-calendar"></span>
                    </span>
                </div>
            </div>
        </div>
        <script type="text/javascript">
            $(function () {
                $('#datetimepicker1').datetimepicker({format: 'YYYY-MM-DD HH:mm:ss '});
            });

        </script>

    </div>

  <p>
    <%= f.submit %>
  </p>



<% end %>
  

Я указал поля: coe,: ece,:ice и т. Д. В моей модели как attr_accessor:

 class Company < ActiveRecord::Base

     attr_accessor :coe,:ece,:ice,:it,:mpae,:bt,:is,:pc,:sp


     validate :deadline_on_or_before_now

  def deadline_on_or_before_now
    errors.add(:deadline, "can't be in the past") if
      !deadline.blank? and deadline < (Time.zone.now 19800).to_datetime
  end

    validates :name, presence: true,
                    length: { minimum: 2 };
    validates :grade,:beCutoff,:details,:package, presence: true;
    validates_inclusion_of :beCutoff,:xiiCutoff,:xCutoff, :in=> 0..100,:message=>"Out of Range";

end
  

Мой контроллер выглядит так:

 class CompaniesController < ApplicationController
    def new
        @company = Company.new
    end

    def edit

        @company = Company.find(params[:id])
    end

    def update
        @company = Company.find(params[:id])

        if @company.update(company_params)
            redirect_to @company
        else
            render 'edit'
        end
    end

    def create

        @company = Company.new(company_params)

        if @company.save
            redirect_to @company
        else
            render 'new'
        end
    end

    def index
        @companies = Company.all
    end
    def destroy
        @company = Company.find(params[:id])
        @company.destroy

        redirect_to companies_path
    end

    def show
        @company = Company.find(params[:id])

    end


    private
        def company_params

            params.require(:company).permit(:name, :beCutoff,:grade,:xiiCutoff,:xCutoff,:backsAllowed,:details,:package,:deadline,:branchesAllowed,:coe,:ece,:ice,:it,:mpae,:bt,:is,:sp,:pc) if params[:company]
        end
end
  

Я хочу объединить поля:coe,:ece,:ice и т. Д. И сохранить их в поле:branchesAllowed(строковое поле) с разделителем commma и возможностью разбивать это поле всякий раз, когда мне нужно использовать их отдельно.
Я использую postgresql с rails 4.1.

Комментарии:

1. Я не хочу снова возиться с кодом, поэтому, конечно, метод, который позволяет мне выполнять одноразовый метод изменения, поможет. Но я хотел бы знать оба метода.

Ответ №1:

Если бы я хотел сделать это таким образом, я бы выбрал хэш, хранящийся в строковом поле.

 before_save :create_string

def create_string
    fields = {}
    fields[:coe]=coe
    fields[:ece]=ece
    # etcetera
    branchesAllowed = fields.to_s # hash to string and then store the string
end
  

А затем для извлечения значений (скажем, в контроллере или представлении):

 values = eval(@company.branchesAllowed) # reversely, evaluate string to hash
@coe = values[:coe]
@ece = values[:ece]
# etcetera
  

Комментарии:

1. Я попробовал @vapire способ решения и сработал для меня, и ваше решение похоже на это. Спасибо за вашу помощь.

2. @Pranav, нет проблем 🙂

Ответ №2:

Самый быстрый и грязный способ — просто объединить поля в методе вашей модели и вызвать этот метод в before_save обратном вызове:

 class Company < ActiveRecord::Base
  # . . .

  before_save :combine_branches   # or choose other callback type respectively
  def combine_branches
    self.branchesAllowed = ""
    self.branchesAllowed  = "COE" if coe
    self.branchesAllowed  = "ECE" if eve
    self.branchesAllowed  = "ICE" if ice
    # … and so on … 
  end
end
  

Теперь при вызове save или update в вашем контроллере запускается обратный вызов и устанавливается branchesAllowed поле перед записью в базу данных.

Это, однако, на самом деле не является сухим и не легко расширяемым. Если вы хотите добавить еще одну ветку, вам придется отредактировать как минимум 3 разных места.

Сухое (er) решение может быть примерно следующим:

 class Company < ActiveRecord::Base
  # keep a constant of possible branches tied to the model
  POSSIBLE_BRANCHES = [:coe, :ece, :ice, :it, :mpae, :bt, :is, :pc, :sp]

  # add the branches as virtual attributes (only assign methods)
  attr_writer *POSSIBLE_BRANCHES

  # add virtual attributes reader methods
  # --> define every branch as method, that looks if the value is currenly
  # stored in the branchesAllowed string and return true or false
  POSSIBLE_BRANCHES.each do |pb|
    define_method(pb) do
      self.banchesAllowed.split(',').include?(pb.to_s.upcase)
    end
  end

  # add the callback to combine the branches
  before_save :combine_branches
  def combine_branches
    self.banchesAllowed = POSSIBLE_BRANCHES.select { |pb| !!self.send(pb) }.map(amp;:upcase).join(',')
  end
end
  

Тогда вы также можете использовать их в своем представлении:

 <%= f.label :branchesAllowed %><br>
<% Company::POSSIBLE_BRANCHES.each do |pb| %>
  <%= f.check_box pb, {}, pb.to_s.upcase, "" %><%= label_tag pb.to_s.upcase %><br>
<% end %>
  

Итак, если вы хотите добавить еще одну ветку позже, вы просто добавите еще один символ к константе массива, и все готово. Надеюсь, это поможет.

Обновить

Поскольку boolean значения в флажках '1' по умолчанию используют и '0' (как строки), я не совсем уверен, должны ли методы чтения виртуальных атрибутов возвращать это вместо логического значения.

Комментарии:

1. Я использовал оба способа, и они работали. Просто применил некоторые условия if, и все было идеально. Спасибо

2. Было бы очень полезно, если бы вы указали какой-либо способ, которым я получаю параметры, отмеченные или снятые при редактировании сообщения.

Ответ №3:

Я бы подошел к этому немного по-другому. Предполагая, что это все атрибуты a, Company которые вы хотите иметь возможность устанавливать, вы можете использовать одно целое поле для хранения этих данных, создав массив типов атрибутов и используя индекс каждого атрибута в этом массиве как двоичное значение, которое вы можете использовать для определения, является ли атрибутустановлено или нет.

Посмотрите этот Railscast для полного объяснения, поскольку Райан делает это намного лучше, чем я: http://railscasts.com/episodes/189-embedded-association

Комментарии:

1. Спасибо @Jon, я ценю вашу помощь. Я также пробовал массивы ранее в своем приложении, но где-то обнаружил, что массивы не являются хорошими вещами в rails. Также я хочу быстро исправить это приложение.

2. Нет ничего плохого в использовании массива в Rails. Кроме того, вы должны предпочесть правильное исправление быстрому исправлению.