Why does this model not show attributes which are accessible? - ruby-on-rails

I have two models, a Person and a Brain. Person has_one :brain, and Brain belongs_to :person. I want to assign Brain attributes through a /person/ update.
class Person < ActiveRecord::Base
has_one :brain
attr_accessible :name
attr_accessible :brain
accepts_nested_attributes_for :brain
end
class Brain < ActiveRecord::Base
belongs_to :person
attr_accessible :weight_kg
attr_accessible :person
accepts_nested_attributes_for :person
end
In the Rails console I can assign to Person.brain:
> p = Person.first
=> #<Person id: 1, name: "Dave", created_at: "2013-02-14 20:17:35", updated_at: "2013-02-14 20:17:35">
> p.brain.weight_kg = 5.0
Brain Load (0.2ms) SELECT "brains".* FROM "brains" WHERE "brains"."person_id" = 1 LIMIT 1
=> 5.0
> p.save
(0.6ms) begin transaction
(0.6ms) UPDATE "brains" SET "weight_kg" = 5.0, "updated_at" = '2013-02-14 20:18:11.010544' WHERE "brains"."id" = 1
(317.6ms) commit transaction
=> true
Via the web form (and via the console) I cannot, because of the well-worn error, "Can't mass-assign protected attributes: brain_attributes".
I have attr_accessible :weight_kg in Brain, and in Person I have accepts_nested_attributes_for :brain, so I (wrongly) expect this to work.
What am I missing?

Change the attr_accessible to:
attr_accessible :brain_attributes

Related

Rails 'includes' and 'where' with belongs_to and has_many association

