Рельсы 3 модели подклассов за рулем меня БЕЗУМНЫ!Пожалуйста, помогите с исправлением хэшей URL / params - PullRequest
1 голос
/ 24 мая 2011

У меня проблема с разделением на подклассы моделей Rail.Предположим, у вас есть модель User и несколько ее подклассов (пользовательских типов), в которых хранятся определенные методы и ассоциации, например: Director, Admin, Trainee, Instructor и т. Д. Это просто "наследование одной таблицы ".Проблема двоякая.

  1. пути / URL часто приводят к сбоям или странным вещам, когда вы передаете подкласс, а не базовый класс.Вот пример:

    <% if user.enabled? %>
      <%= link_to 'Disable', disable_user_path(user) %>
    <% else %>
      <%= link_to 'Enable', enable_user_path(user) %>
    <% end %>
    

    Если вы передадите модель User, она работает просто отлично.Но если вы передаете подкласс, такой как Admin, он выдает это исключение:

    No route matches {:controller=>"users", :action=>"disable", :id=>#<Admin id: 1, first_name: "Ken", ..., created_at: "2011-05-23 21:01:35", updated_at: "2011-05-23 21:04:28">}
    

    Очевидно, что это ведет себя неправильно.Как мы можем заставить рельсы использовать базовый класс все время?

  2. Еще более беспокоят формы (я использую simple_form).Допустим, у вас есть форма /profile.Вы хотите, чтобы все User подклассы имели равный доступ к нему, и вы не хотите иметь дело с их подклассами в особых случаях;он должен быть на 100% общим.

    По какой-то причине, если пользователь является Admin, он отправит хэш параметров как

    params[:admin]
    

    Еще хуже, если вы просматриваете источникформы, на самом деле он говорит user[first_name] вместо admin[first_name], так что что-то определенно пошло!Переменная экземпляра тоже @user, поэтому я не понимаю, почему она должна публиковать в params[:admin].

Вот код вида формы для (2):

<%= simple_form_for(@user, :url => profiles_path, :method => :put, :html => {:multipart => true}) do |f| %>
  <%= f.error_notification %>

  <div class="form">
    <fieldset>
      <legend>Personal Information</legend>
      <%= f.input :first_name %>
      <%= f.input :last_name %>
    </fieldset>

    <fieldset>
      <legend>Credentials</legend>
      <%= f.input :email %>
    </fieldset>

    <fieldset>
      <legend>Preferences</legend>
      <%= f.input :receive_email_notifications %>
      <%= f.input :receive_newsletters %>
      <%= f.input :allow_private_messages %>
    </fieldset>

    <fieldset>
      <legend>Avatar</legend>
      <p>
          Select an image from your computer to use as your avatar. You will be given the oppurtunity
          to crop this image further after your image has been uploaded.
      </p>
      <%= f.input :avatar, :as => :file, :label => "Select File" %>
      <%= f.hidden_field :avatar_cache %>
    </fieldset>

    <div class="actions">
      <%= f.button :submit, :value => "Update Profile" %>
      <%= link_to 'Cancel', profiles_path %>
    </div>
  </div>

<% end %>

Вот действия контроллера, чтобы сначала визуализировать представление, и когда я отправляю форму:

  def edit
    @user = User.find(current_user.id)
  end

  def update
    @user = User.find(current_user.id)

    if @user.update_attributes(params[:user]) # <-- this bombs for Admin subclass
      if params[:user][:avatar] # <-- this would bomb also.
        redirect_to(crop_avatar_profiles_path)
      else
        redirect_to(profiles_path, :notice => 'Your profile was successfully updated.')
      end
    else
      render :action => "edit"
    end
  end

  def crop_avatar
    @user = User.find(current_user.id)
  end

  def update_avatar
    @user = User.find(current_user.id)
    @user.crop(params[:x].to_i, params[:y].to_i, params[:h].to_i, params[:w].to_i)

    redirect_to(profiles_path, :notice => 'Your profile and avatar was successfully updated.')
  end

Помимо придумывания некоторых довольно не элегантных решений (особенно проблем с формами), яв недоумении относительно того, как я могу их исправить.Должен быть лучший способ справиться с этими ситуациями.

Ответы [ 2 ]

7 голосов
/ 12 сентября 2012
= simple_form_for @fruit, :as => :fruit do |form|

@fruit может быть любым классом (оранжевый, яблочный, грушевый), и он все равно будет префиксировать входные данные «фруктами», таким образом, каждый раз получая params[:fruit].

4 голосов
/ 24 мая 2011

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

В вашей пользовательской модели:

    #return an array [User, User, ...], instead of [Admin, Director, Director, Instructor, ...]
    def self.all_without_typecast
      self.all.collect! do |u|
          u.becomes(User)
      end
    end

    #makes subclasses of User (Admin, Director, ...) into User class
    def userize #cast_into_user could be a better name
      self.becomes(User)
    end

Теперь, когда вы не хотите, чтобы пользовательский экземпляр был определенного типа(Администратор, Директор ..), но только Пользователь, вы можете сделать:

    @user = @user.userize

И ваш объект @user теперь будет обрабатываться как Пользователь, а не Администратор или Директор или что-то подобное, то же самое относится к all_without_typecast, какthis:

    @users = User.all_without_typecast # if you're having problems with User.all

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

...