Tuesday, September 27, 2016

How to allow users to define arbitrary number of fields in Rails

Leave a Comment

I'm building a CMS where Administrators must be able to define an arbitrary number fields of different types (text, checkboxes, etc). Users then can fill those fields and generate posts.

How can this be done in Rails? I guess that the persistence of these "virtual" attributes will be done in a serialised attribute in the database. But I am not sure how to struct the views and controllers, or where the fields should be defined.

2 Answers

Answers 1

What you describe is called Entity-Attribute-Value model. MattW. provided 2 possible solutions, but you can also use one of these gems that implement this data pattern instead of handling this yourself:

I haven't use any of these gems before, so I can't suggest which one is best.

RefineryCMS has this extension for the feature you need. You might want to take a look for ideas.

There is also a similar older question here on Stackoverflow.

Answers 2

As soon as it's user defined, it is never a column (field), it's always a row (entity) in the database – users don't get to define your data structure.

Here are two ideas. (1) is the more idiomatic "Rails Way". (2) is somewhat less complicated, but may tie you to your specific DBMS.

(1) Using four models: Posts (n:1) PostTypes (1:n) PostFields (1:n) PostValues (n:1 Posts):

create_table :post_types do |t|   t.string :name   t.text :description   ....  end  create_table :post_fields do |t|   t.references :post_type   t.string :name   t.integer :type   t.boolean :required   .... end  create_table :posts do |t|   t.references :post_type   (... common fields for all posts, like user or timestamp)  end  create_table :post_values do |t|   t.references :post   t.references :post_field   t.string :value   .... end 

Only problem is that you're limited to a single type for values in the database, You could do a polymorphic association and create different models for :boolean_post_values, :float_post_values etc.

  1. One other solution may be to use a json-based data structure, i. e. the same as above, but instead of PostValues to just save it in one field of Posts:

    create_table :posts do |t| t.references :post_type t.json :data ... end // ... no table :post_values

This is probably easier, but json is a postgres-specific datatype (although you could use string and do the de/encoding yourself).

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment