Rails has one association - ruby-on-rails

I am working on building a portfolio section in my website right now. I have it set where a portfolio has one category that is associated with it.
class Portfolio < ApplicationRecord
has_one :category
end
I am trying to access the category attribute name in the show view for the portfolio , but I am getting this error.
SQLite3::SQLException: no such column: categories.portfolio_id: SELECT "categories".* FROM "categories" WHERE "categories"."portfolio_id" = ? LIMIT ?
This is what is in the view:
<li><i class="icon_link"></i><span>Category: </span><%= #portfolio.category.name %></li>
I remembering using this syntax before and not having any problems with it. Any help would be great. I tried to find this question on here =, but could not make of them work.

Rails conventional naming for has_one/belongs_to associations is - if you have category_id column in Portfolio then "a portfolio belongs_to a category" and "a category has one portfolio". You need to rewrite your models as:
class Portfolio < ApplicationRecord
belongs_to :category
class Category < ApplicationRecord
has_one :portfolio
After that #portfolio.category.name should work fine.
Update: Possibly "a category has many portfolios", when the models will be:
class Portfolio < ApplicationRecord
belongs_to :category
class Category < ApplicationRecord
has_many :portfolio

Whenever you have an association in rails, there has to be at least two entites (or models) involved in that association. In your particular case, you've associated a Portfolio with a Category via the has_one association. However, you need to specify the association on the Category end as well. So in your Category.rb model class, you need to write:
class Category < ActiveRecord::Base
belongs_to :portfolio
# other stuff
end
There's one more step to ensuring that the association is working properly. You need to make sure that the categories table in your database has a field called portfolio_id. If it does, then you should be good to go! If it doesn't then do the following:
Type rails generate migration addPortfolioIdToCategory in your terminal
Open up the migration file and ensure it looks like this:
def change
add_column :categories, :portfolio_id, :integer
end
Now run rake db:migrate from your terminal
If you reload your server, your problem should be solved!
NOTE
The model that specifies the belongs_to association must have the primary key attribute in the corresponding database table. In your case, if the Category model has the belongs_to association, then the categories database table must have the field titled portfolio_id.

It's complaining portfolio_id doesn't exist on your categories table. Verify your migration file for categories. Given the foreign key is set on categories it should work. Rails has one association

Related

Rails 5 use belongs_to with condition for different tables

I am working on a rails app, with different tables and associations, and trying to find out the best solution for that, don't know yet.
One table/class is called categories
For example
Categories
- Stocks
- Reits
-(...)
One table for each category
Stocks
belongs_to :category
Reits
belongs_to :category
Than i want to create a table called "Dividends", with a condition, don't know if is possible, for example:
Dividends
belongs_to :category
if category_id == 1
belongs_to :stock
end
if category_id == 2
belongs_to :reit
end
With the command:
rails g scaffold dividends category:references stock:references reit:references
But i don't know if it would be confusing, an if maybe would be better create a table for each class, for example:
StocksDividends
belongs_to :category
belongs_to :stock
ReitsDividends
belongs_to :category
belongs_to :reit
Would like some help on thinking about that solution.
just a quick hint - take a look at polymorphic associations:
https://guides.rubyonrails.org/association_basics.html#polymorphic-associations
The mechanics of it put a foreign key in the dividends table along with a type column. The type column tells you what type of record is the parent - the stock or the reit.

Rails Converting a has_many relationship into a has and belongs to many

I have a Rails app with the following relationship:
region.rb
class Region < ActiveRecord::Base
has_many :facilities
end
facility.rb
class Facility < ActiveRecord::Base
belongs_to :region
end
I want to expand functionality a bit so that facilities can belong to more than one region at a time. I believe I can do this with a has_many_through relationship but I'm needing some guidance on converting the existing has_many into a has many through. I understand how to create and wire up the join table, but how would I take existing data and translate it?
So for instance. On a facility object there is region_id, since the facilities can belong to more than one region I'd probably need a region_ids field and shovel the collection of regions into that column which should then populate the other side of the association via the join table. I have this part pretty much figured out as far as moving forward and wiring up the association. But I'm unsure as to how to take existing data and translate it over so the app doesn't break when I change the model association.
Any advice would be greatly appreciated.
I suggest you to always use has_many :through instead of HBTM.
To establish this kind of relation you'll need the following set up:
# region.rb
class Region
has_many :facility_regions
has_many :facilities, through: :facility_regions
end
# facility.rb
class Facility
has_many :facility_regions
has_many :regions, through: :facility_regions
end
# facility_region.rb
class FacilityRegion
belongs_to :facility
belongs_to :region
end
Also, of course, you'll need to create a migration:
rails g migration create_facility_regions facility_id:integer region_id:integer
# in this migration create a uniq index:
add_index :facility_regions, %I(facility_id region_id), name: :facility_region
rake db:migrate
UPD
As to migration from one database state to another one.
I think it should not be a problem.
1) Do not delete the relations you had before (leave has_many :facilities and belongs_to :region in models).
2) When new table is created and new associations added to the classes (which I showed) create a new migration:
rails g migration migrate_database_state
3) Write the script, which will create new records in db (to reflect the current state of things):
ActiveRecord::Base.transaction do
Facility.where.not(region_id: nil).find_each do |facility|
next if FacilityRegion.find_by(falicity_id: facility.id, region_id: facility.region_id)
FacilityRegion.create!(facility_id: facility.id, region_id: facility.region_id)
end
end
4) Put this script into last created migration and run it (or in console without migration, effect would be the same).
5) After script is successfully run, create new migration in which you delete region_id from facilities table and remove these associations definitions (has_many :facilities and belongs_to :region) from models.
It must be it. I might have made some typos or so, make sure I did not miss anything and
You need to add another model, a "middle guy" called FacilityRegion.rb, like this:
facility.rb
class Facility < ActiveRecord::Base
has_many :falicity_regions
has_many :regions, through: falicity_regions
end
facility_region.rb
class FacilityRegion < ActiveRecord::Base
belongs_to :region
belongs_to :facility
end
region.rb
class Region < ActiveRecord::Base
has_many :falicity_regions
has_many :facilities, through: falicity_regions
end
If you want to use belongs_and_has_many relationship, you need to:
rails g migration CreateJoinTableRegionsFacilities regions facilities
Then,
rake db:migrate
Now, your relationships should be:
Region.rb:
class Region < ApplicationRecord
has_and_belongs_to_many :facilities
end
Facility.rb
class Facility < ApplicationRecord
has_and_belongs_to_many :regions
end
In order to populate the new join table, you will need to in your console:
Region.all.find_each do |r|
Facility.where(region_id: r.id).find_each do |f|
r.facilities << f
end
end
Now, you can either leave the columns region_id and facility_id in Facility and Region table, respectively, or you can create a migration to delete it.

