Jump to content

The ultimate community for Ruby on Rails developers.


Photo

How to store Array values in database column?

array database hash form

  • Please log in to reply
14 replies to this topic

#1 folubode

folubode

    Signalman

  • Members
  • 17 posts

Posted 25 November 2015 - 07:45 AM

I made a column in subscription table to store subscribed subjects but my multi-select variable which is an array is listed like this subject_ids: “subject_ids: "---\n- ''\n- '17'\n- '21'\n- '26'\n" “ when I checked through rails console;

It looks like this in development log: "subject_ids"=>["", "17", "21", "26"]

But not stored or displayed in table when I looked in the table using Querious and MySQL Workbench. Any advise how to store it in table? I have read through many relevant questions and solutions but no luck yet.

Here are codes if somebody want to have a look. I hope i have pasted all the relevant code parts otherwise please let me know if more code is needed to help:

Controller

class SubscriptionsController < ApplicationController

#initiate new subscription
def new

end

#create subscription
def create
@sub = Subscription.new(sub_params)
.
.
.

#save order in DB and send order payment request to Pesapal
if @sub.save
.
.
.

private
#subscription parameters
def sub_params
params
.require(:subscription).permit(:email, :first_name,
:last_name, :mobile, :account_email,
:plan_id, :classroom_id, :subject_ids => [])
end

.
.
.
end

  model

class Subscription < ActiveRecord::Base
belongs_to :subjects
accepts_nested_attributes_for
:subjects, allow_destroy: true
serialize :subject_ids, Array
.
.
.

view

<%= form_for(@subscription) do |f| %>
.
.
.
<div class="col-md-4">
<div class="form-group">
<%= f.label :subject_ids, "Select subject(s)" %>
<%= f.grouped_collection_select :subject_ids,
Classroom.order(:name), :subjects, :name, :id, :name, {prompt:
"Select a classroom"}, :multiple => true, class:"form-control" %>
</div>
</div>

 

 

migrations

class CreateSubscriptions < ActiveRecord::Migration
def change
create_table
:subscriptions do |t|
t.string :first_name
t
.string :last_name
t
.string :email
t
.string :mobile
t
.datetime :expires
t
.decimal :amount, precision: 8, scale: 2
t.text :subject_id
t
.references :person, index: true
t.references :plan, index: true
t.references :classroom, index: true
t.references :subject, index: true
t.timestamps
end
end
end


class AddSubjectIdToSubscriptions < ActiveRecord::Migration
def change
add_column
:subscriptions, :subject_id, :text
end
end


class RenameSubjectIdOnSubscriptions < ActiveRecord::Migration
def change
rename_column
:subscriptions, :subject_id, :subject_ids
end
end



#2 rahoulb

rahoulb

    Controller

  • Members
  • 111 posts
  • LocationHeadingley, Leeds, England

Posted 01 December 2015 - 07:18 PM

Rails serialize option takes your object (in this case an Array) and converts it into YAML. You can override this (store as JSON or XML,even Gzip the contents) but it's still Rails specific.

Most databases don't have a native Array type so Rails just mangles the data into a text field; which is why it doesn't look the same outside of Rails.

Write better Ruby and Rails code: http://theartandscienceofruby.com/


#3 folubode

folubode

    Signalman

  • Members
  • 17 posts

Posted 02 December 2015 - 11:49 AM

thanks @rahoulb; I'm still trying to catch up with a lots of concepts in rails. do you have any tutorial that serves as example for me to understand your explanation because honestly I don't understand and don't know what to do from here. Meanwhile I based on a number of tutorials I have gone through here is my objectives even though I don't know how to implement it:

 

