Rails 5: Add belongs_to association - ruby-on-rails

I have two tables, "users" and "friendships", I'm trying to make a relationship so that a user can have a friendship with other users, so generate this model friendships with rails g model Friendship user:references friend:references status
in the model friendship.rb:
class Friendship < ApplicationRecord
belongs_to :user
belongs_to :friend,class_name: "User"
end
but at the time of creating the record it shows me the following error:
Friendship.create(user_id: 1, friend_id:2, id: 1)
(0.1ms) begin transaction
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "friendships" ("id", "user_id", "friend_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["id", 1], ["user_id", 1], ["friend_id", 2], ["created_at", "2018-02-26 19:39:57.865042"], ["updated_at", "2018-02-26 19:39:57.865042"]]
(0.1ms) rollback transaction
Traceback (most recent call last):
1: from (irb):2
ActiveRecord::StatementInvalid (SQLite3::SQLException: no such table: main.friends: INSERT INTO "friendships" ("id", "user_id", "friend_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?))
irb(main):003:0>
What could it be? I have Rails 5.1.5

Your Friendship model is set up correctly, but perhaps your migration is not. It should look like this:
class CreateFriendships < ActiveRecord::Migration[5.1]
def change
create_table :friendships do |t|
t.references :user, index: true, foreign_key: true
t.references :friend, index: true, foreign_key: { to_table: :users }
end
end
end
I've set this up in a new Rails 5.1.5 project and it is working for me:
>> User.create(name: 'First User')
>> User.create(name: 'Second User')
>> Friendship.create(user_id: 1, friend_id:2, id: 1)
(0.1ms) begin transaction
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
SQL (0.8ms) INSERT INTO "friendships" ("id", "user_id", "friend_id") VALUES (?, ?, ?) [["id", 1], ["user_id", 1], ["friend_id", 2]]
(0.8ms) commit transaction
#<Friendship id: 1, user_id: 1, friend_id: 2>
Incidentally, you should avoid assigning id numbers to non-persisted objects. Better to let the database handle that job. I would create a Friendship simply as:
>> Friendship.create(user_id: 1, friend_id: 2)

Related

How to use an Enum in Concern

I have this (simplified) concern:
module Nobi::Personable
extend ActiveSupport::Concern
included do
belongs_to :person, :autosave => true delegate :gender,
:gender=, :gender_changed?, :gender_was, :to => :person, :allow_nil => true
enum gender: { male: "male", female: "female" }
end
end
A Resident has this concern.
Now when I do this:
2.6.6> Resident.last.gender
Resident Load (16.2ms) SELECT "residents".* FROM "residents" ORDER BY "residents"."id" DESC LIMIT $1 [["LIMIT", 1]]
Person Load (16.1ms) SELECT "people".* FROM "people" WHERE "people"."id" = $1 LIMIT $2 [["id", 48], ["LIMIT", 1]]
=> "male"
However when I ask: male? I get:
2.6.6> Resident.last.male?
Resident Load (17.0ms) SELECT "residents".* FROM "residents" ORDER BY "residents"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> false
How is this possible?
If I include the enum on the Person model, it works fine:
Person.last.male?
Person Load (15.9ms) SELECT "people".* FROM "people" ORDER BY "people"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> true
I've created a minimalistic demo app which demonstrates this behavior:
https://github.com/rept/enum_app
r = Resident.create(gender: :male, local_gender: :local_male)
r.local_male?
=> true
r.male?
=> false
Declare an enum attribute where the values map to integers in the
database, but can be queried by name.
-- ActiveRecord::Enum
You either need to use an integer column with your enum or declare the mapping explicitly. Rails assumes that the values stored in the database are equal to the indices of the array passed to enum. Since "male" != 0 #male? will return false.
module Nobi::Personable
extend ActiveSupport::Concern
included do
belongs_to :person, :autosave => true
enum gender: {
male: "male",
female: "female"
}
end
end
While using a string column kind of defeats the point of using an enum in the first place declaring the mapping explicitly is seen as a best practice as it will prevent hard to debug breakages that can be caused by simply reordering the enum values in the array.
And if you need proof:
class User < ApplicationRecord
enum gender: [:male, :female] # users.gender is an integer column
end
irb(main):003:0> User.first
(0.9ms) SELECT sqlite_version(*)
User Load (2.0ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 9, created_at: "2020-10-29 09:24:12", updated_at: "2020-10-29 09:24:12", gender: "male">
irb(main):004:0> User.first.male?
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> true
class Resident < ApplicationRecord
# residents.gender is a string column
enum gender: {
male: 'male',
female: 'female'
}
end
irb(main):001:0> Resident.create!(gender: 'male')
(0.4ms) SELECT sqlite_version(*)
(0.1ms) begin transaction
Resident Create (1.6ms) INSERT INTO "residents" ("gender", "created_at", "updated_at") VALUES (?, ?, ?) [["gender", "male"], ["created_at", "2020-10-29 09:56:59.471917"], ["updated_at", "2020-10-29 09:56:59.471917"]]
(4.7ms) commit transaction
=> #<Resident id: 1, gender: "male", created_at: "2020-10-29 09:56:59", updated_at: "2020-10-29 09:56:59">
irb(main):002:0> Resident.first.male?
Resident Load (0.2ms) SELECT "residents".* FROM "residents" ORDER BY "residents"."id" ASC LIMIT ? [["LIMIT", 1]]
=> true

Rails 6. Transferring an attached file from the instance of one model to an instance of another model

I have a User model and a Spkr model.
User:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :tlks, dependent: :destroy
has_many :spkrs, dependent: :destroy
has_many :msgs, dependent: :destroy
has_one_attached :image
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
def slug_candidates
[
:username,
[:username, DateTime.now.to_date]
]
end
end
Spkr
class Spkr < ApplicationRecord
belongs_to :tlk
belongs_to :user
has_many :msgs, dependent: :destroy
has_one_attached :image
end
When my user model has an image attached, when a spkr is made I want it to have the same image attached to it as the user generating the spkr.
I have a SpkrMaker module:
module SpkrMaker
def make_spkr
spkr = Spkr.create!(
user: current_user,
tlk: #tlk,
name: current_user.username,
bio: current_user.bio,
)
if current_user.image.present?
ActiveStorage::Attachment.create(
name: 'image',
record_type: 'Spkr',
record_id: spkr.id,
blob_id: current_user.image.id
)
end
end
end
This is called during the flow, when it is called my server logs state:
Started POST "/tlks" for ::1 at 2020-01-11 10:39:41 +0000
Processing by TlksController#create as JS
Parameters: {"authenticity_token"=>"1NJc0ZSwo8hL1JHw5karkWNxoWRzHPNx/xecaAQHdN+EdsG/o+yZwdxZXLZLPVbkgiPiZZX6PpaF38VX5etTAw==", "tlk"=>{"title"=>"goooolan"}, "commit"=>"Create Tlk"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 23], ["LIMIT", 1]]
(0.1ms) begin transaction
↳ app/controllers/tlks_controller.rb:34:in `create'
Tlk Exists? (0.3ms) SELECT 1 AS one FROM "tlks" WHERE "tlks"."id" IS NOT NULL AND "tlks"."slug" = ? LIMIT ? [["slug", "goooolan"], ["LIMIT", 1]]
↳ app/controllers/tlks_controller.rb:34:in `create'
Tlk Create (0.4ms) INSERT INTO "tlks" ("user_id", "title", "created_at", "updated_at", "slug", "invite_code") VALUES (?, ?, ?, ?, ?, ?) [["user_id", 23], ["title", "goooolan"], ["created_at", "2020-01-11 10:39:41.866979"], ["updated_at", "2020-01-11 10:39:41.866979"], ["slug", "goooolan"], ["invite_code", 469667]]
↳ app/controllers/tlks_controller.rb:34:in `create'
(0.9ms) commit transaction
↳ app/controllers/tlks_controller.rb:34:in `create'
(0.1ms) begin transaction
↳ lib/spkr_maker.rb:3:in `make_spkr'
Spkr Create (0.5ms) INSERT INTO "spkrs" ("tlk_id", "user_id", "name", "bio", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["tlk_id", 135], ["user_id", 23], ["name", "test"], ["bio", "info about me"], ["created_at", "2020-01-11 10:39:41.871219"], ["updated_at", "2020-01-11 10:39:41.871219"]]
↳ lib/spkr_maker.rb:3:in `make_spkr'
(0.8ms) commit transaction
↳ lib/spkr_maker.rb:3:in `make_spkr'
ActiveStorage::Attachment Load (0.1ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ? [["record_id", 23], ["record_type", "User"], ["name", "image"], ["LIMIT", 1]]
↳ lib/spkr_maker.rb:9:in `make_spkr'
(0.0ms) begin transaction
↳ lib/spkr_maker.rb:10:in `make_spkr'
Spkr Load (0.2ms) SELECT "spkrs".* FROM "spkrs" WHERE "spkrs"."id" = ? LIMIT ? [["id", 104], ["LIMIT", 1]]
↳ lib/spkr_maker.rb:10:in `make_spkr'
ActiveStorage::Blob Load (0.0ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ? LIMIT ? [["id", 51], ["LIMIT", 1]]
↳ lib/spkr_maker.rb:10:in `make_spkr'
(0.0ms) rollback transaction
↳ lib/spkr_maker.rb:10:in `make_spkr'
Completed 422 Unprocessable Entity in 25ms (ActiveRecord: 3.7ms | Allocations: 14233)
ActiveRecord::RecordInvalid (Validation failed: Blob must exist):
lib/spkr_maker.rb:10:in `make_spkr'
app/controllers/tlks_controller.rb:36:in `create
When I run User.last.image in the rails console, I get the following:
irb(main):002:0> User.last.image
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
=> #<ActiveStorage::Attached::One:0x00007f88a21cda90 #name="image", #record=#<User id: 23, email: "j#test.com", username: "test", bio: "info about me", name: nil, created_at: "2020-01-11 01:56:22", updated_at: "2020-01-11 01:56:48", slug: "test">>
irb(main):003:0>
I do not know what the problem is, and am not good enough at understanding the server logs to work out what is going wrong.
A Spkr is made during the process, so everything above line 10 is working in the SpkrMaker module (ActiveStorage::Attachment.create( = line 10).
OK Further information as of this morning...(11/01/2020)
irb(main):010:0> User.last.image.id
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
ActiveStorage::Attachment Load (0.2ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ? [["record_id", 23], ["record_type", "User"], ["name", "image"], ["LIMIT", 1]]
=> 51
But
irb(main):011:0> ActiveStorage::Blob.last.id
ActiveStorage::Blob Load (0.2ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" ORDER BY "active_storage_blobs"."id" DESC LIMIT ? [["LIMIT", 1]]
=> 49
I don't know why there is this difference.
While attaching the image to Spkr you are tagging active storage attachment id of current_user instead of blob_id instead of blob id I have update the code.
module SpkrMaker
def make_spkr
spkr = Spkr.create!(
user: current_user,
tlk: #tlk,
name: current_user.username,
bio: current_user.bio,
)
if current_user.image.present?
ActiveStorage::Attachment.create(
name: 'image',
record_type: 'Spkr',
record_id: spkr.id,
blob_id: current_user.image.blob_id
)
end
end
end
It should work now.

Rails with FactoryGirl, parent-child association. Omit creating one more record in child model

I have two models.
Parent model Tag:
class Tag < ApplicationRecord
has_many :keywords, inverse_of: :tag, dependent: :destroy
accepts_nested_attributes_for :keywords
validates :keywords, presence: true
end
As you can see tag should have at least one keyword.
Child model Keyword:
class Keyword < ApplicationRecord
belongs_to :tag, inverse_of: :keywords
validates :tag, presence: true
end
Here is the code of FactoryGirl factories
tag factory:
FactoryGirl.define do
factory :tag do
sequence(:name) { |n| "Tag#{n}" }
after(:build) do |tag_object|
tag_object.keywords << build(:keyword, tag: tag_object)
end
end
end
keyword factory:
FactoryGirl.define do
factory :keyword do
tag
sequence(:name) { |n| "Keyword#{n}" }
end
end
When I create a new record in keywords table with keyword factory it creates one more record in keywords table which is associated with the same parent record in tags table.
How to omit creating one more record in keywords table and keep factories valid?
irb(main):023:0> FactoryGirl.create :keyword
(0.1ms) BEGIN
Keyword Exists (0.7ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword1"], ["LIMIT", 1]]
Tag Exists (0.3ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = $1 LIMIT $2 [["name", "Tag1"], ["LIMIT", 1]]
SQL (0.5ms) INSERT INTO "tags" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "Tag1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
SQL (0.6ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(10.4ms) COMMIT
(0.1ms) BEGIN
Keyword Exists (0.4ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword2"], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword2"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(4.4ms) COMMIT
=> #<Keyword id: 63, tag_id: 36, name: "Keyword2", created_at: "2017-01-21 19:20:14", updated_at: "2017-01-21 19:20:14">
irb(main):024:0>
You can see that it created a record in tags, a record in keywords table, and after that one more record in keywords table.
FactoryGirl creates all the stated associations for the model during the build process. Which means a FactoryGirl.build :keyword will do a FactoryGirl.create :tag so it will have an id for Keyword#tag_id to help pass validations on the Keyword model.
This is consistent with the database activity you are seeing.
irb(main):023:0> FactoryGirl.create :keyword
### keywordA = Keyword.new
### call create(:tag) because of association
### tag1 = Tag.new
### call build(:keyword) in after(:build)
###.keywordB.new(tag: tag1) # which prevents trying to make a new tag!
### tag1.save # which saves the keywordB
(0.1ms) BEGIN
Keyword Exists (0.7ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword1"], ["LIMIT", 1]]
Tag Exists (0.3ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = $1 LIMIT $2 [["name", "Tag1"], ["LIMIT", 1]]
SQL (0.5ms) INSERT INTO "tags" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "Tag1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
SQL (0.6ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(10.4ms) COMMIT
### keywordA.tag = tag1
### keywordA.save
(0.1ms) BEGIN
Keyword Exists (0.4ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword2"], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword2"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(4.4ms) COMMIT
### Since keywordA gets saved after keywordB,
### keywordB gets a 1 from the sequence and
### keywordA gets a 2 from the sequence
=> #<Keyword id: 63, tag_id: 36, name: "Keyword2", created_at: "2017-01-21 19:20:14", updated_at: "2017-01-21 19:20:14">
irb(main):024:0>
This is just the gist of what happens. Personally, I cannot imagine wanting a keyword without it's tag based on the database's schema so I would just call create(:tag) and get the first keyword as mentioned before. But the schema is simple enough so the following should hold up in 100% of the situations we would want to test:
FactoryGirl.define do
factory :tag do
sequence(:name) { |n| "Tag#{n}" }
after(:build) do |this|
this.keywords << build(:keyword) if this.keywords.empty?
end
end
end
FactoryGirl.define do
factory :keyword do
sequence(:name) { |n| "Keyword#{n}" }
after(:build) do |this|
this.tag ||= build(:tag)
end
end
end
build(:tag) # unsaved
build(:tag).keyword # also unsaved
create(:tag) # saved
create(:tag).keyword # also saved
build(:keyword) # unsaved
build(:keyword).tag # also unsaved
create(:keyword) # saved
create(:keyword).tag # also saved
# And it still lets you be specific
create(:tag, keywords: [create(:keyword, name: "More of a phrase")])
create(:keyword, tag: create(:tag, name: "Pop Me!"))
A few more options to consider:
# Fake the association
FactoryGirl.define do
factory :keyword do
sequence(:name) { |n| "Keyword#{n}" }
tag_id 1 # Danger!
# Will make it pass validation but you
# will forget and #tag will not be found
# or not what you expect
end
end
# use a trait
FactoryGirl.define do
factory :keyword do
sequence(:name) { |n| "Keyword#{n}" }
trait :with_tag do
tag
end
end
end
# make a new factory
FactoryGirl.define do
# do not need parent if inside the "factory :tag do"
factory :tag_with_keyword, parent: :tag do
sequence(:name) { |n| "Tag#{n}" }
keyword
end
end
# but now we are back to it creating the keyword on build(:tag)
FactoryGirl does give you enough options to solve many situations but the trick is understanding how it sets up the associations and try to stay away from setting up implicit circular ones.
Keywords and tags cannot exist independent of each other. Your tag factory creates a keyword every time it is called, so you should be calling the tag factory. Try this:
tag = FactoryGirl.create(:tag)
keyword = tag.keywords.first

Rails: How to call one-to-one relationship in rails

I'm new to rails and I want to know how to fetch a one-to-one relationship. I want to fetch users city. In my postgresql database I have:
cities Table:
city:varchar
zipcode: integer
users Table
name:varchar
city_id:int
and in city and user model I have:
class City < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :city
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
I tried the following in my search controller but didnt work, when logged in:
current_user.city
I get the following error
Processing by SearchController#index as HTML
Parameters: {"utf8"=>"✓", "q"=>"", "criteria"=>"1", "commit"=>"Search"}
User Load (1.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 6 ORDER BY "users"."id" ASC LIMIT 1
PG::UndefinedColumn: ERROR: column cities.user_id does not exist
LINE 1: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" =...
^
: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" = $1 LIMIT 1
Completed 500 Internal Server Error in 11ms
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column cities.user_id does not exist
LINE 1: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" =...
^
: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" = $1 LIMIT 1):
why am I suppose to add a user_id column to cities table, when I have cities foreign key in users table? I dont want to add user_id into cities table.
You can use has_one :through association with join table. Some example for you below.
user model:
class User < ActiveRecord::Base
has_one :city, through: :user_city
has_one :user_city
end
city model:
class City < ActiveRecord::Base
belongs_to :user
end
user city join model:
class UserCity < ActiveRecord::Base
belongs_to :city
belongs_to :user
end
migration for join tables:
class JoinUserCity < ActiveRecord::Migration
def change
create_table :user_cities do |t|
t.integer :user_id
t.integer :city_id
end
end
end
Test in rails console:
=> u = User.create
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "users" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2014-12-07 15:47:14.595728"], ["updated_at", "2014-12-07 15:47:14.595728"]]
(3.3ms) commit transaction
=> #<User id: 4, created_at: "2014-12-07 15:47:14", updated_at: "2014-12-07 15:47:14">
=> u.city
City Load (0.2ms) SELECT "cities".* FROM "cities" INNER JOIN "user_cities" ON "cities"."id" = "user_cities"."city_id" WHERE "user_cities"."user_id" = ? LIMIT 1 [["user_id", 4]]
=> nil
=> c = City.create
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "cities" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2014-12-07 15:47:24.535039"], ["updated_at", "2014-12-07 15:47:24.535039"]]
(3.3ms) commit transaction
=> #<City id: 1, created_at: "2014-12-07 15:47:24", updated_at: "2014-12-07 15:47:24">
irb(main):004:0> u.city = c
UserCity Load (0.3ms) SELECT "user_cities".* FROM "user_cities" WHERE "user_cities"."user_id" = ? LIMIT 1 [["user_id", 4]]
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "user_cities" ("city_id", "user_id") VALUES (?, ?) [["city_id", 1], ["user_id", 4]]
(1.0ms) commit transaction
=> #<City id: 1, created_at: "2014-12-07 15:47:24", updated_at: "2014-12-07 15:47:24">
irb(main):005:0> u.save
(0.1ms) begin transaction
(0.1ms) commit transaction
=> true
=> u = User.last
User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
=> #<User id: 4, created_at: "2014-12-07 15:47:14", updated_at: "2014-12-07 15:47:14">
=> u.city
City Load (0.2ms) SELECT "cities".* FROM "cities" INNER JOIN "user_cities" ON "cities"."id" = "user_cities"."city_id" WHERE "user_cities"."user_id" = ? LIMIT 1 [["user_id", 4]]
=> #<City id: 1, created_at: "2014-12-07 15:47:24", updated_at: "2014-12-07 15:47:24">
take a look at the document of has_one and belogns_to,
belongs_to(name, options = {})
Specifies a one-to-one association with another class. This method should only be used if this class
contains the foreign key. If the other class contains the foreign key, then you should use has_one
instead.
as the user table has the foreign key, you should change your model definition like this
class City < ActiveRecord::Base
has_one :user
end
class User < ActiveRecord::Base
belongs_to :city
end

Rails 4: nested attributes and PG::NotNullViolation Error

I have a model User.rb that accepts_nested_attributes_for :address
user.rb
has_one :address
accepts_nested_attributes_for :address
address.rb
belongs_to :user
When I try to test it using Rails console the following happens
user = User.find(157)
User Load (1.5ms) SELECT "users". FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 157]]*
#<User id: 157, name: "John Doe", email: ...
user.address.phone
UserAddress Load (0.8ms) SELECT "addresses". FROM "addresses" WHERE "addresses"."type" IN ('UserAddress') AND "addresses"."user_id" = $1 ORDER BY "addresses"."id" ASC LIMIT 1 [["user_id", 157]]
"1234567"
That's fine so far. However, the following does not work:
user.update(:address_attributes => {:phone => "888888"})
It throws
ActiveRecord::StatementInvalid: PG::NotNullViolation: ERROR: null value in column "user_id" violates not-null constraint
The SQL doesn't make much sense to me
UPDATE "addresses" SET "user_id" = $1, "updated_at" = $2 WHERE "addresses"."type" IN ('UserAddress') AND "addresses"."id" = 128 [["user_id", nil], ["updated_at", Sun, 02 Feb 2014 19:55:07 CET +01:00]]
Why does it try to update user_id with nil when I actually updated the phone attribute?
Perhaps the issue is that you're not using the correct ActiveRecord object?
Maybe you could try:
user.address.update_attributes({phone: "888888"})

Resources