SeattleDucati
SeattleDucati

Reputation: 293

Storing an array/hash in a text field

I would like to store an array or hash in a string field. This field holds instructions for how to use a particular resource. When a person edits the field, I want to capture the date and current user, and store them along with the text the person input. I'm trying to avoid adding extra fields just to capture the date and current user. I would like to simply add one 4000 character text field, and store it there.

It looks like I need to serialize the field in the model. But I'm struggling with how to save the date,current user, and the text in a single field as an array, or a hash if that makes more sense. I created a simple CRUD app to model what I'm trying to do, based on a single model called 'product'. Below is the edit page, and below that is the update function in the controller. How could I capture the current date and user, and store it in the 'description' field? Would this happen in the edit view itself, or should this happen in the update method in the controller?

edit.html.erb
<%= form_for @product do |f| %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.label :price %><br>
<%= f.text_area :price %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>

The model

class Product < ActiveRecord::Base
#validates  :name, :price , presence: true
serialize :description
end

The update method

def update
@product = Product.find(params[:id])
if @product.update(product_params)
  redirect_to @product
else
  render 'edit'
end
end  

Upvotes: 1

Views: 1658

Answers (2)

smathy
smathy

Reputation: 27961

It'd be clumsy to try to put this in your view, and your model has no knowledge of current_user so you'll have to have some code for this in your controller.

I'd recommend actually having a virtual attribute for the display/editing of your actual description text, so in your model:

serialize :description
def desc
  self.description? ? self.description[:text] : ""
end

Then in your view you'll have fields for that desc attribute:

= f.label :desc
= f.text_field :desc

So then in your controller you need to take that :desc out of the params and insert a :description instead:

def product_params
  p = params.require(:product).permit(:name, :price, :desc)
  p.merge description: { text: p.delete(:desc), user: current_user, date: Date.today }
end

Upvotes: 0

Jack Collins
Jack Collins

Reputation: 1165

First of all, I do not recommend that you pursue this approach unless you have a better reason than "I'm trying to avoid adding extra fields just to capture the date and current user". It will be far easier to manage and maintain this data in the future if you add a field for date and a foreign key to user. Your code above should just work if those two fields are present on the product model.

That being said, it is possible to do this. This is one way you could do it.

def update
  @product = Product.find(params[:id])
  combined_description = []
  combined_description.push(params[:description], params[:current_user], params[:date])

  # The next line returns a string representation of the array
  product_params[:description] = combined_description.to_s

  if @product.update(product_params)
    redirect_to @product
  else
    render 'edit'
  end
end 

Then, when you need to manipulate this data again in the future, you would have to convert the description string back into an array in order to parse out the current_user and date.

def show
  @product = Product.find(params[:id])

  # next line returns [description, current_user, date]
  description_array = JSON.parse(@product.description) 
end

See Array#to_s for the to_s method docs.

Again, I don't recommend that you follow this approach unless you have a really good reason for doing so. Your life will be far easier if you just add those fields to the product model.

Upvotes: 1

Related Questions