I need an array that contains subject(s) that belong to a specific classroom that a user subscribed to by using ruby object type HASH specifically a mixed hash e.g sub_package = {"Sub_Classroom" => "Standard 8", 'Sub_Subjects' => ['Maths', 'English', etc]} or simply  sub_subjects = {'Subjects' => ['Maths', 'English', etc]} where sub_classroom is the key/label for subscribed classroom and "Standard 8" is the value likewise, sub_subjects is the key/label for subscribed subjects and the subjects are the values then I need to be able to count the number of subject using sub_package[sub_subjects].size and then use sub_package.keys to show sub lables i.e. sub_classroom and sub_subjects but sub_package[sub_subjects].values to show the subscribed classroom and subjects and sub_package[sub_subjects].length/size to count how many subjects. This is what I understand from the recent tutorial I learned on udemy but am still left with QUESTION - how to pass key-value pair as hash through my form_for to my subscriptions Table? i.e. how to implement my understanding if it's the right approach anyway? would appreciate any help



#4 sidk

sidk

    Dispatcher

  • Members
  • 58 posts

Posted 02 December 2015 - 07:08 PM

Hey @folubode, to answer your question of how to pass a key-value pair in your form, checkout http://guides.rubyon...ing-conventions

 

Here you'll see that by playing around with the name and id attributes of the form input, you can get Rails to interpret what you're passing in as a Hash.

 

In general though I think I'd rethink your approach of passing in hashes as it seems to me a code smell.

 

Are you sure you need a belongs_to and not a has_many?

 

Also I don't think you need to serialize subject_ids, as in your case (if you're using a has_many), it is built into how ActiveRecord associations work. So if you go into Rails console and do something like:

    

    subscription = Subscription.new

    subscription.subject_ids = [16, 12, 14]

 

doing `subscription.subjects` will then give you an ActiveRecord::Relation of the relevant subjects. subject_ids should not be a column name though.


Join my free, 4-part course on Managing Technical Debt in your Rails app at http://email-course.ducktypelabs.com


#5 folubode

folubode

    Signalman

  • Members
  • 17 posts

Posted 03 December 2015 - 05:58 AM

Thanks @sidk

 

I changed belongs_to to has_many and here is the results in console:

 

subscription = Subscription.new

=> #<Subscription id: nil, first_name: nil, last_name: nil, email: nil, mobile: nil, expires: nil, amount: nil, person_id: nil, plan_id: nil, created_at: nil, updated_at: nil, status: "PENDING", pesapal_id: nil, classroom_id: nil, subject_ids: {}>

 

irb(main):002:0> subscription.subject_ids = [16, 12, 14]

=> [16, 12, 14]

 

irb(main):003:0> subscription 

=> #<Subscription id: nil, first_name: nil, last_name: nil, email: nil, mobile: nil, expires: nil, amount: nil, person_id: nil, plan_id: nil, created_at: nil, updated_at: nil, status: "PENDING", pesapal_id: nil, classroom_id: nil, subject_ids: {}>

 

irb(main):004:0> subscription.subjects

=> #<ActiveRecord::Associations::CollectionProxy []>

 

When I removed serialize :subject_ids, Array I have same results.

 

Problem not solved yet, so am going through the link you shared, I will update soon



#6 sidk

sidk

    Dispatcher

  • Members
  • 58 posts

Posted 03 December 2015 - 04:46 PM

You should also:
 

1) Remove the subject_ids column from Subcription

2) Guessing you already have a foreign key added to Subject (subscription_id)?

3) I believe to use Subcription.subject_ids = ..., your subscription needs to be saved to the DB. As in, if you do this with an unsaved subscription (Subscription.new), because it's id is nil, the associations will not work as expected.


Join my free, 4-part course on Managing Technical Debt in your Rails app at http://email-course.ducktypelabs.com


#7 sidk

sidk

    Dispatcher

  • Members
  • 58 posts

Posted 03 December 2015 - 04:48 PM

Also, the subject_ids should correspond to already created Subject record IDs - otherwise it doesn't make sense does it ? :)


Join my free, 4-part course on Managing Technical Debt in your Rails app at http://email-course.ducktypelabs.com


#8 folubode

folubode

    Signalman

  • Members
  • 17 posts

Posted 04 December 2015 - 05:19 AM

this is my subject model:

 

Class Subject < ActiveRecord::Base

 
belongs_to :classroom
  
  belongs_to :subscription
  
end


#9 folubode

folubode

    Signalman

  • Members
  • 17 posts

Posted 04 December 2015 - 05:30 AM

