generate consecutive unique number in range - ruby-on-rails

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">

Related

rails_admin won't let me keep my enum field as nil

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.

What is the fastest way to compare two values from different hashes?

I am trying to compare two hashes to see if the values match so i can reconcile some data.
I have a hash of existing accounts and a hash of accounts i need to check if they exist based on the existing accounts to see if it needs to be inserted.
# Reconcile Adaccounts
def self.reconcileAdaccounts(ad_accounts, user_id)
# These are the accounts that exist
existing_accounts = FbAdaccount.active
.select("fb_id")
.where("user_id = ?", user_id)
.as_json
ad_accounts.each do |ad_account|
if #here i would need to check if ad_account["account_id"] matches one of the existing records
p "This one already exists"
p ad_account["account_id"]
else
p "I need to create this one"
p ad_account["account_id"]
end
end
end
I could have nested loops but that does not seem like it would be the best solution, i also tried looking for any rails API functions that could do this but i have not found one.
What is the most efficient way of doing this?
What you're looking for is find_or_create_by. I've additionally wrapped the call in a transaction so all of this hits the database just once — in a single query.
def self.reconcileAdaccounts(ad_accounts, user_id)
FbAdaccount.transaction do
ad_accounts.each do |ad_account|
FbAdaccount.active
.select('fb_id')
.where('user_id = ?', user_id)
.find_or_create_by(ad_account['account_id'])
end
end
end
Transactions
A transaction doesn't magically alter the query, but it wraps it together so that it either all happens together, or doesn't happen at all.
With the following code:
numbers = (3..7).to_a
Ear.transaction do
numbers.each do |number|
Ear.find_or_create_by(bla: number)
end
end
Note the BEGIN and COMMIT statements. They signify where the call starts and ends.
(0.1ms) BEGIN
Ear Load (0.5ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 3], ["LIMIT", 1]]
Ear Create (0.8ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 3], ["created_at", "2019-02-05 23:17:28.157989"], ["updated_at", "2019-02-05 23:17:28.157989"]]
Ear Load (0.2ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 4], ["LIMIT", 1]]
Ear Create (0.2ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 4], ["created_at", "2019-02-05 23:17:28.160838"], ["updated_at", "2019-02-05 23:17:28.160838"]]
Ear Load (0.1ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 5], ["LIMIT", 1]]
Ear Create (0.2ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 5], ["created_at", "2019-02-05 23:17:28.162241"], ["updated_at", "2019-02-05 23:17:28.162241"]]
Ear Load (0.2ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 6], ["LIMIT", 1]]
Ear Create (0.2ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 6], ["created_at", "2019-02-05 23:17:28.163794"], ["updated_at", "2019-02-05 23:17:28.163794"]]
Ear Load (0.2ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 7], ["LIMIT", 1]]
Ear Create (0.2ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 7], ["created_at", "2019-02-05 23:17:28.165589"], ["updated_at", "2019-02-05 23:17:28.165589"]]
(40.9ms) COMMIT
Now here's the same code, just without a transaction to wrap it in:
numbers = (20..25).to_a
numbers.each do |number|
Ear.find_or_create_by(bla: number)
end
Note the total number BEGIN and COMMIT messages:
Ear Load (249.3ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 20], ["LIMIT", 1]]
(73.4ms) BEGIN
Ear Create (48.7ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 20], ["created_at", "2019-02-06 00:12:35.754374"], ["updated_at", "2019-02-06 00:12:35.754374"]]
(79.8ms) COMMIT
Ear Load (0.4ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 21], ["LIMIT", 1]]
(0.2ms) BEGIN
Ear Create (77.7ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 21], ["created_at", "2019-02-06 00:12:35.918461"], ["updated_at", "2019-02-06 00:12:35.918461"]]
(0.3ms) COMMIT
Ear Load (0.2ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 22], ["LIMIT", 1]]
(0.2ms) BEGIN
Ear Create (2.7ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 22], ["created_at", "2019-02-06 00:12:36.003822"], ["updated_at", "2019-02-06 00:12:36.003822"]]
(110.1ms) COMMIT
Ear Load (0.3ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 23], ["LIMIT", 1]]
(0.2ms) BEGIN
Ear Create (0.3ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 23], ["created_at", "2019-02-06 00:12:36.125187"], ["updated_at", "2019-02-06 00:12:36.125187"]]
(37.0ms) COMMIT
Ear Load (0.2ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 24], ["LIMIT", 1]]
(0.2ms) BEGIN
Ear Create (0.4ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 24], ["created_at", "2019-02-06 00:12:36.168414"], ["updated_at", "2019-02-06 00:12:36.168414"]]
(72.4ms) COMMIT
Ear Load (0.3ms) SELECT "ears".* FROM "ears" WHERE "ears"."bla" = $1 LIMIT $2 [["bla", 25], ["LIMIT", 1]]
(1.7ms) BEGIN
Ear Create (0.4ms) INSERT INTO "ears" ("bla", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["bla", 25], ["created_at", "2019-02-06 00:12:36.249285"], ["updated_at", "2019-02-06 00:12:36.249285"]]
(121.2ms) COMMIT

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

