I am on rails 2.3.8 & I am using mysql as db adapter.
I want to store arrays in my database. After searching I could come up with this very useful article.
Now I need to use GUI for input & not only server console. So say I have a text field called nums which logically should have int array. What should be the format of nums so that it becomes easy to retrieve & store the array out of that string ?
If you use serialize then you shouldn't have to worry about how the data is stored within the text field, although it's actually YAML.
serialize is documented in the Rails/ActiveRecord API (scroll down to the section headed "Saving arrays, hashes, and other non-mappable objects in text columns")
For display, you need a format that is understandable to users and that can be easily converted back into an array in your code. Comma- or space-delimited?
Formatting for output:
delim = ',' # or ' ' for spaces, or whatever you choose
array.join(delim)
Converting back into an array might work as follows:
num_array = nums.split(delim).map(&:to_i) # or to_f if not integers
or perhaps using String#scan?
num_array = nums.scan(/\d+/).map(&:to_i) # for positive integers
If you're using postgres and rails 4, now you have a better native option.
# db/migrate/20140207133952_create_books.rb
create_table :books do |t|
t.string 'title'
t.string 'tags', array: true
t.integer 'ratings', array: true
end
add_index :books, :tags, using: 'gin'
add_index :books, :ratings, using: 'gin'
# app/models/book.rb
class Book < ActiveRecord::Base
end
# Usage
Book.create title: "Brave New World",
tags: ["fantasy", "fiction"],
ratings: [4, 5]
## Books for a single tag
Book.where("'fantasy' = ANY (tags)")
## Books for multiple tags
Book.where("tags #> ARRAY[?]::varchar[]", ["fantasy", "fiction"])
## Books with 3 or more ratings
Book.where("array_length(ratings, 1) >= 3")
http://edgeguides.rubyonrails.org/active_record_postgresql.html
Related
I am doing PDFs for invoices in my system and I would like to be able to store numbers with two decimal places in the database. I am using MoneyRails gem for dealing with currencies, I have setup precision: 10 and scale: 2 on the database level (I use postgres as my DB) but I am getting only 1 decimal place after comma. Why?
class AddPrecisionToInvoices < ActiveRecord::Migration[5.2]
def self.up
change_column :invoices, :total_net_amount_cents, :decimal, precision: 10, scale: 2, default: 0.00
change_column :invoices, :total_gross_amount_cents, :decimal, precision: 10, scale: 2, default: 0.00
end
def self.down
change_column :invoices, :total_net_amount_cents, :bigint
change_column :invoices, :total_gross_amount_cents, :bigint
end
end
invoice.rb
monetize :total_net_amount_cents
monetize :total_gross_amount_cents
In rails console,
invoice.total_gross_amount_cents = Money.new(20_000_00)
invoice.total_gross_amount.to_f #=> 2000.0
Is it possible to store numbers with two decimal places in DB, like 20,000.00?
I don't want to display the PDF in a view so I want to be able to drop the number into my DB as I got it from params from my front-end application without further formatting it in a view.
You can try following, (using in model)
ActiveSupport::NumberHelper::number_to_delimited('%.2f' % '3423432.43234', delimiter: ",", separator: ".")
# => "3,423,432.43"
Here, in above input 3423432.43234 is provided as string, you can provide it as number also.
You can directly use number_with_delimiter in view
The money-rails gem requires monetize columns to be numeric in the database. However, it comes with some helper methods that you could use to re-format as you wish in your model:
# inside Invoice model
require "money-rails/helpers/action_view_extension"
class Invoice < ApplicationRecord
include MoneyRails::ActionViewExtension
# ...
def total_gross_amount_formatted
humanized_money total_gross_amount
end
end
Then in your PDF you can just reference the new formatted attribute:
#invoice_instance.total_gross_amount_formatted
So, this may be more of a "Software Engineering" question. But im thinking of a good way at how to store details for a Widget in active record.
Pretend Widget A has a show page, and in that show page we have some accordian style "FAQS" or something to that effect. Within the accordian is a list, with bullet points highlighting different things of how Widget A works, or how to use Widget A.
Since obviously we wouldn't want to make a separate page for each widget, these items would need to be stored somewhere. But we also wouldn't want to make...10, 20 or 30 separate fields in the database for each one of these. So whats the solutions for this?
My first thought is some sort of hash or array, but does rails allow this? Especially if they are long strings per item. Is there a better way?
Or is the proper way to do this is just claim this as a model (like.."faq_item") or something, and then have a reference ID for the Widget it needs to go to? (that way the "faq_item" model/schema would only need a few fields, and can just assigned the reference ID to the Widget it would belong to.
If each widget has only a few "FAQ items" (or "details", as I'll refer to them) and each detail is nothing more than a text string, you could store a widget's details in a serialized array as such:
# models/widget.rb
class Widget < ApplicationRecord
# serialize the `details` attribute as JSON into
# the `details` column on the widgets table
serialize :details, JSON
end
# db/schema.rb
# ...
create_table "widgets", force: :cascade do |t|
t.string "name"
t.text "details"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
# rails console
wid = Widget.create!(
:name =>
'Wideband, Voltage-Feedback Operational Amplifier With Disable',
:details => [
'Flexible supply range: 5-V to 12-V Single Supply, +/- 2.5-V to 5-V Dual Supply',
'Unity-Gain Stable: 500 MHz (G = 1)',
'High Output Current: 190 mA',
'High Slew Rate: 1800 V/us',
'Wideband 5-V Operation: 220 MHz (G = 2)'
])
# => #<Widget ...>
wid.details.first
# => "Flexible supply range: 5-V to 12-V Single Supply, +/- 2.5-V to 5-V Dual Supply"
You can look at the Rails 5 serialization API for more information on serialize.
If, however, you need to store more information for each detail (for instance, created_at/updated_at fields) or each widget has more than a few details, then it may be prudent to create a new table for widget details as you suggested:
# models/widget.rb
class Widget < ApplicationRecord
has_many :details, :dependent => :destroy
end
# models/widget/detail.rb
class Widget::Detail < ApplicationRecord
belongs_to :widget
end
# db/schema.rb
# ...
create_table "widget_details", force: :cascade do |t|
t.integer "widget_id"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
wid = Widget.create!(
:name =>
'CMOS, 125 MHz Complete DDS Synthesizer',
:details => [
Widget::Detail.create!(:content => '125 MHz Clock Rate'),
Widget::Detail.create!(:content => 'On-Chip High Performance DAC'),
Widget::Detail.create!(:content => '32-Bit Frequency Tuning Word')
])
# => #<Widget ...>
wid.details.first
# => #<Widget::Detail ... content: "125 MHz Clock Rate" ...>
If you are using Postgres you could use a JSONB type field in your database. With a JSONB data type you will be able to have unstructured data while being able to query the field with Postgres and ActiveRecord without the need for a new table.
Like this:
rails g migration add_fields_to_widgets details:jsonb
rails db:migrate
Test your widget creation inside the rails console.
Widget.create(name: "Widget Foo", details: { "how to use": "Instructions on how to use", "height": "12cm", "width": "100cm" })
If you'd want to find all the widgets with 12cm height, you would just have to make a query like this:
Widget.where("details->>'height' = ?", "12cm")
which would return your original Widget Foo object, and then you would be able to manipulate it with pure JavaScript on your front-end.
I am using hstore_translate within a Rails4 project to handle
my I18n needs.
Assume I have the following model:
class Thingy < ActiveRecord::Base
translates :name
end
with table defined in a migration as
create_table :thingies do |t|
t.hstore name_translations
end
add_index ::thingies, :name_translations, using: :gin
In my Ruby code I wish to retrieve a list of the all the names and ids for Thingies with a name in a specific locale.
Previously, before my Thingy had a localised name, I could just do
thingies = Thingy.order(:name).pluck(:id, :name)
Now I am doing
thingies = Thingy.where("name_translations ? 'en'").order("name_translations -> 'en'").to_a.map do |t|
{id: t.id, name: t.name}
end
But I can't help feeling there's a way I can better leverage Postgres to do this all in one line of code without invoking a loop in Ruby.
I've worked it out with a bit of trial and error.
thingies = Thingy.where("name_translations ? 'en'")
.order("name_translations -> 'en'")
.pluck(:id, ("name_translations -> 'en'"))
does the job.
It's not very DRY but it works.
I have a PostgreSQL database for a Rails application.
I want to store the Facebook user id so I thought I could use integer but its not big enough so I chose float.
However now Rails adds .0 to the end of my user id's
What datatype can I use so this does not happen for Facebook user ids which are very long example: 100002496803785
You can use :limit => 8 on your integer column to get a bigint. For example:
class Pancakes < ActiveRecord::Migration
def change
create_table :pancakes do |t|
t.integer :c, :limit => 8
end
end
end
And then, from psql:
=> \d pancakes
Table "public.pancakes"
Column | Type | Modifiers
--------+---------+-------------------------------------------------------
id | integer | not null default nextval('pancakes_id_seq'::regclass)
c | bigint | not null
Indexes:
"pancakes_pkey" PRIMARY KEY, btree (id)
And there's your eight byte bigint column.
You could also use a string for the Facebook ID. You're not doing any arithmetic on the IDs so they're really just opaque bags of bits that happen to look like large integers, strings will sort and compare just fine so they might be the best option. There would be some storage and access overhead due to the increased size of a string over the integer but it probably wouldn't be enough to make any noticeable difference.
Never use a double for something that needs to be exact. You'd probably be fine (except for the trailing .0 of course) in this case because you'd have 52 bits of mantissa and that means that the double would act like a 52 bit integer until your values got large enough to require the exponent. Even so, using double for this would be an awful idea and an abuse of the type system.
I don't use postgresql but in mysql I use BIGINT
According to postgresql data types, BIGINT for postgresql as well.
mu is too short has a great answer, I only want to add that if you want to use the ID as a foreign key between tables then you should stick to the BIGINT solution he describes, not use a string. This is what I use, essentially:
Example:
create_table(:photos) do |t|
t.integer :fb_uid, :limit => 8 # Facebook ID of the photo record
t.integer :facebook_profile_uid, :limit => 8, :null => false # foreign key to user
# ...
end
create_table(:users) do |t|
t.integer :fb_uid, :limit => 8, :null => false # Facebook ID of the user record
t.integer :photos_count, :integer, :default => 0
# ...
end
class User < ActiveRecord::Base
has_many :photos, foreign_key: :facebook_profile_uid, primary_key: :fb_uid
# ...
end
class Photo < ActiveRecord::Base
belongs_to :facebook_profile, foreign_key: :facebook_profile_uid, primary_key: :fb_uid, :counter_cache => true
end
Ran into this problem while using the Google uid which also is quite large.
I found the this answer to be most useful:
Getting error indicating number is "out of range for ActiveRecord::Type::Integer with limit 4" when attempting to save large(ish) integer value
Run a migration to change your table column.
Edit the generated migration -> add, limit: 8
Run db:migrate to migrate to the database.
Restart the rails server.
This will allow you to change the limit of your table column.
I'm looking for a way to store a serialized value of eg. IDs in a column. In before claims that this is not an optimal design: the column is used for IDs of associated records, but will only be used when displaying the record - so no queries are made with selection on the column and no joins will be made on this column either.
In Rails I can serialize the column by using:
class Activity
serialize :data
end
This encodes the column as YAML. For legacy sake and since I'm only storing one dimensional arrays containing only integers, I find it more suitable to store it as a comma-separated value.
I've successfully implemented basic accessors like this:
def data=(ids)
ids = ids.join(",") if ids.is_a?(Array)
write_attribute(:data, ids)
end
def data
(read_attribute(:data) || "").split(",")
end
This works pretty fine. However I'd like to add array-like methods to this attribute:
activity = Activity.first
activity.data << 42
...
How would I do this?
You can do it with composed_of feature as explained in this post.
It should be something like:
composed_of :data, :class_name => 'Array', :mapping => %w(data to_csv),
:constructor => Proc.new {|column| column.to_csv},
:converter => Proc.new {|column| column.to_csv}
after_validation do |u|
u.data = u.data if u.data.dirty? # Force to serialize
end
Haven't tested it though.
You can use serialize with a custom coder in rails 3.1.
See my answer to this question. :-)