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')
Related
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)
I have the following models and relationships. I'm building a form and am wanting to initialize terms of the proposal for the form. How can I select a specific ProposalTerm by it's term_type_id to pass on to my fields_for block?
Proposal
class Proposal < ApplicationRecord
after_initialize :add_terms
has_many :terms, class_name: "ProposalTerm", dependent: :destroy
accepts_nested_attributes_for :terms
def add_terms
terms << ProposalTerm.first_or_initialize(type: TermType.signing_bonus)
end
end
ProposalTerm
class ProposalTerm < ApplicationRecord
include DisableInheritance
belongs_to :proposal
belongs_to :type, class_name: "TermType", foreign_key: "term_type_id"
def self.signing_bonus
find_by(type: TermType.signing_bonus)
end
end
My Attempt
>> #proposal.terms
=> #<ActiveRecord::Associations::CollectionProxy [#<ProposalTerm id: nil, season: nil, value: nil, is_guaranteed: false, term_type_id: 2, proposal_id: nil, created_at: nil, updated_at: nil>]>
>> #proposal.terms.where(term_type_id: 2)
=> #<ActiveRecord::AssociationRelation []>
I was able to figure out an answer. I had tried "select" but I was doing it incorrectly.
I had tried the following,
#proposal.terms.select(term_type_id: 2)
but that wasn't returning anything. I then did the following...
#proposal.terms.select { |t| t.term_type_id = 2 }
If you want to return just the first instance use "detect" ...
#proposal.terms.detect { |t| t.term_type_id = 2 } }
I'm running Rails 6.0.2.1.
I have a fairly simple model between clients, invoices, and proposals. An invoice belongs to a proposal (but this is optional - an invoice doesn't have to be based upon a proposal). Also, an invoice belongs to a client.
class Invoice < ActiveRecord::Base
belongs_to :client
belongs_to :proposal, foreign_key: "prop_id"
...
class Proposal < ActiveRecord::Base
belongs_to :client
has_one :invoice, foreign_key: "prop_id", dependent: :destroy
...
class Client < ActiveRecord::Base
has_many :proposals, dependent: :destroy
has_many :invoices, dependent: :destroy
...
These models have no validations between one another. That is, I do not have a validation indicating that an invoice must have a proposal or even a client. However, Rails is giving me validation errors on their existence if I check the validity of any field in the invoice:
> inv = Invoice.new
=> #<Invoice id: nil, client_id: nil, prop_id: nil, tocb_id: nil, fromcb_id: nil,
date_invoice: "2020-02-10", written_by: nil, terms: nil, date_due: nil,
status: "Pending", shipping: nil, amount: 0.0, amt_due: 0.0, deposit: nil,
tax_rate: nil, comments: nil>
> inv.errors.count
=> 0
> inv.valid? :amount
=> false
> inv.errors.count
=> 2
> inv.errors
=> #<ActiveModel::Errors:0x000056466dac7a38 #base=#<Invoice id: nil, client_id: nil,
prop_id: nil, ... , #messages={:client=>["must exist"], :proposal=>["must exist"]},
#details={:client=>[{:error=>:blank}], :proposal=>[{:error=>:blank}]}>
Why is it flagging missing client and missing proposal as existence errors?
You are getting the error because in Rails 5 and above, whenever we define a belongs_to association, it is required to have the associated record present by default.
So you need to link a client and a proposal to invoice, only then would you be able to create an invoice object. Which means you need to do this -
client = Client.create
proposal = Proposal.create
inv = Invoice.new(client: client, proposal: proposal)
You can also mention the belongs_to relationship as optional, then the presence of the associated object won't be validated
class Invoice < ActiveRecord::Base
belongs_to :client, optional: true
belongs_to :proposal, foreign_key: "prop_id", optional: true
...
With optional: true
inv = Invoice.new
will not give any errors
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
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