rails_admin won't let me keep my enum field as nil - ruby-on-rails

Model:
class Reservation < ApplicationRecord
# https://naturaily.com/blog/ruby-on-rails-enum
enum recurrence: {
daily: 0,
weekly: 1,
monthly: 2,
annually: 3
}, _prefix: :recurring
belongs_to :user
validates :name, :user_id, presence: true
...
end
Migration:
class CreateReservations < ActiveRecord::Migration[5.2]
def change
create_table :reservations do |t|
t.string 'name', null: false
...
t.boolean 'recurring', default: false, null: false
t.integer 'recurrence', index: true, allow_blank: true, default: nil # trying a lot of things here
t.datetime 'expire_time'
...
end
end
end
Works as expected in the console:
2.4.5 :002 > res = Reservation.new(name: 'test', user_id: 1)
=> #<Reservation id: nil, name: "test", recurring: false, recurrence: nil, date: nil, start_time: nil, end_time: nil, expire_time: nil, user_id: 1, created_at: nil, updated_at: nil>
2.4.5 :003 > res.valid?
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
=> true
2.4.5 :004 > res.save
(0.2ms) BEGIN
Reservation Create (0.5ms) INSERT INTO "reservations" ("name", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "test"], ["user_id", 1], ["created_at", "2019-09-24 20:42:40.933959"], ["updated_at", "2019-09-24 20:42:40.933959"]]
(2.1ms) COMMIT
=> true
2.4.5 :005 > res.reload
Reservation Load (0.3ms) SELECT "reservations".* FROM "reservations" WHERE "reservations"."id" = $1 LIMIT $2 [["id", 5], ["LIMIT", 1]]
=> #<Reservation id: 5, name: "test", recurring: false, recurrence: nil, date: nil, start_time: nil, end_time: nil, expire_time: nil, user_id: 1, created_at: "2019-09-24 20:42:40", updated_at: "2019-09-24 20:42:40">
2.4.5 :006 > res.recurrence
=> nil
And yet, in Rails Admin, when I create or edit a record, they all get assigned to the first enum. Even when I intentionally delete the value from the form, it still saves the record with the first enum value.
Rails Admin:
config.model Reservation do
weight 2
parent Event
list do
field :name
field :display_date do
formatted_value { bindings[:object].display_date }
end
field :recurrence, :active_record_enum # should be unnecessary, but trying everything
field :expire_time
field :user do
label 'Creator'
formatted_value { bindings[:object].user.name }
end
end
end
I have tried:
not indexing this field (thinking Rails Admin wants a value for indexed columns)
ensuring `:active_record_enum` is declared on `field :recurrence`
As suggested, here's the related section of the log:
Started POST "/admin/reservation/new" for ::1 at 2019-09-25 10:09:50 -0400
Processing by RailsAdmin::MainController#new as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"VbhHM7YD+oQvwMYIQzMHyT5e0MXFFuN3pyH/1s6yM8n6kqKvywZ5E8zaABMKhOU+oWpfU1Kk55FWYjL9TzbkbQ==", "reservation"=>{"name"=>"logs", "date"=>"", "start_time"=>"", "end_time"=>"", "recurring"=>"0", "recurrence"=>"", "expire_time"=>"", "start_time_of_day"=>"", "end_time_of_day"=>"", "day_of_week"=>"", "date_of_month"=>"", "date_of_year"=>"", "user_id"=>"1"}, "return_to"=>"http://localhost:3000/admin/reservation", "_save"=>"", "model_name"=>"reservation"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Reservation Create (0.4ms) INSERT INTO "reservations" ("name", "recurrence", "day_of_week", "date_of_month", "date_of_year", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id" [["name", "logs"], ["recurrence", 0], ["day_of_week", ""], ["date_of_month", ""], ["date_of_year", ""], ["user_id", 1], ["created_at", "2019-09-25 14:09:50.926864"], ["updated_at", "2019-09-25 14:09:50.926864"]]
(1.3ms) COMMIT
Redirected to http://localhost:3000/admin/reservation
Completed 302 Found in 26ms (ActiveRecord: 3.0ms)
And, sure enough the param "recurrence"=>"" is transformed into ["recurrence", 0]
It seems to be a known issue, but if it's a legit bug, I still need help with a work-around. Anyone solved this already?