Setting up a summary table (Rails Associations)

I am having trouble wrapping my head around what I'd consider a more complex association and was hoping someone could help point me in the right direction.
I have four models:
user
profile
feed_group
feed
All 4 of these obviously have their own fields and data stored within. My goal is to have a summary database table called "user_detail" (I am open to calling it something different if Rails has its conventions), which has the following 4 fields:
user_id
profile_id
feed_group_id
feed_id
What would my model associations look like?
Thanks.
You can create the model through a migration like this:
rails g model UserDetail user:references profile:references feed_group:references feed:references
Within the created file models/user_detail.rb you will find the relations:
class UserDetail < ActiveRecord::Base
belongs_to :user
belongs_to :profile
belongs_to :feed_group
belongs_to :feed
end
Also, add references to UserDetail in all the referenced models, eg. in models/user.rb add has_many :user_details, etc.
As I read your question, that's all you need.

Adding an associated Table to an Existing Table (Book > Revision)

I have the following models:
class Instance < ActiveRecord::Base
has_many :users
has_many :books
end
class User < ActiveRecord::Base
belongs_to :instance
end
class Book < ActiveRecord::Base
belongs_to :instance
end
I now want to add a BookRevision table, that has the following columns
(id, user_id, version # (auto increment), diff (old book copy),
timestamps)
Logic:
When a book is Created, a record is
added to the BookRevision table, so
we know who created the book in the
first place
When a book is Updated, a record is added with the user_id (could be
a different user), and a new version
, and the old book text, to serve as an archive.
Given that I have the Instance, User, Book table implement in my rails
add, are these the correct steps to make the above come to life?
- Add a migration for the BookRevision table....
rails generate migration AddTableBookRevision user_id:integer
version:integer diff:text
- Then update the models as follows:
class Instance < ActiveRecord::Base
has_many :users
has_many :books
end
class User < ActiveRecord::Base
belongs_to :instance
end
class Book < ActiveRecord::Base
belongs_to :instance
has_many :BookRevisions
end
class BookRevision < ActiveRecord::Base
belongs_to :Book
end
Then in my controller, when adding a new book? Right now I have:
#book = Book.create(params[:book].merge(:instance_id =>
current_user.instance_id))
How do I update that to account for the BookRevision association?
Thanks for helping me out!
You might want to check out something like acts_as_versioned instead of rolling your own. This works in the fashion you've described here, where modifications are saved into a separate but related table.
Keep in mind you will have to apply migrations to your Book and BookRevision table in parallel from that point forward. They must be schema compatible for revisioning to work.
I have built a version tracking system of this sort that used serialized models to avoid having to maintain migrations, as the intent was to preserve the exact state of the model regardless of future modifications via migrations. This has the disadvantage of not being able to roll back to an arbitrary older version because there may be a schema mis-match.

rails models - two tables have the same primary and foreign key fields

I am using an existing database with a rails app.
I can't change the table or column names.
Lets say table one is "invoices" and table 2 is "orders"
they both have a primary key that is called the same thing, lets say "order_id"
invoices can find its order by looking at the primary key "order_id" in the orders table.
Vice versa for an order finding its invoice.
Invoices can have but not always have one order (you might be invoiced for something besides an order, like a "work_order" which would be found by looking for the "order_id" in the primary key position of the "work_orders" table. So invoices might have a work_order or an order.
orders always has an invoice
work_orders always has an invoice
Im trying to figure out the classes in the models.
Do you set the primary and foreign keys to the same thing? What about belongs_to? The way this DB is set up, nothing really belongs to anything, they just reference each other by this same value "order_id". Would it be like this?
class Invoice < ActiveRecord::Base
set_primary_key(:order_id)
set_foreign_key(:order_id)
end
-- snip --
class Order < ActiveRecord::Base
set_primary_key(:order_id)
set_foreign_key(:order_id)
end
... And the same for a work order.
class WorkOrder < ActiveRecord::Base
set_primary_key(:order_id)
set_foreign_key(:order_id)
end
Is this correct? It seems to trashy of a way to do it but this DB is terrible.
What about all the belongs_to stuff?
Let me know if I have left anything out.
Thanks!
I believe the answer could be:
class Order < ActiveRecord::Base
set_primary_key(:order_id)
belongs_to :invoice, :foreign_key => :order_id
end
class WorkOrder < ActiveRecord::Base
set_primary_key(:order_id)
belongs_to :invoice, :foreign_key => :order_id
end
class Invoice < ActiveRecord::Base
set_primary_key(:order_id)
has_one :work_order
has_one :order
end
Although I'm not really sure your primary key can also be a foreign key (I'm new to Rails too)

Resources