ActiveRecord::NotNullViolation (SQLite3::ConstraintException: NOT NULL constraint failed: - ruby-on-rails

I am doing a simple project which has four model Owner Store Product Warehouse
Owner
class Owner < ApplicationRecord
has_one :store
end
Store
class Store < ApplicationRecord
has_many :ware_houses
has_many :products, through: :ware_houses
end
Product
class Product < ApplicationRecord
has_many :ware_houses
has_many :stores, through: :ware_houses
end
WareHouse
class Product < ApplicationRecord
has_many :ware_houses
has_many :stores, through: :ware_houses
end
first I add o1 as Owner by
o1 = Owner.new(name: "o1") then save it
then add s1 as store by s1 = Store.new(name: "s1")
assign s1 to o1 by writing
o1.store = s1
I tried to add three product by using p1 = Product.new(name: "p1")
but When I wanted to assign p1,p2,p3 to s1 by s1.products = [p1, p2]
it will show this
Product Load (0.2ms) SELECT "products".* FROM "products" INNER JOIN "ware_houses" ON "products"."id" = "ware_houses"."product_id" WHERE "ware_houses"."store_id" = ? [["store_id", 1]]
(0.1ms) begin transaction
Product Create (0.8ms) INSERT INTO "products" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "P1"], ["created_at", "2020-06-23 17:35:41.028631"], ["updated_at", "2020-06-23 17:35:41.028631"]]
(0.1ms) rollback transaction
Traceback (most recent call last):
1: from (irb):16
ActiveRecord::NotNullViolation (SQLite3::ConstraintException: NOT NULL constraint failed: products.store_id)
was following a tutorial online, not quite sure about this unfinished process..
it seems like p1,p2 is not successfully assign to s1.
quite confused, would like to know how to fix it..
thanks a lot!

Related

Rails Eager loading not loading nested associative records though included