You should also:
 

1) Remove the subject_ids column from Subcription

2) Guessing you already have a foreign key added to Subject (subscription_id)?

3) I believe to use Subcription.subject_ids = ..., your subscription needs to be saved to the DB. As in, if you do this with an unsaved subscription (Subscription.new), because it's id is nil, the associations will not work as expected.

Thanks @sidk, I put added column subject_ids in order to store subjects selected (using form_for) in the Subscription. All available subjects are in Subject Table.  So if I remove that column where will I capture the subscribed subjects during subscription? 

 

These are my updated models:

 

Class Subject < ActiveRecord::Base

  belongs_to :classroom
  belongs_to :subscription
end
 
class Classroom < ActiveRecord::Base
   has_many :subjects, dependent: :destroy
   belongs_to :subscription 
end
 
class Subscription < ActiveRecord::Base  
  has_one   :classroom #foreign key - classroom_id
  accepts_nested_attributes_for :classroom
  has_many  :subjects
  #serialize :subject_ids, Hash
  accepts_nested_attributes_for :subjects, allow_destroy: true
end
 
and my schema:
create_table "classrooms", force: true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
 
  create_table "collections", force: true do |t|
    t.string   "name"
    t.string   "description"
    t.boolean  "premium"
    t.integer  "subject_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "entities_count", default: 0,     null: false
    t.boolean  "public",         default: false, null: false
  end
 
  add_index "collections", ["subject_id"], name: "index_collections_on_subject_id", using: :btree
 
  create_table "entities", force: true do |t|
    t.text     "entity_text"
    t.string   "graphic"
    t.text     "embed_code"
    t.string   "format"
    t.integer  "collection_id"
    t.string   "question_answer"
    t.text     "comment"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.text     "option_a"
    t.text     "option_b"
    t.text     "option_c"
    t.text     "option_d"
  end
 
  add_index "entities", ["collection_id"], name: "index_entities_on_collection_id", using: :btree
 
  create_table "people", force: true do |t|
    t.string   "email",                  default: "",    null: false
    t.string   "encrypted_password",     default: "",    null: false
    t.string   "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer  "sign_in_count",          default: 0,     null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string   "current_sign_in_ip"
    t.string   "last_sign_in_ip"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.boolean  "customer"
    t.integer  "person_sets_count",      default: 0,     null: false
    t.string   "first_name"
    t.string   "last_name"
    t.string   "nickname"
    t.string   "auth_token"
    t.boolean  "admin"
    t.string   "confirmation_token"
    t.datetime "confirmed_at"
    t.datetime "confirmation_sent_at"
    t.boolean  "dead",                   default: false
    t.string   "mobile"
  end
 
  add_index "people", ["confirmation_token"], name: "index_people_on_confirmation_token", using: :btree
  add_index "people", ["email"], name: "index_people_on_email", unique: true, using: :btree
  add_index "people", ["reset_password_token"], name: "index_people_on_reset_password_token", unique: true, using: :btree
 
  create_table "person_sets", force: true do |t|
    t.integer  "progress"
    t.boolean  "done"
    t.integer  "person_id"
    t.integer  "collection_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.text     "chosen"
    t.boolean  "marked",        default: false, null: false
  end
 
  add_index "person_sets", ["collection_id"], name: "index_person_sets_on_collection_id", using: :btree
  add_index "person_sets", ["person_id"], name: "index_person_sets_on_person_id", using: :btree
 
  create_table "plans", force: true do |t|
    t.string   "span"
    t.decimal  "price",      precision: 8, scale: 2
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "duration",                           default: 3600
  end
 
  create_table "subjects", force: true do |t|
    t.string   "name"
    t.integer  "classroom_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
 
  add_index "subjects", ["classroom_id"], name: "index_subjects_on_classroom_id", using: :btree
 
  create_table "subscriptions", force: true do |t|
    t.string   "first_name"
    t.string   "last_name"
    t.string   "email"
    t.string   "mobile"
    t.datetime "expires"
    t.decimal  "amount",       precision: 8, scale: 2
    t.integer  "person_id"
    t.integer  "plan_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "status",                               default: "PENDING"
    t.string   "pesapal_id"
    t.integer  "classroom_id"
    t.text     "subject_ids"
  end
 
  add_index "subscriptions", ["person_id"], name: "index_subscriptions_on_person_id", using: :btree
  add_index "subscriptions", ["plan_id"], name: "index_subscriptions_on_plan_id", using: :btree
 
