I'm trying to create a comment instance. It returns me a validation error.
A comment has one moderation and include reportable. So you can do moderation.reportable and it return comment.
I want the moderation instance to be create when a new comment is created.
class Comment < ApplicationRecord
include Reportable
after_create :create_moderation
def create_moderation
blog = self.blog
self.create_moderation!(blog: blog)
end
end
class Moderation < ApplicationRecord
belongs_to :reportable, foreign_key: "reportable_id", foreign_type: "reportable_type", polymorphic: true
...
end
module Reportable
extend ActiveSupport::Concern
included do
has_one :moderation, as: :reportable, foreign_key: "reportable_id", foreign_type: "reportable_type", class_name: "Moderation"
has_many :reports, through: :moderation
end
Failure/Error: self.create_moderation!(blog: blog)
ActiveRecord::RecordInvalid:
Validation failed: Reportable must exist
EDIT
Trying to add :
belongs_to :reportable, foreign_key: "reportable_id", foreign_type: "reportable_type", polymorphic: true, optional: true
and get :
ActiveRecord::NotNullViolation:
PG::NotNullViolation: ERROR: null value in column "reportable_id" violates not-null constraint
DETAIL: Failing row contains (2, 1, Comment, null, 0, null, 2017-12-01 09:02:11.81419, 2017-12-01 09:02:11.81419, Blog, unmoderate).
: INSERT INTO "moderations" ("blog_id", "reportable_type", "created_at", "updated_at", "blog_type") VALUES ($1, $2, $3, $4, $5) RETURNING "id"
Try optional: true in association. Something like below:
belongs_to :reportable, foreign_key: "reportable_id", foreign_type: "reportable_type", polymorphic: true, optional: true
Refer this. The optional: true is introduced in Rails 5.
EDIT
after_create :create_moderation
def create_moderation
blog = self.blog
self.create_moderation!(blog: blog)
end
I see the two method names are same, i.e., after comment creation, the create_moderation is called which again calls the create_moderation. Can you try changing the name of the method maybe?
ANOTHER SUGESSTION
Can you change the method to
def create_moderation
blog = self.blog
Moderation.create!(blog: blog, reportable: self)
end
or
def create_moderation
blog = self.blog
comment = self
comment.create_moderation!(blog: blog)
end
Do you still get the same error?
You can try below code:
class Comment < ApplicationRecord
include Reportable
before_create :create_moderation
def create_moderation
blog = self.blog
self.build_moderation(blog: blog)
end
end
Related
I have Users and Absences
class User
has_many :absences
end
class Absence
belongs_to :student, foreign_key: "student_id", class_name: "User"
end
and my Absence migration has
t.integer :student_id, index: true, null: false
For some reason, I can say
Absence.first.student
but when I say
Absence.first.student.absences
I get
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column
absences.user_id does not exist) LINE 1: SELECT "absences".* FROM
"absences" WHERE "absences"."user_...
^ : SELECT "absences".* FROM "absences" WHERE "absences"."user_id" = $1 LIMIT $2
Obviously, I haven't set up sth right as it is looking for user_id instead of student_id but I have no idea why this happens... Thank you for any suggestions!
The reason you are getting this error because you didn't specify custom foreign key in has_many relation. Following code will solve your problem.
class User
has_many :absences, foreign_key: "student_id"
end
class Absence
belongs_to :student, foreign_key: "student_id", class_name: "User"
end
I have a polymorphic association of User and AuthorizedReceiver using PersonalInfo.
class User < ApplicationRecord
has_many :authorized_receivers
has_one :personal_info, as: :info_owner
end
class AuthorizedReceiver < ApplicationRecord
belongs_to :user
has_one :personal_info, as: :info_owner
end
class PersonalInfo < ApplicationRecord
belongs_to :info_owner, polymorphic: true
end
When I create my first AuthorizedReceiver to the user, and try to update the PersonalInfo of this AuthorizedReceiver, I have success. However, when I create the second AuthorizedReceiver and try to update its PersonalInfo, I have the following error:
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: insert or update on table "personal_infos" violates foreign key constraint "fk_rails_796da13f22"
DETAIL: Key (info_owner_id)=(2) is not present in table "users".
: INSERT INTO "personal_infos" ("info_owner_id", "full_name", "created_at", "updated_at", "info_owner_type") VALUES ($1, $2, $3, $4, $5) RETURNING "id"
What am I doing wrong?
PS: The same doesn't happens if I do the same with User (i.e, try to update the PersonalInfo of a second user succeed).
EDIT:
class CreatePersonalInfos < ActiveRecord::Migration[5.1]
def up
create_table :personal_infos do |t|
t.references :user, foreign_key: true, index: true, unique: true
end
end
class AddInfoOwnerToPersonalInfos < ActiveRecord::Migration[5.1]
def up
rename_column :personal_infos, :user_id, :info_owner_id
add_column :personal_infos, :info_owner_type, :string
add_index :personal_infos, [ :info_owner_type, :info_owner_id]
PersonalInfo.update_all(info_owner_type: 'User')
change_column :personal_infos, :info_owner_type, :string, null: false
end
def down
rename_column :personal_infos, :info_owner_id, :user_id
remove_column :personal_infos, :info_owner_type
end
end
That resulted in a wrong fk constraint that still pointing to users:
ALTER TABLE ONLY public.personal_infos
ADD CONSTRAINT fk_rails_796da13f22 FOREIGN KEY (info_owner_id) REFERENCES public.users(id);
Like both me and #PavelMikhailyuk commented:
you just renamed the column to info_owner_id
rename_column :personal_infos, :user_id, :info_owner_id
You also need to remove the foreign key:
Since you changed the column name, Rails may not find the association automatically, then you may try to remove the foreign_key with a migration like this:
remove_foreign_key :personal_infos, column: :info_owner_id
I have Rails API application with many to many relationship between users and projects though project_memberships table.
Models:
class User < ActiveRecord::Base
has_many :project_memberships, dependent: :destroy
has_many :projects, -> { uniq }, through: :project_memberships
accepts_nested_attributes_for :project_memberships, allow_destroy: true
end
class Project < ActiveRecord::Base
has_many :project_memberships, dependent: :destroy
has_many :users, -> { uniq }, through: :project_memberships
end
class ProjectMembership < ActiveRecord::Base
belongs_to :user
belongs_to :project
validates :user, presence: true
validates :project, presence: true
end
Controller:
class UsersController < ApplicationController
expose(:user, attributes: :user_params)
respond_to :json
# removed unrelated actions
def update
user.update user_params
respond_with user
end
private
def user_params
params.require(:user).permit(
:first_name, :last_name,
project_memberships_attributes:
[:id, :project_id, :membership_starts_at, :_destroy]
)
end
end
The problem is that when I send a PUT request to http://localhost:3000/users/1 with the following json:
{"user":{"project_memberships_attributes":[{"project_id": 1}]}
user.update user_params creates 2 ProjectMembership records with the same user_id and project_id.
SQL (0.3ms) INSERT INTO "project_memberships" ("project_id", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["project_id", 1], ["user_id", 1], ["created_at", "2016-03-18 18:00:07.670012"], ["updated_at", "2016-03-18 18:00:07.670012"]]
SQL (0.2ms) INSERT INTO "project_memberships" ("project_id", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["project_id", 1], ["user_id", 1], ["created_at", "2016-03-18 18:00:07.671644"], ["updated_at", "2016-03-18 18:00:07.671644"]]
(1.0ms) COMMIT
Btw destroying and updating already existing records by specifying id in nested attributes works correctly.
The first step you need to take is to ensure uniqueness on the database level:
class AddUniquenessConstraintToProjectMemberships < ActiveRecord::Migration
def change
# There can be only one!
add_index :project_memberships, [:user, :project], unique: true
end
end
This avoids race conditions that would occur if we relied on ActiveRecord alone.
From Thoughtbot: The Perils of Uniqueness Validations.
You then want to add an application level validation to avoid the ugly DB driver exceptions that occur if you violate the constraint:
class ProjectMembership < ActiveRecord::Base
belongs_to :user
belongs_to :project
validates :user, presence: true
validates :project, presence: true
validates_uniqueness_of :user_id, scope: :project_id
end
You can then remove the -> { uniq } lambda on your associations as you have taken the proper steps to ensure uniqueness.
The rest of your issues are due to a misunderstanding of how accepts_nested_attributes_for works:
For each hash that does not have an id key a new record will be
instantiated, unless the hash also contains a _destroy key that
evaluates to true.
So {"user":{"project_memberships_attributes":[{"project_id": 1}]} will always create a new record if you do not have proper uniqueness validations.
class Position < ActiveRecord::Base
belongs_to :product, foreign_key: :symbol
end
class Product < ActiveRecord::Base
has_many :positions, primary_key: :symbol, foreign_key: :symbol
end
When I do
Product.first.positions.first I am getting a Product back.
But, when I do Position.first.product I am getting nothing.
When I look at the SQL generated by the query, it is:
SELECT "products.*" FROM "products" WHERE "products.id" = ? LIMIT 1 [["id", 0]]
Why?
The SQL generated is using products.id instead of products.symbol because you didn't tell it that the association should use symbol as the primary key instead of the default of id. So, in your Position class just add primary_key: :symbol to the belongs_to and I think that'll do it.
Try this:
class Product < ActiveRecord::Base
self.primary_key = "symbol"
end
First of all, you need to revise the creation of your model Product.
You need to create it the following way:
class CreateProducts < ActiveRecord::Migration
def change
create_table :products, id: false do |t|
t.string :symbol, null: false
t.timestamps
end
add_index :products, :symbol, unique: true
end
end
And then let your model know about the primary_key, that is not id:
class Product < ActiveRecord::Base
self.primary_key = "symbol"
end
And after that, when you do Product.last, it will generate the following query:
Product.last
# Product Load (0.3ms) SELECT "products".* FROM "products" ORDER BY "products"."symbol" DESC LIMIT 1
I run as my textbook says, but error occurs.
It's a book management application.
What I want to run
irb(main):001:0>publisher = Publisher.create name: 'Gihyo inc.', address: 'Ichigaya'
irb(main):002:0>publisher.books << Book.find(1)
irb(main):003:0>publisher.books.to_a
result
First,
irb(main):001:0>publisher = Publisher.create name: 'Gihyo inc.', address: 'Ichigaya'
seems succeeded.
Next,
irb(main):002:0>publisher.books << Book.find(1)
failed.
The result is as follows.
Book Load(0.6ms) SELECT "books".* FROM "books" WHERE "books"."id" = ? LIMIT 1 [["id", 1]]
(4.0ms) begin transaction
(0.3ms) rollback transaction
ActiveModel::MissingAttributeError: can't write unknown attribute `publisher_id'
from ...
related models
book.rb
class Book < ActiveRecord::Base
scope :costly, ->{where("price>?" ,3000) }
belongs_to :publisher
end
publisher.rb
class Publisher < ActiveRecord::Base
has_many :books
end
related migrations
20141218113551_create_publishers.rb
class CreatePublishers < ActiveRecord ::Migration
def change
create_table :publishers do |t|
t.string :name
t.text :address
t.timestamps
end
end
end
20141218113811_add_publisher_id_to_books.rb
class AddPublisherIdToBooks < ActiveRecord::Migration
def change
add_reference :books, :publisher, index: true
end
end
What is wrong?
I fixed this error after changing -
belongs_to :user, counter_cache: :true
to this - belongs_to :user, counter_cache: true
Notice the boolean, I was using as a symbol...which was totally incorrect.
It actually means, that you have no such field in database.
Checking your migration will help with this issue.