Issue with Creating Model Relationships in Rails - ruby-on-rails

I have 3 main models in my program; User, Country, City.
User and Country have a many-to-many relationship, which I join with
a Trip model.
User and City have a many-to-many relationship, which I join with a
Visit model.
Country and City have a one-to-many relationship.
When I run rails db:migrate I get no errors and all appears well, however when I try and seed data or go into the console to create a City it will not save. Any Users or Countries will be successfully created and I am able to build relationships between them.
See my models below.
user.rb
class User < ApplicationRecord
before_save { self.email = email.downcase }
#attr_accessible :user_name, :email
validates_confirmation_of :password
has_secure_password
validates :user_name, presence: true, length: { maximum: 25 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }
validates :password, presence: true, confirmation: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
has_many :trips
has_many :visits
has_many :countries, through: :trips
has_many :cities, through: :visits
end
city.rb
class City < ApplicationRecord
has_many :visits
has_many :users, through: :visits
belongs_to :country
end
country.rb
class Country < ApplicationRecord
has_many :trips
has_many :cities
has_many :users, through: :trips
end
trip.rb
class Trip < ApplicationRecord
belongs_to :country
belongs_to :user
end
visit.rb
class Visit < ApplicationRecord
belongs_to :city
belongs_to :user
end
Originally I did not even have the Visit model, I just joined both many-to-many relationships through the Trip model. However, in trying to solve the issue I separated it.
Any help with this issue would be appreciated. If you need more information just let me know.

I would start by modeling it properly:
class City < ApplicationRecord
has_many :visits
has_many :users, through: :visits
belongs_to :country
end
class Country < ApplicationRecord
has_many :trips
has_many :cities
has_many :users, through: :trips
end
class Trip < ApplicationRecord
belongs_to :country
belongs_to :user
has_many :visits
has_many :cities, through: :visits
end
class Visit < ApplicationRecord
belongs_to :trip
belongs_to :city
has_one :country, through: :city
has_one :user, through: :trip
end
# added
class User < ApplicationRecord
# ...
has_many :trips
has_many :visits, through: :trips
has_many :countries, through: :trips
has_many :cities, through: :visits
end
This creates a one to many association between Trip and Visit and avoids duplicating the user_id foreign key on both.
In Rails 5 one of the major changes is that belongs_to associations are non-optional by default.
So if you attempt to create a city without a country the validations will fail. But if you create the city from an existing record:
Country.first.cities.create!(name: 'Toronto')
Or pass a record:
City.create!(name: 'Toronto', country: Country.first)
The validation will pass.
You can also set the association as optional which is the behaviour in Rails 4:
class City < ApplicationRecord
has_many :visits
has_many :users, through: :visits
belongs_to :country, optional: true
end

as city has_many users through visits, then you have to declare has_many :visits inside city model below is sample code probably can help you problem
class City < ApplicationRecord
has_many :visits
has_many :users, through: :visits
belongs_to :country
end
when you create city in your console, make sure you gave country_id as in belongs_to country, here is a sample code:
#city = City.new
#city.country = Country.first
# #city.name = .... # your seed code
#city.save

Related

ActiveRecord::HasManyThroughAssociationNotFoundError cannot get the posts for each user for some reason Rails

In the rails console I am inputing User.first.posts and i am getting this error ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :likes in model User
from
USER MODEL
class User < ApplicationRecord
has_secure_password
has_many :posts
has_many :comments
has_many :posts, through: :likes
validates :name, presence: true, uniqueness: true
end
POSTS MODEL
class Post < ApplicationRecord
belongs_to :user
has_many :comments
has_many :likes
has_many :users, through: :likes
def number_of_comments
comments.length
end
def number_of_likes
likes.length
end
end
LIKES MODEL
class Like < ApplicationRecord
belongs_to :user
belongs_to :post
end
Im only showing you the likes model because its popping up in the error but i just want all the posts for each user so if i type User.first.posts all their posts will show up
You need to actually define the likes assocation that you're defining the indirect assocation through:
class User < ApplicationRecord
# ...
has_many :likes # this was missing
has_many :posts, through: :likes
end
But you also have two different assocations between users and posts that are sharing the same name - you actually need something more like:
class User < ApplicationRecord
has_many :posts # posts that this user has created
has_many :likes
has_many :liked_posts,
through: :likes,
source: :post
end
There's seems to be a typo in your User model. There are 2 associations for posts.
class User < ApplicationRecord
has_secure_password
has_many :posts
has_many :comments
# has_many :posts, through: :likes # <-- this i think is the culprit.
# it should be like this:
has_many :likes, through: :posts
validates :name, presence: true, uniqueness: true
end

Trying to match Products and Requests through Categories