end

 



#10 folubode

folubode

    Signalman

  • Members
  • 17 posts

Posted 04 December 2015 - 05:30 AM

Also, the subject_ids should correspond to already created Subject record IDs - otherwise it doesn't make sense does it ? :)

yes, that's what am thinking



#11 sidk

sidk

    Dispatcher

  • Members
  • 58 posts

Posted 04 December 2015 - 06:26 PM

I'd suggest you do some reading up on how has_many associations work. To answer your question, you don't need the subject_ids column because when you define a has_many association (`has_many :subjects` for example), ActiveRecord will create two methods `#subject_ids` and `#subject_ids=`, which allow you to automatically create the associations (for example: `@subscription.subjects << [1, 2, 4]`). So if you create a column with the same name, this will lead to unexpected effects.

 

Also, if you're going to use a `has_many :subjects` on your Subcription model, you should create a foreign key column in your Subject model (subscription_id). 

 

You might also want to look into `has_many :through`, and create a join table so that both Subject and Subcription can be associated with multiple subcriptions and subjects respectively.

 

Check this out, this should give you some ideas hopefully: http://railscasts.co...wo-many-to-many

 

Another thing I've found helpful to understand what goes on behind the scenes: go into Rails console and use the `to_sql` method to see what the Rails code translates to. For example what do you get when you do @subcription.subjects.to_sql? 


  • folubode likes this

Join my free, 4-part course on Managing Technical Debt in your Rails app at http://email-course.ducktypelabs.com


#12 folubode

folubode

    Signalman

  • Members
  • 17 posts

Posted 05 December 2015 - 05:35 AM

I'd suggest you do some reading up on how has_many associations work. To answer your question, you don't need the subject_ids column because when you define a has_many association (`has_many :subjects` for example), ActiveRecord will create two methods `#subject_ids` and `#subject_ids=`, which allow you to automatically create the associations (for example: `@subscription.subjects << [1, 2, 4]`). So if you create a column with the same name, this will lead to unexpected effects.

 right, i didnt know this before, thanks.

 

 @subcription.subjects.to_sql gives:

irb(main):002:0*  @subcription.subjects.to_sql

NoMethodError: undefined method `subjects' for nil:NilClass



#13 sidk

sidk

    Dispatcher

  • Members
  • 58 posts

Posted 06 December 2015 - 06:22 PM

You have to define @subscription obviously :). Ruby is complaining because it has no clue what @subscription is. It's a weird error message, but in Ruby, when you do @<anything>, it is automatically defined as a nil if you don't assign it to something. Look up "instance variables in Ruby"


Join my free, 4-part course on Managing Technical Debt in your Rails app at http://email-course.ducktypelabs.com


#14 folubode

folubode

    Signalman

  • Members
  • 17 posts

Posted 08 December 2015 - 04:25 AM

You have to define @subscription obviously :). Ruby is complaining because it has no clue what @subscription is. It's a weird error message, but in Ruby, when you do @<anything>, it is automatically defined as a nil if you don't assign it to something. Look up "instance variables in Ruby"

humm... looks like, lots for me to learn. at risk of dumb question, can I define @subscription in ruby console?



#15 sidk

sidk

    Dispatcher

  • Members
  • 58 posts

Posted 08 December 2015 - 04:21 PM

No worries, you certainly can do it - but if you're in console you can just define local variables (without the @). I'd recommend reading "The Well Grounded Rubyist", this was really helpful to me back when I was starting out and had had some Rails experience. This will give you a solid background in Ruby and allow you to see Rails for what it is, a Ruby app.


Join my free, 4-part course on Managing Technical Debt in your Rails app at http://email-course.ducktypelabs.com






Also tagged with one or more of these keywords: array, database, hash, form

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users