I'm new to rails and working on a simple application. I have a model called Client in my ERD and would like to save the address for each client.
My initial thoughts where to save the address as seperate fields i.e:
rails g model Client address_first:string address_second:string city:string ...
but this seems very inefficient. I'm certain there must be a way to save the address s a hash, array or perhaps JSON? I've read around and people keep referring to using "serialise" but I'm uncertain if and how I would implement this for an address field.
Any help would be most appreciated.
Thanks
It depends on what database you are using - both MySQL and Postgres have JSON column types - Postgres also has a hash type called hstore.
They also have different options for querying inside the hash/json column. While these data types are great for dynamic data - there is no real performance gain (it might actually be slower).
And you can't use all the rails goodness of object mapping, validations etc for attributes stored inside a hstore/json column.
When it comes to addresses everybody naively puts it in there User (or Client or Customer) model only to discover that in the real world you need multiple addresses.
# == Schema Information
#
# Table name: clients
#
# id :integer not null, primary key
# created_at :datetime not null
# updated_at :datetime not null
#
class Client < ActiveRecord::Base
has_many :addresses
end
# == Schema Information
#
# Table name: addresses
#
# id :integer not null, primary key
# address_first :string
# address_second :string
# street :string
# city :string
# country :string
# client_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Address < ActiveRecord::Base
belongs_to :client
end
To further Max's
suggestion, I'd like to add that you can include address information inside a single model, if you want.
Having multiple models is cool, but if you're sure you only want certain attributes for your Client
, you can endeavour to save a hash of options to a column in there.
To answer your question directly, and to elaborate on Max's recommendation, there are several column types you can use, depending on the variant of SQL you're employing.
PostgreSQL (Heroku uses this) has the hstore
and json
column types.
Both of these provide you with a column which allows you to store a single serialized object, allowing you to populate this object with serialized data:
I've never used this functionality on PGSQL
before, so you'll be best reading this blog post on how to choose between HSTORE
and JSON
column types.
--
MYSQL, which most people use, also has the capacity to store JSON:
As of MySQL 5.7.8, MySQL supports a native JSON data type that enables efficient access to data in JSON (JavaScript Object Notation) documents. The JSON data type provides these advantages over storing JSON-format strings in a string column:
This seems to work in the same way as its PGSQL brother.
If you do go for one of these (you could use the following setup):
#clients
#id | name | email | address (json) | created_at | updated_at
... it will give you the ability to insert JSON formatted objects into the address
column.
Now, as pointed out by Max
, this will prevent you from running validations
and other attribute-level logic on the address
column. It will prevent you from looking up this column through ActiveRecord, and will make it very difficult to use this data in any other capacity than just to store it.
Thus, if you were absolutely sure you wanted to store it in this way, you'd be able to use the following to do it:
#app/models/client.rb
class Client < ActiveRecord::Base
serialize :address
end
#app/controllers/clients_controller.rb
class ClientsController < ApplicationController
def new
@client = Client.new
end
def create
@client = Client.new client_params
@client.save
end
private
def client_params
params.require(:client).permit(:name, :email, address:{})
end
end
#app/views/clients/new.html.erb
<%= form_for @client do |f| %>
<%= f.text_field :name %>
<%= f.email_field :email %>
<%= f.fields_for :address, OpenStruct.new(f.object.address || {}) do |address| %>
<%= address.text_field :street %>
<%= address.text_field :other_data %>
<% end %>
<%= f.submit %>
<% end %>
fields_for
available here: How to edit a Rails serialized field in a form?
The alternative, as pointed out by Max, is to use multiple models.
This, although looking good, is actually a very delicate process. Many people start inserting models for everything, and soon enough your application is full of models which don't do anything.
If you wanted to use models, you'd be best using the following:
#app/models/client.rb
class Client < ActiveRecord::Base
#columns id | name | email | etc | created_at | updated_at
has_one :address
before_create :build_address, unless: Proc.new { |client| client.address }
end
#app/models/address.rb
class Address < ActiveRecord::Base
#columns id | client_id | your | address | columns | here | created_at | updated_at
belongs_to :client
end
I would only recommend this if you wanted the address to play a major role in the application (IE if they are going to be shipping to it etc).
If you're using the address in any other non-important form, I'd leave it as the serialized hash.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With