I'm making a rails marketplace app for uni where Users can be matched with specific products based on their request.
Users can list products that have specific categories.
Users can also list Requests where they can specify what products they're looking and their categories.
The aim is to match the request to a particular product based on the matching categories
Here are my models
class Product < ApplicationRecord
belongs_to :user
has_many_attached :images, dependent: :destroy
has_many :product_categories
has_many :categories, through: :product_categories
validates :user_id, presence: true
end
class Category < ApplicationRecord
has_many :product_categories
has_many :products, through: :product_categories
validates :name, presence: true, length: { minimum: 3, maximum: 25}
validates_uniqueness_of :name
end
class ProductCategory < ApplicationRecord
belongs_to :product
belongs_to :category
end
class Request < ApplicationRecord
belongs_to :user
has_many_attached :images, dependent: :destroy
has_many :request_categories
has_many :categories, through: :request_categories
validates :user_id, presence: true
end
class RequestCategory < ApplicationRecord
belongs_to :request
belongs_to :category
end
I was thinking of creating a new model called Match to bring together the product and categories or is it easier to match it in the request?
In my mind, your new Match class would essentially be a join table for a has_many :through association. Assuming that you're implementing an asynchronous worker (e.g. Sidekiq / ActiveJob) to go through and make "matches", you'll want to connect matches to a particular Request, and likely store some meta-data (has the user seen the Match yet? Have they rejected it?)
So, I'd probably generate a Match class like this:
rails g model Match seen_at:datetime deleted_at:datetime request:references product:references
And set up the associations as follows:
class Match < ApplicationRecord
belongs_to :request
belongs_to :product
end
class Request < ApplicationRecord
belongs_to :user
has_many_attached :images, dependent: :destroy
has_many :request_categories
has_many :categories, through: :request_categories
has_many :matches
has_many :products, through: :matches
validates :user_id, presence: true
end
class Product < ApplicationRecord
belongs_to :user
has_many_attached :images, dependent: :destroy
has_many :product_categories
has_many :categories, through: :product_categories
has_many :matches
has_many :requests, through: :matches
validates :user_id, presence: true
end
Also, you'll likely want to add the Request has_many :through to your Category model (I think you forgot that one):
class Category < ApplicationRecord
has_many :product_categories
has_many :products, through: :product_categories
has_many :request_categories
has_many :requests, through: :request_categories
validates :name, presence: true, length: { minimum: 3, maximum: 25}
validates_uniqueness_of :name
end
The big part of the job is working out how to have your app periodically look for matches - you may want to start with the Active Job Basics documentation.

belongs_to of associated has_many through not being recognized

I have three models making up a basic has_many through relationship:
class Booking < ApplicationRecord
validates_presence_of :user, :ride, :role, :required_seats
belongs_to :user
belongs_to :ride
end
class Ride < ApplicationRecord
validates_presence_of :origin, :destination, :leave_at, :arrive_at, :price, :seats
has_many :bookings, dependent: :destroy
has_many :users, through: :bookings
accepts_nested_attributes_for :bookings, :allow_destroy => true
end
class User < ApplicationRecord
has_secure_password
validates_presence_of :first_name, :last_name, :email, :password_digest
has_many :bookings, dependent: :destroy
has_many :rides, through: :bookings
accepts_nested_attributes_for :bookings, :allow_destroy => true
end
When running a model spec below:
RSpec.describe Booking, type: :model do
it { should belong_to(:users) }
it { should belong_to(:rides) }
it returns
Failure/Error: it { should belong_to(:users) }
Expected Booking to have a belongs_to association called users (no association called users)
Failure/Error: it { should belong_to(:rides) }
Expected Booking to have a belongs_to association called rides (no association called rides)
The belongs_to association has clearly been made in the join model `bookings', yet it is not being recognized in the model.
The bookings table has a user_id and ride_id column, with foreign keys assigned to their respective tables.
I've been stuck on this for a week now, any help would be appreciated as to why this could be happening!
as arieljuod rightfully pointed out, the models in my spec should have been singular instead of plural.
RSpec.describe Booking, type: :model do
it { should belong_to(:user) }
it { should belong_to(:ride) }
Thanks!

Cascade of deletes ActiveRecord

How can add a cascade of deletes that will remove Profile, TodoList, and TodoItem rows for any User removed.
User Model:
class User < ActiveRecord::Base
has_one :profile
has_many :todo_lists
has_many :todo_items, through: :todo_lists, source: :todo_items
validates :username, presence: true
end
Profile Model:
class Profile < ActiveRecord::Base
belongs_to :user
validates :first_name, presence: true
validates :last_name, presence: true
validates :gender, inclusion: %w(male female)
validate :first_and_last
validate :male_Sue
def first_and_last
if (first_name.nil? and last_name.nil?)
errors.add(:base, "Specify a first or a last.")
end
end
def male_Sue
if (first_name == "Sue" and gender == "male")
errors.add(:base, "we are prevent male by name Sue.")
end
end
end
TodoList Model:
class TodoList < ActiveRecord::Base
belongs_to :user
has_many :todo_items, dependent: :destroy
default_scope { order :list_due_date }
end
TodoItem Model:
class TodoItem < ActiveRecord::Base
belongs_to :todo_list
default_scope {order :due_date }
end
Thanks, Michael.
I guess adding dependent: :destroy will do.
#user.rb
class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
has_many :todo_lists, dependent: :destroy
has_many :todo_items, through: :todo_lists, source: :todo_items, dependent: :destroy
validates :username, presence: true
end
From the docs:
has_many, has_one and belongs_to associations support the :dependent option. This allows you to specify that associated records should be deleted when the owner is deleted
By using dependent: :destroy on your association in the User class, anytime you destroy a User, all associated objects to that instance gets destroyed as well.
You can check this documentation for more information.

AcriveRecord relationship validation between two or more belongs_to associations

I'm wondering if there is a cleaner way to validate multiple relationships in rails. I don't mean validating an association, rather ensuring relationship integrity between two or more belongs_to associations. Here is some code as an example:
class User < ActiveRecord::Base
has_many :products, inverse_of: :user
has_many :purchases, inverse_of: :user
end
class Purchase < ActiveRecord::Base
belongs_to :user, inverse_of: :purchases
has_many :products, inverse_of: :purchase
end
class Product < ActiveRecord::Base
belongs_to :user, inverse_of: :products
belongs_to :purchase, inverse_of: :products
validates :user, :purchase, presence: true
validate :purchase_user
private
def purchase_user
errors.add(:purchase, 'purchase user does not match user') if user != purchase.user
end
end
The purchase_user validation method here checks that the user is the same as purchase.user and adds an error if they are not.
I could change it to use :inclusion like so:
validates :purchase, inclusion: { in: proc { |record| record.user.purchases } }
But that seems even more inefficient, any suggestions?

Resources