I have models:
class Order < ApplicationRecord
acts_as_paranoid
has_paper_trail
enum status: %i[created in_process]
has_many :order_containers
has_many :line_items
end
class LineItem < ApplicationRecord
acts_as_paranoid
has_paper_trail
enum status: %i[in_process collected]
belongs_to :order
belongs_to :variant
end
class Variant < ApplicationRecord
acts_as_paranoid
has_paper_trail
has_many :line_items
belongs_to :product
validates :barcode, presence: true
end
class Product < ApplicationRecord
acts_as_paranoid
has_paper_trail
belongs_to :isles, required: false
has_many :variants
validates :name, :volume, :sku, :price, presence: true
end
class Isle < ApplicationRecord
acts_as_paranoid
has_paper_trail
has_many :products
validates :name, presence: true
end
I need to output only those orders in which the products belong to a specific island. For example, if there are no products in the order that belong to the island I need, then this order and its products do not need to be displayed. And if there are products in the order that belong to a specific island for example (isle.id 1), then such an order needs to be withdrawn and those products that belong to this department
I try this:
#products = Order.includes([:line_items, :variants, :products, :isles]).where('products.isle_id = isle.id').references(:orders)
but i got error:
ailure/Error: return { "#{root_name}": [] } if records.blank?
ActiveRecord::StatementInvalid:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "products"
LINE 1: ..."orders" WHERE "orders"."deleted_at" IS NULL AND (products.i...
I'm sorry if I didn't design well, I'm a novice developer, and here's my first assignment)
This will return all products in Order#1 from Isle#1. If order has multiple variants from the same product it will return duplicate products, if this is not what you need add .distinct to these queries.
>> order = Order.first
>> isle = Isle.first
>> Product.joins(variants: { line_items: :order }).where(isle_id: isle, line_items: { order_id: order })
=> [#<Product:0x00007f1551fc4820 id: 1, isle_id: 1>,
#<Product:0x00007f1551fc4258 id: 2, isle_id: 1>]
You can add a few associations to Order to simplify this:
class Order < ApplicationRecord
has_many :line_items
has_many :variants, through: :line_items
has_many :products, through: :variants
end
>> Order.first.products.where(isle_id: Isle.first)
=> [#<Product:0x00007f154babcb30 id: 1, isle_id: 1>,
#<Product:0x00007f154babca18 id: 2, isle_id: 1>]
Update
Make sure you're creating the associations correctly. Use create! and save! methods in the console to raise any validation errors.
# NOTE: create a new order with two products in two different isles
# just add the required attributes from your models.
order = Order.create!(line_items: [
LineItem.new(variant: Variant.new(product: Product.new(isle: (isle = Isle.create!)))),
LineItem.new(variant: Variant.new(product: Product.new(isle: Isle.new)))
])
# NOTE: verify that you have two products
>> order.products
=> [#<Product:0x00007f6f1cb964e0 id: 1, isle_id: 1>,
#<Product:0x00007f6f1cb963f0 id: 2, isle_id: 2>]
# NOTE: filter by isle
>> order.products.where(isle_id: isle)
=> [#<Product:0x00007f6f1ccda630 id: 1, isle_id: 1>]
>> order.products.where(isle_id: 2)
=> [#<Product:0x00007f6f1d140cd8 id: 2, isle_id: 2>]
https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
You have quite a few issues with the structure of that. If you truly just want the products for a specific Isle then you should be able to just use:
#products = Product.where(isle_id: my_isle_variable)
You also probably need to update the models so that Product belongs_to :isle (singular not plural)

Simulating selecting a file for Active Storage in irb

Creating a new item for Active Storage.
<!-- app/views/docs/_form.html.erb -->
<%= f.label :image, "Select document or image that support this information." %>
<%= f.file_field :image %>
I'm getting a Please review the problems below error when I click Create and thought I'd see what errors happen in irb. But how do I simulate the above step.
Models:
# models/doc.rb
class Doc < ApplicationRecord
has_one_attached :image # Active Storage
belongs_to :source
belongs_to :docable, polymorphic: true
# models/source.rb
class Source < ApplicationRecord
has_many :docs
# models/year.rb
class Year < ApplicationRecord
belongs_to :location
belongs_to :person
has_many :docs, as: :docable
# models/person.rb
class Person < ApplicationRecord
has_many :years, dependent: :destroy
has_many :locations, through: :years
has_many :docs, as: :docable
# models/location.rb
class Location < ApplicationRecord
has_many :years
has_many :people, through: :years
has_many :docs, as: :docable
Where a person lived or worked at on a date is set in years. year, person and location use doc to show the reference for that information.
The source is the title of an old book and I'm imaging various pages in the book. Later I refer to those images using docable (that's the plan).
db/structure.sql: CREATE INDEX index_docs_on_docable_type_and_docable_id ON public.docs USING btree (docable_type, docable_id);
Here's the session:
irb(main):100:0> doc = Doc.new
=> #<Doc id: nil, source_id: nil, page_no: nil, original_url: nil, basename: nil, created_at: nil, updated_at: nil, notes: nil, docable_id: nil, docable_type: nil>
irb(main):101:0> doc.save
=> false
irb(main):102:0> doc.errors.messages
=> {:source=>["must exist"], :docable=>["must exist"]}
irb(main):104:0> doc.source_id = 4
=> 4
irb(main):105:0> doc.save
(42.8ms) BEGIN
Source Load (45.3ms) SELECT "sources".* FROM "sources" WHERE "sources"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]]
(0.2ms) ROLLBACK
=> false
irb(main):106:0> doc.errors.messages
=> {:docable=>["must exist"]}
irb(main):107:0> doc.image =
I may have problems with the polymorphic relationship, so I'm trying to sort that out.
https://edgeguides.rubyonrails.org/active_storage_overview.html#attaching-file-io-objects
doc.image.attach(io: File.open('/path/to/file'), filename: 'file.jpg')

How to set up this belongs_to association in Rails / ActiveRecord?

I have User and Review models. A review can have an author and a subject, both pointing to a User:
class Review < ApplicationRecord
belongs_to :subject, class_name: 'User', optional: true
belongs_to :author, class_name: 'User', optional: true
end
class CreateReviews < ActiveRecord::Migration[5.0]
def change
create_table :reviews do |t|
t.references :subject
t.references :author
end
end
end
This works fine and now I can assign two separate User objects to the Review object to represent who wrote the review against whom.
The user though, doesn't "know" how many reviews he's associated with either as a subject or the author. I added has_and_belongs_to_many :users on reviews and vice-versa, and though doable, isn't exactly what I want.
How do I set up the associations to be able to do the following:
review.author = some_other_user
review.subject = user2
another_review.author = some_other_user
another_review.subject = user2
user2.a_subject_in.count
#=> 2
user2.a_subject_in
#=> [#<Review>, #<Review>]
some_other_user.an_author_in.count
#=> 2
In other words, how do I see how many times a User has been saved as an author or subject for a model with belongs_to?
IF you want to use has_many association on users side, you need to define two separate has_many relations like
class User < ApplicationRecord
has_many :reviews, foreign_key: :author_id
has_many :subject_reviews, class_name: 'Review', foreign_key: :subject_id
end
Now with this you can simply use
irb(main):033:0> s.reviews
Review Load (0.2ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."author_id" = ? [["author_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Review id: 1, comment: "random", subject_id: 2, author_id: 1, created_at: "2016-07-12 01:16:23", updated_at: "2016-07-12 01:16:23">]>
irb(main):034:0> s.subject_reviews
Review Load (0.2ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."subject_id" = ? [["subject_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy []>
Comment: subject_reviews is not a good name :), change it to your requirements.
I think you're looking for this query:
class User
def referenced_in
# this fetches you all the reviews that a user was referenced
Review.where("reviews.author_id = :user_id OR reviews.subject_id = :user_id", user_id: id).distinct
end
end
User.first.referenced_in #should give you all records a user was referenced

Why does `validates_associated` exist?

This section of railsguide says:
You should use this helper when your model has associations with other models and they also need to be validated.
So I thought validation of associated models wouldn't be run without validates_associated.
But actually, It was run without it.
There are two models, School and Student.
class School < ActiveRecord::Base
has_many :students
validates :name, presence: true
end
class Student < ActiveRecord::Base
belongs_to :school
validates :name, presence: true
end
On rails console,
school = School.new
=> #<School id: nil, name: nil, created_at: nil, updated_at: nil>
school.students << Student.new
=> #<ActiveRecord::Associations::CollectionProxy [#<Student id: nil, name: nil, school_id: nil, created_at: nil, updated_at: nil>]>
school.name = "test shcool"
=> "test shcool"
school.save
(0.1ms) begin transaction
(0.1ms) rollback transaction
=> false
school.errors.full_messages
=> ["Students is invalid"]
If with validates_associated like below:
class School < ActiveRecord::Base
has_many :students
validates :name, presence: true
validates_associated :students
end
class Student < ActiveRecord::Base
belongs_to :school
validates :name, presence: true
end
On rails console, I ran the exact same commands as above. But the last command school.errors.full_messages returned different result. (It is strange that there are duplicate error messages.)
school.errors.full_messages
=> ["Students is invalid", "Students is invalid"]
My questions are
Is this a RailsGuide's mistake?
Why does validates_associated exist?
Or do I have any mistaken idea?
My environment is
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin14.0]
Rails 4.2.0
it checks to see if the associated objects are valid before saving

Why is the before filter giving me an error

Here is my data structure
class User < ActiveRecord::Base
has_many :companies, :through => :positions
has_many :positions
class Company < ActiveRecord::Base
has_many :positions
has_many :users, :through => :positions
class Position < ActiveRecord::Base
belongs_to :company
belongs_to :user
attr_accessible :company_id, :user_id, :regular_user
end
class Position < ActiveRecord::Base
belongs_to :company
belongs_to :user
attr_accessible :company_id, :user_id, :regular_user
before_save :set_regular_user
def set_regular_user
if self.user.is_admin?
self.regular_user = false
else
self.regular_user = true
end
end
end
everytime i run
#user.companies << Company.last
I get ActiveRecord::RecordNotSaved: ActiveRecord::RecordNotSaved
but if i remove my before filter everthing works out perfect and it saves as expected
#user.companies << Company.last
Company Load (0.2ms) SELECT `companies`.* FROM `companies` ORDER BY `companies`.`id` DESC LIMIT 1
(0.1ms) BEGIN
SQL (0.2ms) INSERT INTO `positions` (`company_id`, `created_at`, `regular_user`, `updated_at`, `user_id`)
VALUES
(263, '2012-07-25 14:44:15', NULL, '2012-07-25 14:44:15', 757)
Any ideas what i am missing ....this question is based on this earlier question
Callbacks need to return true in order to continue, false cancels the operation. In your function, the value of the if statement may be false: self.regular_user = false The return value of a ruby function is the last statement.
Just add a return true to the end.
As #DGM says, it is good practice for callbacks to always return true at the end (or false at some point in the flow if they should prevent the code continuing). Otherwise it can be the source of some very odd bugs further down the line (speaking from experience :) ).
I suspect it is the if branch returning the false. Hopefully if you just add true as the last statement in the callback it should work.

Resources