railsadminteam / rails_admin

RailsAdmin is a Rails engine that provides an easy-to-use interface for managing your data
MIT License
7.89k stars 2.26k forks source link

Rails 4.1 enum support #1993

Closed dmilisic closed 9 years ago

dmilisic commented 10 years ago

Rails 4.1 introduced enum attributes which don't behave well with RailsAdmin.

RailsAdmin (v 0.6.2) threats enum attributes as integers by default. This can be "fixed" by adding method to the model, ie.

enum status: [ :active, :archived ]
def status_enum
  self.class.statuses.to_a
end

There is another issue - creating or updating record raises:

ArgumentError in RailsAdmin::MainController#new 
'1' is not a valid state

Obviously the controller got '1' as a string value which doesn't match any of the states ('active', 'archived'), nor state as integer (0, 1). The quick fix might look like:

def status= value
  if value.kind_of?(String) and value.to_i.to_s == value
    super value.to_i
  else
    super value
  end
end

It works, but isn't nice. Especially when you have multiple enum attributes.

Looking forward to support for ActiveRecord::Enum out of the box.

dmilisic commented 10 years ago

Hope this gist helps someone - just put in in config/initializers and RailsAdmin will recognize ActiveRecord enums

https://gist.github.com/dmilisic/38fcd407044ace7514df

jessieay commented 10 years ago

This just got us out of a bind with Rails Admin and ActiveRecord enums. Thank you so much for the gist!

kenchung commented 10 years ago

I put rails_admin_active_record_enum.rb file in the config/initializers but it's not creating the necessary method handlers. Any help? I am using Rails 4.1.1 with Ruby 2.1.2.

jessieay commented 10 years ago

Did you restart your server? You need to restart in order for the file to be picked up by Rails Admin

jessieay commented 10 years ago

Also, for context on what my model looks like:

class User < ActiveRecord::Base
  ROLES = [:learner, :admin, :mentor]
  enum role: ROLES

  def role_enum
    ROLES.each_with_index.to_a
  end
end

Did not need to any any special configuration to the role field in my rails_admin config file once I had the gist linked to above saved in config/initializers/rails_admin_active_record_enum.rb

kenchung commented 10 years ago

Yes, I did restart the server. The part that's not working is this: ActiveRecord::Base.send(:extend, ActiveRecord::RailsAdminEnum)

It's not actually extending the built-in Rails enum.rb for some reason. By the way, shouldn't extending ActiveRecord::RailsAdminEnum module create #{name}_enum method for you without having to be explicitly defined in your model? It's not overriding the set property either.

axsuul commented 10 years ago

Worked for me

TimSullivan commented 10 years ago

The gist works for display, but when I edit and save, I get an ActiveModel::ForbiddenAttributesError exception.

PelagicDev commented 10 years ago

I'm getting the following error... screen shot 2014-07-23 at 11 52 58 am

Loremaster commented 10 years ago

@jegodwin @TimSullivan To fix you problem you need to install rails_admin from master:

gem 'rails_admin', '0.6.2', git: 'git://github.com/sferik/rails_admin.git', branch: 'master', ref: '02ba1dab32d'

This fixes the error ActiveModel::ForbiddenAttributesError error

sanrodari commented 10 years ago

Thanks for the gist @dmilisic +1 to the issue

fstephany commented 10 years ago

@kenchung Same problem here. Display is fine but creating/saving is not.

(edit: Rails 4.1.5 + RailsAdmin 0.6.3, saving triggers ArgumentError in RailsAdmin::MainController#new '1' is not a valid state which makes me think that the gist does not actually overrides enum)

VarunBatraIT commented 10 years ago
extend Enumerize
enumerize :ssl_tls, in: [ [ 'not_used', '0' ], [ 'ssl', '1' ], [ 'tls', '2' ] ], default: 'not_used'

This works

gabu commented 10 years ago

Rails 4.1.5 + RailsAdmin 0.6.3 I also have a same problem.

ArgumentError in RailsAdmin::MainController#edit
'2' is not a valid order_status
gabu commented 10 years ago

@dmilisic Thanks! Your gist helps for me. It's working.

mulaiko commented 10 years ago

Hi @dmilisic, First of all thanks for your gist, it helped solve my problem, but now im facing another issue related to your fix. could you help me solve this maybe? search/filter functionality in rails admin doesnt work anymore after adding the gist above. im getting the following - i have a users table and im search for a user by the name of "user":

ActiveRecord::StatementInvalid in RailsAdmin::Main#index

.rvm/gems/ruby-2.1.1/gems/rails_admin-0.6.2/app/views/rails_admin/main/index.html.haml where line #128 raised:

PG::InvalidTextRepresentation: ERROR: invalid input syntax for integer: "user" LINE 1: ...email) ILIKE '%user%') OR (users.access_level IN ('user'))) ... ^ : SELECT "users".* FROM "users" WHERE ((LOWER(users.email) ILIKE '%user%') OR (users.access_level IN ('user'))) ORDER BY users.id desc LIMIT 20 OFFSET 0

