In Rails, how should I implement a Status field for a Tasks app - integer or enum?

后端 未结 6 496
南笙
南笙 2021-01-30 03:24

For a Rails 3.0 Todo app, I have a Tasks model with a Status field. What\'s the best way to store the Status field data (field type) and still display a human-r

相关标签:
6条回答
  • 2021-01-30 03:48

    I prefer to store "normal", "active", .. "completed" as string in the table because:

    • it's self documentation (for example, someone may look at the data but never read the Rails source code)
    • there is little (if not no) performance penalty
    • it is (still) easy to do i18n by means of Rails virtual attribute in the model or whatever layer in other languages

    These days, I tend to decouple Rails constants from the database as much as I can. There are always some PHP/MSSQL/??DBA folks around us (who may not love Rails as much as we do ;-)

    So, the answer is not integer nor enum (but a varchar ;-)

    0 讨论(0)
  • 2021-01-30 03:49

    I have used Enum-Column for such use cases. The plugin allows you to define a enum column type in your migration script and creates a MYSQL enum column type for the attribute.

    create_table :tasks do |t|
      ...
      t.enum :status, :limit => [:normal, :active, :completed], :default => :normal
      ...
    end
    

    Now in your code you can do the following:

    task.status = "active"
    task.status = :completed
    p "Task status: #{task.status}" # prints Task status: completed
    
    
    Task.find_by_status(:active)
    Task.find_by_status("active")
    Task.find_by_status(2)
    

    Works well with serialization too:

    task.to_xml # will result in status= active instead of status-2
    

    Other nice aspect is, the status values are displayed as strings when viewed using a regular DB client(E.g: mysql command console or phpMyAdmin)

    The plugin provides optimal storage and user friendly access for the enumeration types.

    Caveat:

    The plugin is quite old and not maintained. I am using it extensively with MySQL DB. I had to patch the code to get it work on PostgreSQL. If you are using MySQL, this plugin is a good choice.

    0 讨论(0)
  • 2021-01-30 03:51

    Using integer to store the status is a good idea. Nonetheless, I think the code provided by the accepted answer is not elegant since it pollutes the whole model class just because of an attribute.

    My suggestion is overriding the attribute getter:

    def status
      {
        0 => "active",
        1 => "inactive"
      }[read_attribute(:status)] # use the read_attribute method to prevent infinite loop.
    end
    

    The logic of transforming the integer into a string will be only in this getter method, so you don't need to make the class dirty.

    0 讨论(0)
  • 2021-01-30 03:54

    The easiest thing to do would be to just store the actual strings in the field instead of adding another table. Depending on your point of view this is either a bad idea as your database will not be sufficiently normalized or a great idea as your app is now more efficient for being normalized.

    If you opt to not do that and to keep the values in a separate table; you need to setup the relationships in the model.

    class Task < ActiveRecord::Base
        has_one :status
    end
    
    class Status < ActiveRecord::Base
        belongs_to :tasks
    end 
    

    Then in your view you can reference the value by:

    <%= task.status %>
    
    0 讨论(0)
  • 2021-01-30 03:58

    I know this is an old question but I wanted to mention 2 points that come from experience, especially if someone is looking for this now ( 2014 - OQ was in 2010) :

    1. If you are starting a new project > Rails 4 ( technically ActiveRecord 4) - use Enums - the most efficient route. Especially if you need to create any kind of complicated SQL queries later on.
    2. There is on more alternative - create a composite Status model that will hold statuses for all your other models. Make it an STI model (add type column)- then you can create things like OrderStatus < Status or CustomerStatus < Status and your Order and Customer would have status_id attributes. This makes for slower (!) and more cumbersome (!) queries, however you might want to go this route if you are a creating an app that will be shipped to client that has no technical expertise and they would need some kind of ability to add / remove statuses through something like ActiveAdmin on their own rather than modify your code base. This is also an option if your data layer can't handle enums.
    0 讨论(0)
  • 2021-01-30 03:59

    1.It depends on how much you want to optimize queries on the DB.

    2.Not really, it is not supported 'out of the box' by AR. # As of Rails 4 enums are supported out of the box.

    3.IMHO you can use strings without a big performance penalty (just remember to add field to an index). I would do this because it's easier to internationalize and to maintain. However, you can go with integers if you need extra performance.

    You may take a look on 2 SO threads here and here where this is debated.

    4.If you want to keep them as integer, here is how you can accomplish this:

    class Task << AR::Base
      NORMAL    = 1
      ACTIVE    = 2
      COMPLETED = 3
    
    
      STATUSES = {
        NORMAL    => 'normal',
        ACTIVE    => 'active',
        COMPLETED => 'completed'
      }
    
      validates_inclusion_of :status, :in => STATUSES.keys,
          :message => "{{value}} must be in #{STATUSES.values.join ','}"
    
      # just a helper method for the view
      def status_name
        STATUSES[status]
      end
    end
    

    and in view:

    <td><%= task.status_name %></td>
    

    If you want to use strings, it's more simplified:

    STATUSES = ['normal', 'active', 'completed']
    validates_inclusion_of :status, :in => STATUSES,
              :message => "{{value}} must be in #{STATUSES.join ','}"
    
    0 讨论(0)
提交回复
热议问题