This technically works, but I'm thinking it's not the ideal solution:
If you declare your enum as a hash (not an array) and don't assign anything to 0, it works.
In my case enum recurrence: { daily: 0, weekly: 1, monthly: 2, annually: 3 } becomes enum recurrence: { daily: 1, weekly: 2, monthly: 3, annually: 4 }
The logs show the param isn't passed to the SQL:
Started POST "/admin/reservation/new" for ::1 at 2019-09-25 10:21:17 -0400
Processing by RailsAdmin::MainController#new as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Joyg5VuquwMMKg+ps2zg9t+8r0DZ0iOIkqRlk1cBkemJpkV5Jq84lO8wybL62wIBQIgg1k5gJ25j56i41oVGTQ==", "reservation"=>{"name"=>"test3", "date"=>"", "start_time"=>"", "end_time"=>"", "recurring"=>"0", "recurrence"=>"", "expire_time"=>"", "start_time_of_day"=>"", "end_time_of_day"=>"", "day_of_week"=>"", "date_of_month"=>"", "date_of_year"=>"", "user_id"=>"1"}, "return_to"=>"http://localhost:3000/admin/reservation", "_save"=>"", "model_name"=>"reservation"}
User Load (1.2ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Reservation Create (1.3ms) INSERT INTO "reservations" ("name", "day_of_week", "date_of_month", "date_of_year", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["name", "test3"], ["day_of_week", ""], ["date_of_month", ""], ["date_of_year", ""], ["user_id", 1], ["created_at", "2019-09-25 14:21:17.091292"], ["updated_at", "2019-09-25 14:21:17.091292"]]
(5.7ms) COMMIT
The param contains "recurrence"=>"", but the INSERT INTO doesn't have an array for recurrence like it did before.
And I confirmed in the console:
2.4.5 :001 > res = Reservation.first
Reservation Load (0.4ms) SELECT "reservations".* FROM "reservations" ORDER BY "reservations"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Reservation id: 4, name: "test3", date: nil, start_time: nil, end_time: nil, recurring: false, recurrence: nil, expire_time: nil, start_time_of_day: nil, end_time_of_day: nil, day_of_week: "", date_of_month: "", date_of_year: "", user_id: 1, created_at: "2019-09-25 14:21:17", updated_at: "2019-09-25 14:21:17">
2.4.5 :002 > res.recurrence
=> nil
However, it seems to me like I'm just passing a dis-allowed value and letting Rails reject it. This still seems to be an issue with Rails Admin, specifically with integer enums.

It could be a restriction of using hashes, because nil values should work. Since you're mapping those to 0-indexed values, you should just use a symbol array. The ActiveRecord::Enum documentation (linked above) states:
Note that when an array is used, the implicit mapping from the values to database integers is derived from the order the values appear in the array. In the example, :active is mapped to 0 as it's the first element, and :archived is mapped to 1. In general, the i-th element is mapped to i-1 in the database.
So you should be able to just use an array of symbols for this.

Related

How to save into another table when one table's one record changed from nil to not nil with Rails?

Rails version: 5.2.2.1
Want to insert or update data in post_copies when title data in posts changed from nil to real data.
What I tried:
Model
class Post < ApplicationRecord
after_save :save_post_copy
private
def save_post_copy
if will_save_change_to_title? == nil and saved_change_to_title?
post_copy = PostCopy.find_by(id: self.id)
if post_copy.nil?
PostCopy.create!(id: self.id, title: self.title, body: self.body)
else
post_copy.update!(title: self.title, body: self.body)
end
end
end
end
Console
Case 1: Update from nil to not nil data
post = Post.new
=> #<Post id: nil, title: nil, body: nil, created_at: nil, updated_at: nil>
post.body = 'test'
=> "test"
post.save!
(0.1ms) begin transaction
Post Create (0.9ms) INSERT INTO "posts" ("body", "created_at", "updated_at") VALUES (?, ?, ?) [["body", "test"], ["created_at", "2019-05-20 07:19:50.289983"], ["updated_at", "2019-05-20 07:19:50.289983"]]
(6.5ms) commit transaction
=> true
post.title = '3'
=> '3'
post.save!
(0.1ms) begin transaction
Post Update (0.5ms) UPDATE "posts" SET "title" = ?, "updated_at" = ? WHERE "posts"."id" = ? [["title", "3"], ["updated_at", "2019-05-20 07:20:04.138089"], ["id", 8]]
PostCopy Load (0.1ms) SELECT "post_copies".* FROM "post_copies" WHERE "post_copies"."id" = ? LIMIT ? [["id", 8], ["LIMIT", 1]]
PostCopy Create (0.3ms) INSERT INTO "post_copies" ("title", "body", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["title", "3"], ["body", "test"], ["created_at", "2019-05-20 07:20:04.148852"], ["updated_at", "2019-05-20 07:20:04.
148852"]]
(7.1ms) commit transaction
=> true
Case 2: Update from not nil to nil data
post
=> #<Post id: 10, title: "ten", body: "10", created_at: "2019-05-20 07:21:08", updated_at: "2019-05-20 07:21:41">
post.title = nil
post.save!
(0.1ms) begin transaction
Post Update (1.4ms) UPDATE "posts" SET "title" = ?, "updated_at" = ? WHERE "posts"."id" = ? [["title", nil], ["updated_at", "2019-05-20 07:34:15.594000"], ["id", 10]]
PostCopy Load (0.1ms) SELECT "post_copies".* FROM "post_copies" WHERE "post_copies"."id" = ? LIMIT ? [["id", 10], ["LIMIT", 1]]
PostCopy Create (0.3ms) INSERT INTO "post_copies" ("body", "created_at", "updated_at") VALUES (?, ?, ?) [["body", "10"], ["created_at", "2019-05-20 07:34:15.598453"], ["updated_at", "2019-05-20 07:34:15.598453"]]
(1.8ms) commit transaction
=> true
Case 3: Update from not nil to not nil(another) data
post
=> #<Post id: 10, title: "ten", body: "10", created_at: "2019-05-20 07:21:08", updated_at: "2019-05-20 07:21:41">
post.will_save_change_to_title?
=> nil
post.title = '1'
=> "1"
post.will_save_change_to_title?
=> true
post.save!
(0.1ms) begin transaction
Post Update (0.6ms) UPDATE "posts" SET "title" = ?, "updated_at" = ? WHERE "posts"."id" = ? [["title", "1"], ["updated_at", "2019-05-20 07:37:46.467899"], ["id", 10]]
PostCopy Load (0.1ms) SELECT "post_copies".* FROM "post_copies" WHERE "post_copies"."id" = ? LIMIT ? [["id", 10], ["LIMIT", 1]]
PostCopy Update (0.3ms) UPDATE "post_copies" SET "title" = ?, "updated_at" = ? WHERE "post_copies"."id" = ? [["title", "1"], ["updated_at", "2019-05-20 07:37:46.471654"], ["id", 10]]
(1.3ms) commit transaction
=> true
Case 2 and case 3 trigger the saving/updating to post_copies action. So the will_save_change_to_title? == nil can't known data change from nil to not nil.
From here:
https://api.rubyonrails.org/classes/ActiveModel/Dirty.html
There are these methods:
person = Person.new
person.changed? # => false
person.name = 'Bob'
person.changed? # => true
person.name_changed? # => true
person.name_changed?(from: nil, to: "Bob") # => true
person.name_was # => nil
person.name_change # => [nil, "Bob"]
person.name = 'Bill'
person.name_change # => [nil, "Bill"]
But seems 'not exists in will_save_change_to_title.
I think your problem is in using "after_save": try using "before_save" instead.
EDIT: Alright, i've tried it as well now, and after i realized it wasn't working, i checked your link: it doesn't look like you've done what is required to implement those methods.
Everything is specified at the top of the page:
The requirements for implementing ActiveModel::Dirty are:
include ActiveModel::Dirty in your object.
Call define_attribute_methods passing each method you want to track.
Call [attr_name]_will_change! before each change to the tracked attribute.
Call changes_applied after the changes are persisted.
Call clear_changes_information when you want to reset the changes information.
Call restore_attributes when you want to restore previous data.
Try following those instructions and the example.

generate consecutive unique number in range

I have a field in my database to save a unique number adding the last two digits of the created_at year. At the moment it all works well, except instead of using the SecureRandom method i'm looking for an option to save consecutive numbers. ie. 1901, 1902, 1903...
Model
before_create :create_number
def create_number
loop do
self. number = ((created_at + 1.year).strftime("%y")).concat(sprintf '%02d', SecureRandom.random_number(200))
break unless self.class.exists?(:number => number)
end
end
The key is to use a count query fetch the count of records for that specific year:
class Thing < ApplicationRecord
before_commit :create_number!, if: -> { number.nil? }
private
def count_records_from_same_year
self.class.where(
created_at: (created_at.beginning_of_year..created_at.end_of_year)
).count
end
def create_number!
loop do
year = (created_at + 1.year).strftime("%y")
self.number = year.concat(sprintf '%02d', count_records_from_same_year)
break unless self.class.where(number: self.number).exists?
end
end
end
As you can see it generates consecutive numbers:
irb(main):001:0> Thing.create
(0.3ms) BEGIN
Thing Create (1.3ms) INSERT INTO "things" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2018-10-12 09:57:24.527527"], ["updated_at", "2018-10-12 09:57:24.527527"]]
(1.2ms) SELECT COUNT(*) FROM "things" WHERE "things"."created_at" BETWEEN $1 AND $2 [["created_at", "2018-01-01 00:00:00"], ["created_at", "2018-12-31 23:59:59.999999"]]
Thing Exists (1.4ms) SELECT 1 AS one FROM "things" WHERE "things"."number" = $1 LIMIT $2 [["number", "1901"], ["LIMIT", 1]]
(0.7ms) COMMIT
=> #<Thing id: 1, number: "1901", created_at: "2018-10-12 09:57:24", updated_at: "2018-10-12 09:57:24">
irb(main):002:0> Thing.create
(0.3ms) BEGIN
Thing Create (0.8ms) INSERT INTO "things" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2018-10-12 09:57:26.402797"], ["updated_at", "2018-10-12 09:57:26.402797"]]
(0.7ms) SELECT COUNT(*) FROM "things" WHERE "things"."created_at" BETWEEN $1 AND $2 [["created_at", "2018-01-01 00:00:00"], ["created_at", "2018-12-31 23:59:59.999999"]]
Thing Exists (0.8ms) SELECT 1 AS one FROM "things" WHERE "things"."number" = $1 LIMIT $2 [["number", "1902"], ["LIMIT", 1]]
(0.7ms) COMMIT
=> #<Thing id: 2, number: "1902", created_at: "2018-10-12 09:57:26", updated_at: "2018-10-12 09:57:26">
irb(main):003:0> Thing.create
(0.5ms) BEGIN
Thing Create (0.7ms) INSERT INTO "things" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2018-10-12 09:57:27.537635"], ["updated_at", "2018-10-12 09:57:27.537635"]]
(1.6ms) SELECT COUNT(*) FROM "things" WHERE "things"."created_at" BETWEEN $1 AND $2 [["created_at", "2018-01-01 00:00:00"], ["created_at", "2018-12-31 23:59:59.999999"]]
Thing Exists (0.6ms) SELECT 1 AS one FROM "things" WHERE "things"."number" = $1 LIMIT $2 [["number", "1903"], ["LIMIT", 1]]
(0.7ms) COMMIT
=> #<Thing id: 3, number: "1903", created_at: "2018-10-12 09:57:27", updated_at: "2018-10-12 09:57:27">

Rails not including attribute in SQL Query even though specified

Despite providing the name, the SQL query clearly shows that it's not being passed properly. Rails console doesn't require any whitelist parameters as far as I'm aware, but I've included my controller as well.
Query in rails console: Profession.first.skills.create(name: 'rails')
Profession Load (0.5ms) SELECT "professions".* FROM "professions" ORDER BY "professions"."id" ASC LIMIT $1 [["LIMIT", 1]]
(0.2ms) BEGIN
Skill Exists (0.4ms) SELECT 1 AS one FROM "skills" WHERE "skills"."name" = $1 LIMIT $2 [["name", "twitter"], ["LIMIT", 1]]
SQL (1.0ms) INSERT INTO "skills" ("profession_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["profession_id", 1], ["created_at", "2017-12-18 12:22:11.154775"], ["updated_at", "2017-12-18 12:22:11.154775"]]
(0.1ms) ROLLBACK
Not sure what's going on here.
Validation errors are still working though...
Valid object:
:027 > Skill.new(name: "rails", profession: Profession.first).valid?
Profession Load (0.4ms) SELECT "professions".* FROM "professions" ORDER BY "professions"."id" ASC LIMIT $1 [["LIMIT", 1]]
Skill Exists (1.9ms) SELECT 1 AS one FROM "skills" WHERE "skills"."name" = $1 LIMIT $2 [["name", "twitter"], ["LIMIT", 1]]
=> true
Name being detected for validations:
:020 > Skill.create!(name: String.new, profession: Profession.first)
Profession Load (0.4ms) SELECT "professions".* FROM "professions" ORDER BY "professions"."id" ASC LIMIT $1 [["LIMIT", 1]]
(0.1ms) BEGIN
Skill Exists (0.4ms) SELECT 1 AS one FROM "skills" WHERE "skills"."name" = $1 LIMIT $2 [["name", ""], ["LIMIT", 1]]
(0.1ms) ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
from (irb):20
Max Length:
:023 > Skill.create!(name: "sjadfkahskdfkjsahdfkjaskjdfkjhsdjkfhksajhfjksahasdljflasjdlfkjaskldjflkasjdklfjklasjdklfjlasjdflkjasklfjsdfhkjsahkjdfhjkasdhfkjhkj", profession: Profession.first)
Profession Load (0.4ms) SELECT "professions".* FROM "professions" ORDER BY "professions"."id" ASC LIMIT $1 [["LIMIT", 1]]
(0.2ms) BEGIN
Skill Exists (0.3ms) SELECT 1 AS one FROM "skills" WHERE "skills"."name" = $1 LIMIT $2 [["name", "sjadfkahskdfkjsahdfkjaskjdfkjhsdjkfhksajhfjksahasdljflasjdlfkjaskldjflkasjdklfjklasjdklfjlasjdflkjasklfjsdfhkjsahkjdfhjkasdhfkjhkj"], ["LIMIT", 1]]
(0.3ms) ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Name is too long (maximum is 50 characters)
from (irb):23
ProfessionsController
class ProfessionsController < ApplicationController
def new
#profession = Profession.new
end
def create
#profession = Profession.find_by(name: profession_params[:name])
skill_params = get_nested_params(profession_params, :skills_attributes)
if #profession
# Save skill under existing profession
#skill = Skill.create(name: skill_params[:name], profession_id: #profession.id)
else
#profession = Profession.new(name: profession_params[:name])
if #profession.save {
saved_profession = Profession.find_by(name: profession_params[:name])
saved_profession.skills.create(name: "twitter")
# Skill.create(name: skill_params[:name], profession_id: Profession.find_by(name: profession_params[:name])).save!
}
end
end
respond_to do |format|
if #profession.save || #skill.save
format.js { render layout: false }
format.html { redirect_back fallback_location: root_path, notice: 'Profession was successfully created.' }
else
format.html { redirect_back fallback_location: root_path, notice: 'Skill was not created.' }
end
end
end
private
def profession_params
params.require(:profession).permit(:name,
skills_attributes: [:id,
:name,
:starting_date,
:profession_id,
:_destroy])
end
def get_nested_params parent_params, nested_params
nested_attrs = parent_params[nested_params]
nested_attrs[nested_attrs.keys[0]]
end
end
Updated:
Error from controller when using saved_profession.skills.create(name: "twitter")
Started POST "/professions" for 127.0.0.1 at 2017-12-19 01:50:50 +1300
Processing by ProfessionsController#create as JS
Parameters: {"utf8"=>"✓", "profession"=>{"name"=>"software", "skills_attributes"=>{"1513601431887"=>{"name"=>"rails", "_destroy"=>"false"}}}, "commit"=>"Create Profession"}
Profession Load (0.3ms) SELECT "professions".* FROM "professions" WHERE "professions"."name" = $1 LIMIT $2 [["name", "software"], ["LIMIT", 1]]
(0.1ms) BEGIN
Profession Exists (0.3ms) SELECT 1 AS one FROM "professions" WHERE "professions"."name" = $1 LIMIT $2 [["name", "software"], ["LIMIT", 1]]
SQL (0.3ms) INSERT INTO "professions" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "software"], ["created_at", "2017-12-18 12:50:50.202270"], ["updated_at", "2017-12-18 12:50:50.202270"]]
Profession Load (0.2ms) SELECT "professions".* FROM "professions" WHERE "professions"."name" = $1 LIMIT $2 [["name", "software"], ["LIMIT", 1]]
Skill Exists (0.3ms) SELECT 1 AS one FROM "skills" WHERE "skills"."name" = $1 LIMIT $2 [["name", "twitter"], ["LIMIT", 1]]
SQL (0.7ms) INSERT INTO "skills" ("profession_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["profession_id", 4], ["created_at", "2017-12-18 12:50:50.205484"], ["updated_at", "2017-12-18 12:50:50.205484"]]
(0.1ms) ROLLBACK
Completed 500 Internal Server Error in 8ms (ActiveRecord: 2.2ms)
ActiveRecord::NotNullViolation (PG::NotNullViolation: ERROR: null value in column "name" violates not-null constraint
DETAIL: Failing row contains (11, null, 2017-12-14, 4, 2017-12-18 12:50:50.205484, 2017-12-18 12:50:50.205484, null).
: INSERT INTO "skills" ("profession_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"):
app/controllers/professions_controller.rb:21:in `block in create'
app/controllers/professions_controller.rb:18:in `create'
Try modifying your get_nested_params as:
def get_nested_params(parent_params, nested_params)
parent_params # For logging purpose only
# => {"name"=>"software", "skills_attributes"=>{"1513601431887"=>{"name"=>"rails", "_destroy"=>"false"}}}
nested_params # For logging purpose only
# => :skills_attributes
nested_attrs = parent_params[nested_params]
# => {"1513601431887"=>{"name"=>"rails", "_destroy"=>"false"}}
nested_attrs.values[0] # Return this
# => {"name"=>"rails", "_destroy"=>"false"}
end

How to pass time_select in controller params?

<%= f.time_select :exact, {ampm: true, :discard_minute=> true} %> # t.time "exact"
How do I pass the above in params?
session[:challenge_exact] = params[:exact].parse_time_select! # got nil error
session[:challenge_exact] = challenge_params[:exact]
session[:challenge_exact] = [params["challenge"]["exact(1i)"], params["challenge"]["exact(2i)"], params["challenge"]["exact(3i)"], params["challenge"]["exact(4i)"], params["challenge"]["exact(5i)"]].join(',')
None of the above work. After the challenge is created I see that exact: nil even though it was populated in the form.
Started POST "/challenges" for ::1 at 2017-05-02 08:28:31 -0400
Processing by ChallengesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"oN+DzJK+/GGmpTOOVYMUJQdywuENwxhmX/EVeW6NSZ3w1MctoMd/VHS39d+JasEjHujO88nLPSB2qUKEhet5yA==", "challenge"=>{"name"=>"Write 3 Gratitudes", "categorization"=>"health", "category"=>"goal", "deadline(2i)"=>"6", "deadline(3i)"=>"2", "deadline(1i)"=>"2017", "push"=>"0", "message"=>"1", "mail"=>"0", "remind"=>["mon", "thu", ""], "exact(1i)"=>"2017", "exact(2i)"=>"5", "exact(3i)"=>"2", "exact(4i)"=>"08", "exact(5i)"=>"27", "conceal"=>"0"}, "number"=>"", "email"=>"", "button"=>""}
Redirected to http://localhost:3000/signup
Completed 302 Found in 47ms (ActiveRecord: 0.0ms)
After POST challenge a person then creates a user account...
Started POST "/users" for ::1 at 2017-05-03 01:02:13 -0400
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"4Lv5NSfH9tMnC9+t5bVC5x1d571y0jhwsgvYU3t2sp85XAnNOsHk+FCmtGcj2wJVysV6IlAIzF4jCx1hIofwsNg==", "user"=>{"name"=>"test", "last_name"=>"daddy", "email"=>"testdaddy#gmail.com", "password"=>"[FILTERED]", "time_zone"=>"Eastern Time (US & Canada)", "subscribe"=>"0"}, "button"=>""}
(0.2ms) BEGIN
User Exists (0.5ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('testdaddy#gmail.com') LIMIT 1
SQL (2.3ms) INSERT INTO "users" ("time_zone", "name", "last_name", "subscribe", "email", "password_digest", "number", "created_at", "updated_at", "activation_digest", "activated") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING "id" [["time_zone", "Eastern Time (US & Canada)"], ["name", "test"], ["last_name", "daddy"], ["subscribe", "f"], ["email", "testdaddy#gmail.com"], ["password_digest", "$2a$10$JSSPUl0JDnUXybnAuSnEO.q5DgZscaU3hRpcM4C5L0a0aEtflzb7ne"], ["number", "15169499507"], ["created_at", "2017-05-03 05:02:14.144232"], ["updated_at", "2017-05-03 05:02:14.144232"], ["activation_digest", "$2a$10$NDtWKvxuJm/Hb9YIOzuVIuPpttCZwjsgYws6HhEcbHc.PukxXNpeLa"], ["activated", "t"]]
(18.8ms) COMMIT
(0.2ms) BEGIN
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 16]]
# AS YOU CAN SEE WITH INSERT INTO CHALLENGES FOR SOME REASON THE SESSION PARAMS FOR EXACT ISN'T PASSING FOR EXACT
SQL (0.6ms) INSERT INTO "challenges" ("committed", "name", "categorization", "category", "deadline", "remind", "message", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING "id" [["committed", nil], ["name", "Write 3 Gratitudes"], ["categorization", "health"], ["category", "goal"], ["deadline", "2017-06-03"], ["remind", "---\n- mon\n- thu\n- ''\n"], ["message", "t"], ["user_id", 16], ["created_at", "2017-05-03 05:02:14.434251"], ["updated_at", "2017-05-03 05:02:14.434251"]]
ActsAsTaggableOn::Tag Load (0.6ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = $1 AND "taggings"."taggable_type" = $2 AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL) [["taggable_id", 40], ["taggable_type", "Challenge"]]
ActsAsTaggableOn::Tag Load (0.8ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = $1 AND "taggings"."taggable_type" = $2 AND (taggings.context = 'tags' AND
taggings.tagger_id = 16 AND
taggings.tagger_type = 'User') [["taggable_id", 40], ["taggable_type", "Challenge"]]
(17.2ms) COMMIT
Redirected to http://localhost:3000/
How it appears in console...
exact: 2000-01-01 02:32:00 UTC>
You can access exact params like:
> params[:challenge]["exact(1i)"]
#=> "2017"
> params[:challenge]["exact(2i)"]
#=> "5"
> params[:challenge]["exact(3i)"]
#=> "2"
> params[:challenge]["exact(4i)"]
#=> "08"
> params[:challenge]["exact(5i)"]
#=> "27"
You can parse time like this:
exact_time = "#{params[:challenge]['exact(4i)']}:#{params[:challenge]['exact(5i)']}"
#=> "08:27"
> session[:challenge_exact] = Time.parse(exact_time)
#=> 2017-05-03 08:27:00 +0530
I would do:
session[:challenge_exact] = components_to_absolute(params[:exact])
def components_to_absolute(components)
DateTime.new(components['exact(1i)'].to_i,
components['exact(2i)'].to_i,
components['exact(3i)'].to_i,
components['exact(4i)'].to_i,
components['exact(5i)'].to_i)
end

activerecord model with 2 parent levels

A user can have many questions. Many users can participate on a question and provide multiple answers to the same question. Relation between users and questions seems to be working fine. Problem is with answers.
model/user.rb
has_and_belongs_to_many :questions
has_many :answers
model/question.rb
has_and_belongs_to_many :users
has_many :answers
model/answer.rb
has_one :question
has_one :user, :through => :question
Here is what I'm trying in the console:
irb(main):022:0> u1.questions.first.answers.create(answer: "foo1", order:1)
(0.2ms) BEGIN
Answer Exists (0.5ms) SELECT 1 AS one FROM "answers" WHERE "answers"."answer" = $1 LIMIT $2 [["answer", "foo1"], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "answers" ("created_at", "updated_at", "answer", "order", "question_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", 2017-03-31 19:31:04 UTC], ["updated_at", 2017-03-31 19:31:04 UTC], ["answer", "foo1"], ["order", 1], ["question_id", 1]]
(13.5ms) COMMIT
=> #<Answer id: 6, created_at: "2017-03-31 19:31:04", updated_at: "2017-03-31 19:31:04", answer: "foo1", order: 1, user_id: nil, question_id: 1>
irb(main):023:0> u1.questions.first.answers.create(answer: "bar1", order:2)
(0.4ms) BEGIN
Answer Exists (0.9ms) SELECT 1 AS one FROM "answers" WHERE "answers"."answer" = $1 LIMIT $2 [["answer", "bar1"], ["LIMIT", 1]]
SQL (0.5ms) INSERT INTO "answers" ("created_at", "updated_at", "answer", "order", "question_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", 2017-03-31 19:31:04 UTC], ["updated_at", 2017-03-31 19:31:04 UTC], ["answer", "bar1"], ["order", 2], ["question_id", 1]]
(19.9ms) COMMIT
=> #<Answer id: 7, created_at: "2017-03-31 19:31:04", updated_at: "2017-03-31 19:31:04", answer: "bar1", order: 2, user_id: nil, question_id: 1>
irb(main):024:0> u2.questions.first.answers.create(answer: "foo2", order:1)
(0.7ms) BEGIN
Answer Exists (0.6ms) SELECT 1 AS one FROM "answers" WHERE "answers"."answer" = $1 LIMIT $2 [["answer", "foo2"], ["LIMIT", 1]]
SQL (0.9ms) INSERT INTO "answers" ("created_at", "updated_at", "answer", "order", "question_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", 2017-03-31 19:31:04 UTC], ["updated_at", 2017-03-31 19:31:04 UTC], ["answer", "foo2"], ["order", 1], ["question_id", 1]]
(16.7ms) COMMIT
=> #<Answer id: 8, created_at: "2017-03-31 19:31:04", updated_at: "2017-03-31 19:31:04", answer: "foo2", order: 1, user_id: nil, question_id: 1>
irb(main):025:0> u2.questions.first.answers.create(answer: "bar2", order:2)
(0.5ms) BEGIN
Answer Exists (0.6ms) SELECT 1 AS one FROM "answers" WHERE "answers"."answer" = $1 LIMIT $2 [["answer", "bar2"], ["LIMIT", 1]]
SQL (0.5ms) INSERT INTO "answers" ("created_at", "updated_at", "answer", "order", "question_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", 2017-03-31 19:31:05 UTC], ["updated_at", 2017-03-31 19:31:05 UTC], ["answer", "bar2"], ["order", 2], ["question_id", 1]]
(14.9ms) COMMIT
=> #<Answer id: 9, created_at: "2017-03-31 19:31:05", updated_at: "2017-03-31 19:31:05", answer: "bar2", order: 2, user_id: nil, question_id: 1>
It's obvious something is wrong given the fact user_id is null, unless I specify. I expect user_id populated given the fact I'm creating it from a user object through a question.
irb(main):027:0* u1.questions.first.answers.count
(0.7ms) SELECT COUNT(*) FROM "answers" WHERE "answers"."question_id" = $1 [["question_id", 1]]
=> 4
As I can see here, it just filters by question_id. I expect to retrieve the answers from question_id AND user_id
Can this problem be fixed? Should I just do it in another way?
Thanks
UPDATE
As a summary, this is what I would like to achieve:
User.first.answers.where(question_id: 1)
Where the query looks like:
SELECT "answers".* FROM "answers" WHERE "answers"."user_id" = $1 AND "answers"."question_id" = $2 [["user_id", 1], ["question_id", 1]]
I would like to be able to do:
User.first.questions.first.answers
And get the answers for user_id and question_id
Thanks
I think you should use belongs_to for :user and :question (instead of has_one) and set user manually to your answers from current_user in controller.

Resources