Setting value as params on creation

Attempting to make different orders where depending on the order there is different revisions_left which is an integer in the migration file.
I pass the params[:orderref] through a button like so <%= link_to 'Package 1', new_order_path(orderref: 1)%> and then am trying to call it in the create method like so
if params[:orderref] == "1"
#order.revisions_left = 1
elsif params[:orderref] == "2"
#order.revisions_left = 2
elsif params[:orderref] == "3"
#order.revisions_left = 3
end
However the revisions_left fails to save and am unsure why
Edit: Logs
Processing by OrderController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"VGt/2PCPMmVnzXGm+UgHcmSvX7FEHxOoF/TfJoKf8gn3NJ0grD9JkydfswwV50GXIneFuTSClLK7cqvpsRCwRg==", "order"=>{"name"=>"sdada", "email"=>"as#tt.com", "company"=>"", "event_type"=>"Birthday", "country"=>"Brazil", "description"=>"1233 1 123 11", "order_type"=>"3", "price"=>"80"}, "commit"=>"Create Order"}
Order Load (3.0ms) SELECT "orders".* FROM "orders" WHERE "orders"."name" = $1 AND "orders"."company" = $2 AND "orders"."email" = $3 AND "orders"."event_type" = $4 AND "orders"."country" = $5 AND "orders"."description" = $6 AND "orders"."order_type" = $7 AND "orders"."price" = $8 ORDER BY "orders"."id" ASC LIMIT $9 [["name", "sdada"], ["company", ""], ["email", "as#tt.com"], ["event_type", "Birthday"], ["country", "Brazil"], ["description", "1233 1 123 11"], ["order_type", 3], ["price", 80], ["LIMIT", 1]]
(0.4ms) BEGIN
Order Exists (1.0ms) SELECT 1 AS one FROM "orders" WHERE LOWER("orders"."email") = LOWER($1) LIMIT $2 [["email", "as#tt.com"], ["LIMIT", 1]]
SQL (6.2ms) INSERT INTO "orders" ("name", "company", "email", "event_type", "country", "description", "price", "order_type", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING "id" [["name", "sdada"], ["company", ""], ["email", "as#tt.com"], ["event_type", "Birthday"], ["country", "Brazil"], ["description", "1233 1 123 11"], ["price", 80], ["order_type", 3], ["created_at", 2017-08-03 05:26:21 UTC], ["updated_at", 2017-08-03 05:26:21 UTC]]
(71.2ms) COMMIT
(0.1ms) BEGIN
Order Exists (0.5ms) SELECT 1 AS one FROM "orders" WHERE LOWER("orders"."email") = LOWER($1) AND ("orders"."id" != $2) LIMIT $3 [["email", "as#tt.com"], ["id", 3], ["LIMIT", 1]]
SQL (0.7ms) UPDATE "orders" SET "status" = $1, "updated_at" = $2 WHERE "orders"."id" = $3 [["status", 1], ["updated_at", 2017-08-03 05:26:22 UTC], ["id", 3]]
(0.3ms) COMMIT

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