Database field refuses to be an integer - Rails/Postgres - ruby-on-rails

I am creating a join table, broadly following the Railscast: http://railscasts.com/episodes/17-habtm-checkboxes-revised?view=asciicast
I cannot set the has_many records on the object and get the following error:
2.0.0p353 :012 > invoice.fly_ids
(0.9ms) SELECT "flies".id FROM "flies" INNER JOIN "categorizations" ON "flies"."id" = "categorizations"."fly_id" WHERE "categorizations"."invoice_id" = 1
ActiveRecord::StatementInvalid: PG::Error: ERROR: operator does not exist: integer = character varying
LINE 1: ...ies" INNER JOIN "categorizations" ON "flies"."id" = "categor...
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
: SELECT "flies".id FROM "flies" INNER JOIN "categorizations" ON "flies"."id" = "categorizations"."fly_id" WHERE "categorizations"."invoice_id" = 1
I can't get it to work and I think it is because the 'categorizations' table is using varchar instead of integer.
My migration file looks like:
class CreateCategorizations < ActiveRecord::Migration
def change
create_table :categorizations do |t|
t.integer :user_id
t.integer :fly_id
t.timestamps
add_index :categorizations, :user_id
add_index :categorizations, :fly_id
end
end
end
However, when I peek at the database table that is created, :user_id and :fly_id are both varchar
Why is it creating these fields as varchar when I am specifying integer in my migration file?
(even if I get this to work, it might not fix the issue...)
Edit:
User Model:
class User < ActiveRecord::Base
....
has_many :invoices
....
end
Invoice Model:
class Invoice < ActiveRecord::Base
attr_accessible :active
validates :user_id, presence: true
belongs_to :user
has_many :categorizations
has_many :flies, through: :categorizations
end
Invoice migration:
class CreateInvoices < ActiveRecord::Migration
def change
create_table :invoices do |t|
t.boolean :active
t.integer :user_id
t.timestamps
end
add_index :invoices, :user_id
end
end
Categorization Model:
class Categorization < ActiveRecord::Base
attr_accessible :fly_id, :user_id
belongs_to :invoice
belongs_to :fly
end
Categorization migration:
class CreateCategorizations < ActiveRecord::Migration
def change
create_table :categorizations do |t|
t.integer :user_id
t.integer :fly_id
t.timestamps
add_index :categorizations, :user_id
add_index :categorizations, :fly_id
end
end
end
Fly Model:
class Fly < ActiveRecord::Base
attr_accessible :description, :name
validates :description, :name, presence: true
has_many :categorizations
has_many :invoices, through: :categorizations
end
Fly migration:
class CreateFlies < ActiveRecord::Migration
def change
create_table :flies do |t|
t.string :name
t.string :description
t.timestamps
end
end
end

HABTM
If you're using HABTM, your tables don't need any primary keys (id):
create_table :invoices_flies, :id => false do |t|
t.references :user
t.references :flies
end
has_many :through
If you're using has_many :through, your table will use a primary key, as it will be a model of its own:
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :categorizations
has_many :flies, through: :categorizations
end
#app/models/fly.rb
Class Fly < ActiveRecord::Base
has_many :categorizations
has_many :users, through: :categorizations
end
#app/models/categorization.rb
Class Categorization < ActiveRecord::Base
belongs_to :fly
belongs_to :user
end
create_table :categorizations do |t|
t.integer :user_id
t.integer :fly_id
end
I think your issue will probably be with the structure of your associations - can you give us some info on how you've set them up?

Related

rails - can't save data in associated models in RoR

