I have two models: University and Market
A University belongs to a single Market, and a Market can have many universities. For example, An instance of Market like Boston might have University instances like MIT, Harvard, Boston University, etc.
I want to be able to do something like this in console:
University.first.market.name
But I get the following error:
NoMethodError: undefined method `market' for #
I can get the market id from University.first.market_id, but I can't get the name from market.name.
Here is how I have my models set up:
class University < ApplicationRecord
belongs_to :markets
class Market < ApplicationRecord
has_many :universities
end
And here is my schema - I think with the market_id integer column and index correctly implemented (?)
create_table "universities", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "full_name"
t.integer "market_id"
t.index ["market_id"], name: "index_universities_on_market_id"
end
Here's my markets schema:
create_table "markets", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
end
What am I doing incorrectly?
In a one-to-many relationship, the "belongs_to" end must be singular, as below:
class University < ApplicationRecord
belongs_to :market
end
class Market < ApplicationRecord
has_many :universities
end
Let me know if that helps
Related
I've been researching friendship models using roles, custom associations, etc. But I haven't been able to connect my project to the concepts in a clear way.
I want a "User" to be able to create an event I'm calling a "Gather". A User can also attend a Gather created by other Users. By attending a Gather, the "User" can also be a "Gatherer".
The list of Gatherers will technically be considered friends of the "creator". This is how far I've gotten:
Models:
User
Gather
Gatherer (?)
User
class User < ApplicationRecord
has_many :gathers_as_creator,
foreign_key: :creator_id,
class_name: :Gather
has_many :gathers_as_gatherer,
foreign_key: :gatherer_id,
class_name: :Gather
end
Gather
class Gather < ApplicationRecord
belongs_to :creator, class_name: :User
belongs_to :gatherer, class_name: :User
end
My question is, do I need to a join table, such as Gatherer, to allow multiple attendees and then later pull a friend list for the user/creator ?
Gatherer
belongs_to :gather_attendee, class_name: "User"
belongs_to :attended_gather, class_name: "Gather"
Here's what I think that schema would look like:
create_table "gatherers", force: :cascade do |t|
t.bigint "attended_gather_id"
t.bigint "gather_attendee_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["attended_gather_id"], name: "index_gatherers_on_attended_gather_id"
t.index ["gather_attendee_id"], name: "index_gatherers_on_gather_attendee_id"
end
Help, my head is spinning trying to understand the connections and how to proceed.
Previous planning:
Schema:
create_table "activities", force: :cascade do |t|
t.string "a_type"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "gatherers", force: :cascade do |t|
t.bigint "attended_gather_id"
t.bigint "gather_attendee_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["attended_gather_id"], name: "index_gatherers_on_attended_gather_id"
t.index ["gather_attendee_id"], name: "index_gatherers_on_gather_attendee_id"
end
create_table "gathers", force: :cascade do |t|
t.integer "creator_id"
t.integer "activity_id"
t.text "gather_point"
t.boolean "active"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "interest_gathers", force: :cascade do |t|
t.string "gather_id"
t.string "interest_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "interests", force: :cascade do |t|
t.string "i_type"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "img"
t.string "first_name"
t.string "last_name"
t.string "state"
t.string "city"
t.string "bio"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "gatherers", "gathers", column: "attended_gather_id"
add_foreign_key "gatherers", "users", column: "gather_attendee_id"
end
class User < ActiveRecord::Base
has_many :gatherers, foreign_key: gather_attendee_id
has_many :attended_gathers, through: :gatherers
has_many :created_gathers, foreign_key: :creator_id, class_name: "Gather"
end
class Gather < ActiveRecord::Base
has_many :gatherers, foreign_key: :attended_gather_id
has_many :attendees, through: :gatherers, source: :gather_attendee
belongs_to :creator, class_name: "User"
end
class Gatherer < ActiveRecord::Base
belongs_to :gather_attendee, class_name: "User"
belongs_to :attended_gather, class_name: "Gather"
end
The naming here is not great. When naming your models choose nouns as models represent the actual things in your buisness logic - choosing verbs/adverbs makes the names of your assocations very confusing.
class User < ApplicationRecord
has_many :gatherings_as_creator,
class_name: 'Gathering',
foreign_key: :creator_id
has_many :attendences
has_many :gatherings,
through: :attendences
end
# think of this kind of like a ticket to an event
# rails g model Attendence user:references gathering:references
class Attendence < ApplicationRecord
belongs_to :user
belongs_to :gathering
end
# this is the proper noun form of gather
class Gathering < ApplicationRecord
belongs_to :creator,
class_name: 'User'
has_many :attendences
has_many :attendees,
though: :attendences,
class_name: 'User'
end
My question is, do I need to a join table, such as Gatherer, to allow multiple attendees and then later pull a friend list for the user/creator ?
Yes. You always need a join table to create many to many assocations. Gatherer is a pretty confusing name for it though as that's a person who gathers things.
If you want to get users attending Gatherings created by a given user you can do it through:
User.joins(attendences: :groups)
.where(groups: { creator_id: user.id })
You're on the right track.
If I understand what you're looking for correctly, you want a Gather to have many Users and a User to have many Gathers (for the attending piece). So you need a join table like this (this is similar to your gatherers table, but is in a more conventional Rails style):
create_join_table :gathers, :users do |t|
t.index [:gather_id, :user_id]
t.index [:user_id, :gather_id]
end
And then you'd want your User model to be like this:
class User < ApplicationRecord
has_many :gathers_as_creator, foreign_key: :creator_id, class_name: "Gather"
has_and_belongs_to_many :gathers
end
class Gather < ApplicationRecord
belongs_to :creator, class_name: "User"
has_and_belongs_to_many :users
end
(You can change the name of that :users association if you really want, by specifying extra options -- I just like to keep to the Rails defaults as much as I can.)
That should be the bulk of what you need. If you want to pull all the friends of a creator for a specific gather, you would just do gather.users. If you want to pull all of the friends of a creator across all their gathers, that will be:
creator = User.find(1)
friends = User.joins(:gathers).where(gathers: { creator: creator }).all
Three models Professor, Expertise & ExpertisesProfessor (the join table). I would like to use a has_many activerecord structure but when I call Expertise.professors.all I get an error
*NoMethodError (undefined method `professors' for Class:0x000000000a1ddda0) *
I want to be able to call Expertise.professors and Professor.expertise ???
I am comfortable with using HABTM instead of "has_many through" but for my project I prefer to use the the " has_many through " relationship so please if I could get solutions along those lines only if possible .
**professor.rb**
class Professor < ApplicationRecord
has_many :expertise_professors
has_many :expertises, through: :expertise_professors
end
**expertise.rb**
class Expertise < ApplicationRecord
has_many :expertise_professors
has_many :professors, through: :expertise_professors
end
**expertises_professor.rb**
class ExpertisesProfessor < ApplicationRecord
belongs_to :expertise
belongs_to :professor
end
My Schema File
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_12_18_191008) do
create_table "expertises", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "expertises_professors", id: false, force: :cascade do |t|
t.integer "expertise_id", null: false
t.integer "professor_id", null: false
end
create_table "professors", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
Any ideas what I have missed ?
You can not call Expertise.professors. You first need to load the single record or object of the Expertise like
expertise = Expertise.first
And then you can get all professors
expertise.professiors.all
Same way you can get all expertises for specific professor.
Having three models: Datum, Author, and Book .
class Datum < ApplicationRecord
has_many :authors
end
class Book < ApplicationRecord
belongs_to :author
end
class Author < ApplicationRecord
belongs_to :datum
has_many :books, dependent: :destroy
end
For exercise purpose, I wanted to model it that Datum(more general), can have many authors, which can have books.
After creating a datum object and an associated author for it, I could call nameofdatum.authors, but if I added a book to that author, it could not be recognized through nameofdatum.authors.books. Am I having wrong expectations ? (Should this be done with 'through'(an explanation of it would be much appreciated)
(Schema here if needed)
create_table "authors", force: :cascade do |t|
t.string "name"
t.integer "age"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "book_id"
t.integer "datum_id"
t.index ["book_id"], name: "index_authors_on_book_id"
t.index ["datum_id"], name: "index_authors_on_datum_id"
end
create_table "books", force: :cascade do |t|
t.string "name"
t.string "book_type"
t.integer "pages"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "author_id"
t.index ["author_id"], name: "index_books_on_author_id"
end
create_table "data", force: :cascade do |t|
t.string "region"
t.integer "budget"
t.date "aval"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Should this be done with 'through'?
Yes, Datum has_many books through the authors assocition:
class Datum < ApplicationRecord
has_many :authors
has_many :books, through: :authors
end
And the books can be selected via:
Datum.last.books
It's actually selects books using the following query:
SELECT "books".* FROM "books" INNER JOIN "authors" ON "authors"."id" = "books"."author_id" WHERE "authors"."datum_id" = ?
If you want to add a new book through author, you have to assign an author. So you can try:
nameofdatum.author.books.build ....
your codenameofdatum.authors.books, you can't use a plural(author) to add a new book.
Hope to help you.
Currently I have a Group and GroupPeriod that contains the same attributes
create_table "groups", force: :cascade do |t|
t.bigint "company_id"
t.string "name"
t.date "cutoff_date"
t.date "processing_date"
t.integer "working_days"
t.integer "working_hours"
t.integer "status"
t.float "basic_pay"
t.string "type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["company_id"], name: "index_groups_on_company_id"
end
create_table "group_periods", force: :cascade do |t|
t.bigint "company_id"
t.date "start_date"
t.date "end_date"
t.string "type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "group_id"
t.index ["company_id"], name: "index_group_periods_on_company_id"
t.index ["group_id"], name: "index_group_periods_on_group_id"
end
The logic is Group has many GroupPeriods. But then I have different groups; Bill and Pay. So I'm creating an STI for both BillGroup and PayGroup:
class Group < ApplicationRecord
has_many :group_periods
end
class BillGroup < Group
#=> has_many :bill_periods??
end
class PayGroup < Group
#=> has_many :pay_periods??
end
The issue I'm having is that each group will have many PayPeriod or BillPeriod. So I created a GroupPeriod to link
class GroupPeriod < ApplicationRecord
belongs_to :group
end
class BillPeriod < GroupPeriod
#=> belongs_to :bill_group??
end
class PayPeriod < GroupPeriod
#=> belongs_to :pay_group??
end
My question is, how can I ensure through inheritance, I can be flexible that
BillGroup has many BillPeriods;
PayGroup has many PayPeriods;
without overlapping (BillGroup will not see PayPeriod and vice versa) with each other? At the same time, is this a bad practice that I should make them into 2 different tables for each BillGroup and PayGroup?
class Group < ApplicationRecord
has_many :group_periods
end
class Period < ApplicationRecord
belongs_to :group
belongs_to :group_periods, polymorphic: true
end
class BillPeriod < GroupPeriod
has_many :periods, as: :group_periods, dependent: :destroy
end
class PayPeriod < GroupPeriod
has_many :periods, as: :group_periods, dependent: :destroy
end
your model looks something like this , rest depends on your associations.
I am developing a portfolio for my website, I decided to add skills to each portfolio item.
class PortfolioSkill < ApplicationRecord
belongs_to :portfolio
belongs_to :skill
end
class Portfolio < ApplicationRecord
has_many :portfolio_skills
has_many :skills, through: :portfolio_skills
def all_tags=(names)
self.skills = names.split(",").map do |name|
Skill.where(name: name.strip).first_or_create!
end
end
def all_tags
self.skills.map(&:name).join(", ")
end
def remove_skill_tags
PortfolioSkill.where(portfolio_id: id).destroy_all
end
end
create_table "portfolio_skills", force: :cascade do |t|
t.integer "portfolio_id"
t.integer "skill_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["portfolio_id"], name: "index_portfolio_skills_on_portfolio_id"
t.index ["skill_id"], name: "index_portfolio_skills_on_skill_id"
end
create_table "portfolios", force: :cascade do |t|
t.string "name"
t.string "client"
t.date "completed"
t.text "about"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "long_landscape"
t.string "cover"
t.integer "category_id"
t.index ["category_id"], name: "index_portfolios_on_category_id"
end
When I click destroy on the index page I get the
SQLite3::ConstraintException: FOREIGN KEY constraint failed: DELETE FROM "portfolios" WHERE "portfolios"."id" = ?
error. All the associations look right. I used this same pattern for my tags on other models and it worked with no issues. Any help would be great.
You are deleting from portfolios table, but table portfolio_skills has a column referencing it as foreign key. Hence the error.
Trying to delete a parent without checking and deleting its associated children can lead to data inconsistency. This exception is in place to prevent that.
Rails dependent destroy will take care of removing associated children rows while removing a parent.
Try using a dependent destroy:-
class Portfolio < ApplicationRecord
has_many :portfolio_skills, :dependent => :destroy
...
end