I have three classes as follows
Class User < ActiveRecord
has_many :addresses
end
Class Address < ActiveRecord
belongs_to :country
end
Class Country < ActiveRecord
has_many :addresses
end
I am trying to eager load all the nested associative records by the following command
User.includes(addresses: :country)
But with this on rails console, only User and addresses get loaded but not the country. I am not sure what I am missing.
You need to use a hash to declare eager loading of nested resources, e.g.
User.includes(addresses: [:country])
instead of
User.includes(addresses: :country)
See the docs
Here's a full working example (Rails 6)
class Person < ApplicationRecord
has_many :projects
end
class Company
has_many :projects
end
class Project < ApplicationRecord
belongs_to :person
belongs_to :company
end
Then, on the console:
ActiveRecord::Base.logger = Logger.new(STDOUT)
me = Person.includes(projects: [:company]).find 4
Person Load (0.3ms) SELECT "people".* FROM "people" WHERE "people"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]]
Project Load (0.4ms) SELECT "projects".* FROM "projects" WHERE "projects"."person_id" = $1 [["person_id", 4]]
Company Load (0.5ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" IN ($1, $2, $3, $4, $5 ...)
me.projects.first.company.name
=> "my first project's customer name"
Note that there is no query to the database between asking for the name of the customer of my first project and the production of the return value. If there would have been one, the query would have been printed by the logger that pipes it output to STDOUT.

can't write unknown attribute "parent_id", has_one and belongs_to

i'm using rails 4 and was wondering if anyone could find what's wrong in my code.
I have project model and I created a team model that has a belongs_to - has_one relation with project.
project model:
class CrmProject < ActiveRecord::Base
has_one :crm_team
team model
class CrmTeam < ActiveRecord::Base
belongs_to :crm_project
accepts_nested_attributes_for :crm_project
belongs_to :crm_section
belongs_to :manager, class_name: "User"
has_many :users
accepts_nested_attributes_for :users
end
When submitting the form to create a new team i get this error:
ActiveModel::MissingAttributeError in CrmTeamsController#create
can't write unknown attribute `crm_team_id`
and log from server :
Parameters: {"crm_team"=>{"crm_project"=>"5", "manager"=>"3", "user_ids"=>["", "2"]}, "co
mmit"=>"Create Crm team"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1
[["id", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
(0.1ms) begin transaction
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'email#gmail.com' AND "users"."
id" != 2) LIMIT 1
SQL (0.2ms) INSERT INTO "crm_teams" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2020-03-1
1 07:29:19.735115"], ["updated_at", "2020-03-11 07:29:19.735115"]]
(0.2ms) rollback transaction
Completed 500 Internal Server Error in 9ms (ActiveRecord: 1.0ms)
When you use a has_one relation like this
has_one :crm_team
Rails expects you to add crm_team_id to your crm_project, in order to understand which crm_team is related with crm_project object. Adding it going to solve your problem.
In my opinion defining opposite like this much better in logic. Because in future these teams can have multiple projects.
class CrmProject < ActiveRecord::Base
belongs_to :crm_team
class CrmTeam < ActiveRecord::Base
***has_one/has_many(pick one)*** :crm_project
accepts_nested_attributes_for :crm_project
belongs_to :crm_section
belongs_to :manager, class_name: "User"
has_many :users
accepts_nested_attributes_for :users
end

Two belong_to referring the same table + eager loading

First of all, based on this (Rails association with multiple foreign keys) I figured out how to make two belong_to pointing to the same table.
I have something like that
class Book < ApplicationRecord
belongs_to :author, inverse_of: :books
belongs_to :co_author, inverse_of: :books, class_name: "Author"
end
class Author < ApplicationRecord
has_many :books, ->(author) {
unscope(:where).
where("books.author_id = :author_id OR books.co_author_id = :author_id", author_id: author.id)
}
end
It's all good. I can do either
book.author
book.co_author
author.books
However, sometimes I need to eager load books for multiple authors (to avoid N queries).
I am trying to do something like:
Author.includes(books: :title).where(name: ["Lewis Carroll", "George Orwell"])
Rails 5 throws at me: "ArgumentError: The association scope 'books' is instance dependent (the scope block takes an argument). Preloading instance dependent scopes is not supported."
I am trying to figure out what I should do?
Should I go with many-to-many association? It sounds like a solution. However, it looks like it will introduce it's own problems (I need "ordering", meaning that I need explicitly differentiate between main author and co-author).
Just trying to figure out whether I am missing some simpler solution...
Why do you not use HABTM relation? For example:
# Author model
class Author < ApplicationRecord
has_and_belongs_to_many :books, join_table: :books_authors
end
# Book model
class Book < ApplicationRecord
has_and_belongs_to_many :authors, join_table: :books_authors
end
# Create books_authors table
class CreateBooksAuthorsTable < ActiveRecord::Migration
def change
create_table :books_authors do |t|
t.references :book, index: true, foreign_key: true
t.references :author, index: true, foreign_key: true
end
end
end
You can use eagerload like as following:
irb(main):007:0> Author.includes(:books).where(name: ["Lewis Carroll", "George Orwell"])
Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."name" IN (?, ?) LIMIT ? [["name", "Lewis Correll"], ["name", "George Orwell"], ["LIMIT", 11]]
HABTM_Books Load (0.1ms) SELECT "books_authors".* FROM "books_authors" WHERE "books_authors"."author_id" IN (?, ?) [["author_id", 1], ["author_id", 2]]
Book Load (0.1ms) SELECT "books".* FROM "books" WHERE "books"."id" IN (?, ?) [["id", 1], ["id", 2]]
Try this:
Author.where(name: ["Lewis Carroll", "George Orwell"]).include(:books).select(:title)

Delete has_one through: association

I have
class Job < ApplicationRecord
has_one :user, through: :jobs_user
has_one :jobs_user, dependent: :destroy
end
and the model for the join_table looks like this:
class JobsUser < ApplicationRecord
belongs_to :job
belongs_to :user
end
The migration was:
create_join_table :jobs, :shops do |t|
t.index :job_id
end
When I create a job and try to delete it fails :
j = Job.create(user: User.last)
j.destroy!
Job Load (0.3ms) SELECT "jobs".* FROM "jobs" ORDER BY "jobs"."id" DESC LIMIT 1
(0.2ms) BEGIN
JobsShop Load (0.3ms) SELECT "jobs_shops".* FROM "jobs_shops" WHERE "jobs_shops"."job_id" = 21365 LIMIT 1 [["job_id", 21365]]
SQL (0.7ms) DELETE FROM "jobs_shops" WHERE "jobs_shops"."" = NULL [[nil, nil]]
(0.2ms) ROLLBACK
ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: zero-length delimited identifier at or near """"
LINE 1: DELETE FROM "jobs_shops" WHERE "jobs_shops"."" = NULL
^
: DELETE FROM "jobs_shops" WHERE "jobs_shops"."" = NULL
It seems I failed somewhere and it cannot find the column to destroy.
The answer can be found here : https://github.com/rails/rails/issues/25347#issuecomment-300067025
Active Record doesn't have built in support for composite primary keys
That means you can't manipulate a model whose corresponding table doesn't have a single-column primary key defined. That includes doing so through an association that uses said model.
So in my case, choosing create_join_table was not the right choice. Instead create a normal table.
create_table :users_jobs do |t|
t.integer :user_id
t.integer :job_id
# t.index :job_id
end

Using `assign_attributes` saves `has_many through:` association immediately

As far as I know, assign_attributes (unlike update_attributes) is not supposed to save the record or for that matter, any record.
So it quite startled me when I discovered that this is not true when supplying _ids for a has_many through: relation.
Consider the following example:
class GroupUser < ApplicationRecord
belongs_to :group
belongs_to :user
end
class Group < ApplicationRecord
has_many :group_users
has_many :users, through: :group_users
end
class User < ApplicationRecord
has_many :group_users
has_many :groups, through: :group_users
validates :username, presence: true
end
So we have users and groups in an m-to-m relationship.
Group.create # Create group with ID 1
Group.create # Create group with ID 2
u = User.create(username: 'Johny')
# The following line inserts two `GroupUser` join objects, despite the fact
# that we have called `assign_attributes` instead of `update_attributes`
# and, equally disturbing, the user object is not even valid as we've
# supplied an empty `username` attribute.
u.assign_attributes(username: '', group_ids: [1, 26])
The log as requested by a commenter:
irb(main):013:0> u.assign_attributes(username: '', group_ids: [1, 2])
Group Load (0.2ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" IN (1, 2)
Group Load (0.1ms) SELECT "groups".* FROM "groups" INNER JOIN "group_users" ON "groups"."id" = "group_users"."group_id" WHERE "group_users"."user_id" = ? [["user_id", 1]]
(0.0ms) begin transaction
SQL (0.3ms) INSERT INTO "group_users" ("group_id", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["group_id", 1], ["user_id", 1], ["created_at", "2017-06-29 08:15:11.691941"], ["updated_at", "2017-06-29 08:15:11.691941"]]
SQL (0.1ms) INSERT INTO "group_users" ("group_id", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["group_id", 2], ["user_id", 1], ["created_at", "2017-06-29 08:15:11.693984"], ["updated_at", "2017-06-29 08:15:11.693984"]]
(2.5ms) commit transaction
=> nil
I daresay that update_attributes and the _ids construct are mostly used for processing web forms - in this case a form that updates the user itself as well as its group association. So I think it is quite safe to say that the general assumption here is all or nothing, and not a partial save.
Am I using it wrong in some way?
#gokul-m suggests reading about the issue at https://github.com/rails/rails/issues/17368. One of the comments in there points to a temporary workaround: https://gist.github.com/remofritzsche/4204e399e547ff7e3afdd0d89a5aaf3e
an example of my solution to this problem:
ruby:
def assign_parameters(attributes, options = {})
with_transaction_returning_status {self.assign_attributes(attributes, options)}
end
You can handle validation with assign_attributes like so
#item.assign_attributes{ year: "2021", type: "bad" }.valid?

Resources