Extracted source (around line #128):

%th.other.right.shrink= "..." %th.last.shrink %tbody

any pointers?

dmilisic commented 10 years ago

@mulaiko I just came across the same issue. Seems that rails_admin tries to search enum fields like strings. The workaround could be setting searchable option to false for the enum field. Works for me.

config.model 'User' do
  configure :access_level do
    searchable false
  end
end
dmilisic commented 10 years ago

@jegodwin sorry for the delay... Are you using rails 4.1?

Pavel-Guseynov commented 10 years ago

If you have custom config for model with enum in rails_admin initializer, rename it to z_rails_admin or whatever to load rails_admin_active_record_enum.rb first.

It solve "1 is not a valid" staff.

osiro commented 9 years ago

Well guys, don't know if anyone here still having issues with Rails Admin and Rails 4 Enum, but anyway here's what I've done to work it out without the need of adding config/initializers/rails_admin_active_record_enum.rb to my initializers:

field :role, :enum do
  searchable false
  enum do
    User.roles.map { |k,_| [k.titleize, k] }
  end

  pretty_value do
    bindings[:object].send(:role).titleize
  end

  def form_value
    bindings[:object].role
  end
end

It partially works, I couldn't make enum fields searchable though, it triggers a invalid SQL statement as mentioned above by @mulaiko.

Anyway... has anyone come up with a workaround for this?

gcstr commented 9 years ago

It's not a workaround, but just something to prevent the error since Rails Admin can't filter enum fields:

config.model User do
    field :name
    field :email
    field :kind do
      filterable false
    end
  end

It will prevent Rails Admin from filtering on that field.

Steviehayes commented 9 years ago

Thanks for the Gist, worked for me as well!!!!

jacek213 commented 9 years ago

Is there a fix for enum filtering? I use version 0.6.5 and when I attempt to filter by enum column rails_admin queries for the actual name of the status instead of integer.

Sacristan commented 9 years ago

I wonder if this will be implemented in Rails admin anytime soon

almozavr commented 9 years ago

To add localization (default attribute path translation) support, change getter method definition to

define_method("#{ name }_enum") {
  self.class.send(name.to_s.pluralize).to_a.map do |k, v|
    translated = I18n.t(k, scope: [:activerecord, :attributes, self.class.to_s.downcase, name.to_s.pluralize])
    processed_key = translated.include?('translation missing') ? k : translated
    [processed_key, v]
  end
}
dgobaud commented 9 years ago

searching still seems broken even with the monkey patch

PG::InvalidTextRepresentation: ERROR: invalid input syntax for integer: "hunter" LINE 1: ...".* FROM "orders" WHERE ((orders.shift_window IN ('hunter'))... ^ : SELECT "orders".* FROM "orders" WHERE ((orders.shift_window IN ('hunter')) OR ....

shift_window is an enum

siegfried commented 9 years ago

@kenchung @fstephany did you get any luck? Same problem here.

dgobaud commented 9 years ago

no :(

dmilisic commented 9 years ago

@dgobaud you could disble search for that filed (in config/initializers/rails_admin.rb):

config.model 'Order' do
  configure :shift_window do
    queryable false
  end
end
dgobaud commented 9 years ago

@dmilisic thanks that is what I'm currently doing but I would like search to work ideally

mshibuya commented 9 years ago

Done :star2:

dgobaud commented 9 years ago

awesome thanks!

dgobaud commented 8 years ago

this is still happening in 0.7.0

dgobaud commented 8 years ago

never mind it was the old monkey patch causing the problem. removed it and now search seems to work

taiwoayanleye commented 7 years ago

This is still happening in 1.1.1. Any fix?

jeremymarc commented 7 years ago

+1

dotdidi commented 7 years ago

@onlyyouandty I also did experience the error in 1.1.1 but solve it by changing the data type of the enum from integer to string. Hope that helps

taiwoayanleye commented 7 years ago

@dotdidi Enum can only be integer values. If you change it to string, the values won't get stored when you try to fill the form because enum calls in integer like [0, 1, 2]. Have you tried it?

dotdidi commented 7 years ago

@onlyyouandty not necesarilty in rails admin

class ReunionRecord

   def graduating_year_enum
      (1950...Date.today.year)
   end

   def class_enum
      ['Regular', 'International', 'Express']

   rails_admin do
      list do

         field :name do
            filterable true
         end

         field :graduating_year do
            filterable true
         end

         field :class do
            filterable true
         end

I put the graduating_year and class as :string and the enum works without any errors

taiwoayanleye commented 7 years ago

You are actually right, based on https://github.com/rails/rails/pull/12747, enum could be string like you said. I will have to figure out why it is not saving as string type in my own case.

tinbka commented 7 years ago

It still doesn't work. Having

class A < ApplicationRecord
  enum status: %w(a b c)
  rails_admin do
    list do
      field :status, :enum do
        enum {%w(a b c)}
        filterable true
      end
    end
  end

  def status_enum
    %w(a b c)
  end
end

(substitute %w(a b c) with {a: 0, b: 1, c: 2}, no difference)

/a?model_name=a&utf8=✓&f%5Bstatus%5D%5B56339%5D%5Bv%5D=c

leads to such a query

SELECT  "as".* FROM "as" WHERE ((as.status IN ('c'))) ORDER BY as.id desc LIMIT $3 OFFSET $4  [["LIMIT", 60], ["OFFSET", 0]]

that fails because as.status is an integer field.

Interestingly, enums work as expected in the edit section with no tweaking: select boxes with word keys and numeric values even though they're treated as integer fields.

tolgap commented 6 years ago

I'd say filtering on an enum is one of the strongest things you can do. Disabling filtering on an enum or "status" field is the worst thing I can do in my interface.

Is there a known workaround by now for having both edit support and filter support for enums?

tinbka commented 6 years ago

@tolgap isn't it fixed yet? I've seen the note in the changelog that it was done in 1.3.0 by https://github.com/sferik/rails_admin/pull/2971

AlexMcConnell commented 6 years ago

My godawful hacky solution for v0.8.1:

rails_admin.rb

RailsAdmin::ApplicationController.class_eval do
  before_action :fix_params

  def fix_params
    <code to change params how you need to make them work right>
  end
end