I have 2 Models with association has_many along with cascade property between them.
class ServicesBrandDetail < ApplicationRecord
has_many :services_brands, foreign_key: "brand_id", dependent: :delete_all
end
class ServicesBrand < ApplicationRecord
belongs_to :services_brand_details, foreign_key: "brand_id",
end
Migration for both files
class CreateServicesBrandDetails < ActiveRecord::Migration[6.1]
def change
create_table :services_brand_details do |t|
t.string :brand
t.string :mail_list
t.string :cc_list
t.timestamps
end
end
end
class CreateServicesBrands < ActiveRecord::Migration[6.1]
def change
create_table :services_brands do |t|
t.string :warehouse
t.references :brand, null: false, foreign_key: {to_table: :services_brand_details}
t.timestamps
end
end
end
Now I was able to create and save data in from ServicesBrandDetails model. but the Problem is when i create record from ServiceBrand It created record perfectly but i was not able to store data in DB.
record = ServicesBrandDetail.create(:brand => "a", :mail_list => 'abc#mail.com', :cc_list => 'def#mail.com')
record.save
Record successfully stored in DB.
child = record.services_brands.new(:warehouse => "in") <-- record was created successfully.
child.save
it give me error
C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/activerecord-6.1.5/lib/active_record/inheritance.rb:237:in `compute_type': uninitialized constant ServicesBrand::ServicesBrandDetails (NameError)
Please follow proper Naming convention
This article might help - https://www.bigbinary.com/learn-rubyonrails-book/summarizing-rails-naming-conventions
In ServiceBrand Model
class ServiceBrand < ApplicationRecord
belongs_to :brand, class_name: 'ServiceBrandDetail'
end
belongs_to should be foreign key name i.e brand in your case
You can delete existing models and tables from your codebase and try below one. (I've tested)
class ServiceBrandDetail < ApplicationRecord
has_many :service_brands, foreign_key: :brand_id, dependent: :delete_all
end
class ServiceBrand < ApplicationRecord
belongs_to :brand, class_name: 'ServiceBrandDetail'
end
Migration for both files
class CreateServiceBrandDetails < ActiveRecord::Migration[6.1]
def change
create_table :service_brand_details do |t|
t.string :brand
t.string :mail_list
t.string :cc_list
t.timestamps
end
end
end
class CreateServiceBrands < ActiveRecord::Migration[6.1]
def change
create_table :service_brands do |t|
t.string :warehouse
t.references :brand, null: false, foreign_key: {to_table: :service_brand_details}
t.timestamps
end
end
end
Then try to create model objects which you tried in your question. It will work 👍🏽
In your model ServicesBrand you have to use singular association name for belongs_to
Change this belongs_to :services_brand_details to this belongs_to :services_brand_detail
class ServicesBrand < ApplicationRecord
belongs_to :services_brand_detail, foreign_key: "brand_id"
end

has_one and belong_to associations with the same table

I wonder is it ok to have like 2 associations with the same table. For example:
class CreateTeams < ActiveRecord::Migration[6.0]
def change
create_table :teams do |t|
t.string :name, null: false
t.references :manager, foreign_key: { to_table: 'users' }
end
end
end
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name, null: false
t.references :team
end
end
end
class Team < ApplicationRecord
has_many :users
belongs_to :manager, class_name: 'User', foreign_key: 'manager_id'
end
class User < ApplicationRecord
belongs_to :team
has_one :child_team, class_name: 'Team' # bad name, but just for example
end
Or it would be better to create a join table with team_id, user_id, and member_type?
Ruby/Rails versions do not matter but let's assume Ruby is 2.7.0 and Rails is 6.0.0
From a technical point of view - that's perfectly fine, but be careful with possible foreign key loop in the future.
This is more a question of architecture and your predictions of how system will evolve. A many-to-many relation with a explicit join model is more flexible. For example:
does manager always belong to the team? with a join table it's easier to fetch "all users from the team, no matter the role" or "all the teams a person has relation to, also no matter the role"
if there will be other roles or multiple people at same position - join table will also come handy
What you have seems fine.
Though a join table would be more flexible to provide more roles. This also avoids having a circular dependency in setting up teams and users.
class CreateTeams < ActiveRecord::Migration[6.0]
def change
create_table :teams do |t|
t.string :name, null: false
t.timestamps
end
end
end
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name, null: false
t.timestamps
end
end
end
class CreateTeamMembers < ActiveRecord::Migration[6.0]
def change
create_table :team_members do |t|
t.belongs_to :user, null: false, foreign_key: true
t.belongs_to :team, null: false, foreign_key: true
t.integer :role, null: false
t.timestamps
# Enforce one manager per team
t.index [:team_id],
name: :one_manager,
unique: true,
where: "role = 0"
end
end
end
class TeamMember < ApplicationRecord
enum role: { manager: 0, player: 1, fan: 2 }
belongs_to :user
belongs_to :team
end
class Team < ApplicationRecord
has_many :users, through: :team_members
has_many :team_members, dependent: :destroy
has_one :manager, -> { where(role: :manager) }, class_name: "TeamMember"
has_many :players, -> { where(role: :player) }, class_name: "TeamMember"
has_many :fans, -> { where(role: :fan) }, class_name: "TeamMember"
end
class User < ApplicationRecord
has_many :team_memberships, dependent: :destroy, class_name: "TeamMember"
has_many :teams, through: :team_memberships
end
You could even potentially take advantage of single table inheritance to differentiate your users by their role.
This is something you could migrate to later if necessary.

How to set up multiple belongs_to and has_many relations in Rails::ActiveRecord?

I am trying to create a movies app where a movie can have multiple categories and a category can have multiple movies. I want to access categories of a movie like this:
aMovie.categories
I expect this query to return ActiveRecord_Associations_CollectionProxy
and the reverse also applies
aCategory.movies
Below are my models and migrations
class Movie < ApplicationRecord
validates :name, presence: true
end
class Category < ApplicationRecord
validates :name, presence: true
end
class CreateMovies < ActiveRecord::Migration[5.2]
def change
create_table :movies do |t|
t.string :name
t.text :description
t.integer :year
t.float :rating
t.timestamps
end
end
end
def change
create_table :categories do |t|
t.string :name
t.timestamps
end
end
end
How should i adjust my migrations and models?
Thanks in advance
You should create intermediate join table
Movie_categories
belongs_to :movie
belongs_to :category
Movie
has_many :movie_categories
has_many :categories, through: :movie_categories
Category
has_many :movie_categories
has_many :movies, through: :movie_categories
You can refer to has_many through relationship in https://guides.rubyonrails.org/association_basics.html

Rails 4 HABTM has_many :through

Team, looking for some help for a very specific (newbie) situation on a Rails 4 association.
We have 3 models:
class Brand < ActiveRecord::Base
has_many :lines, dependent: :destroy
has_many :products, through: :lines, dependent: :destroy
end
class Line < ActiveRecord::Base
belongs_to :brand
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :lines
has_many :brands, through: :lines
end
This configuration works well when trying to check for Products under specific Brand (or Line) and viceversa: different Brands (or Lines) available for a specific Product. However, when it comes to delete/destroy there is an issue. We are getting this Rspec error:
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection:
Cannot modify association 'Brand#products' because the source reflection
class 'Product' is associated to 'Line' via :has_and_belongs_to_many.
We have made research on this exception, checked for Rails API, with no luck, examples found are showing a different model configuration. What's missing on this approach?
Appreciate your help guys!
In my opinion, it should be something like this:
class Brand < ActiveRecord::Base
has_many :lines, dependent: :destroy
has_many :products, through: :lines, dependent: :destroy
end
class Line < ActiveRecord::Base
belongs_to :brand
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
belongs_to :brand, through: :line
has_and_belongs_to_many :lines
end
And in migrations:
create_table :brands , force: true do |t|
t.string :name
...
t.timestamps null: false
end
create_table :lines , force: true do |t|
t.string :name
t.belongs_to :brand
...
t.timestamps null: false
end
create_table :products , force: true do |t|
t.string :name
...
t.timestamps null: false
end
create_table :line_products, force: true, id: false do |t|
t.belongs_to :line, index: true
t.belongs_to :product, index: true
end
I hope it will help.

rails undefined method 'has_many'

I'm making a database:
class CreateUsers < ActiveRecord::Migration
def change
has_many :listings, :dependent => :restrict #won't delete if listings exist
has_many :transactions, :dependent => :restrict #won't del if trans exist
create_table :users do |t|
t.integer :key #it's hard to use string as primary
t.string :identifier_url
t.string :username
t.integer :rating
t.timestamps
end
end
end
and
class CreateListings < ActiveRecord::Migration
def change
has_one :book
belongs_to :transaction
belongs_to :user
create_table :listings do |t|
t.integer :key
t.integer :condition
t.decimal :price
t.timestamps
end
end
end
I can't find anything anywhere on this so I'm guessing it's something really basic.
The associations (has_many, belongs_to etc...) should be declared in the model, not in the migration.
This is a good read to start with migrations:
http://guides.rubyonrails.org/migrations.html
And this one for associations:
http://guides.rubyonrails.org/association_basics.html
Put your association in the model
class Member < ActiveRecord::Base
has_many :listings, :dependent => :restrict
has_many :transactions, :dependent => :restrict
end
You don't have to declare the associations in the migration, but in the models!

Resources