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!
Related
I have the following Rails models:
class Project < ApplicationRecord
validates :title, presence: true
validates :project_managers, length: { minimum: 1, message: "Please assign at least one PM" }
has_many :project_assignments, dependent: :destroy
has_many :project_managers, through: :project_assignments, source: :user
end
class User < ApplicationRecord
has_many :project_assignments, dependent: :destroy
has_many :projects, through: :project_assignments
end
class ProjectAssignment < ApplicationRecord
belongs_to :project
belongs_to :user
end
Now I'm trying to add project managers while creating the record:
Project.create!(title: "foobar", project_manager_ids: [1])
But this leads too ActiveRecord::RecordInvalid: Validation failed: Project assignments is invalid
Is there a way to add project managers directly on create?
In your project model, add the following
accepts_nested_attributes_for :project_assignments, allow_destroy: true
In your Project controller, in strong params add the following
def params
params.require(:project).permit(
:id,
:title,
project_assignments_attributes: [
:id,
:user_id,
_destroy
]
)
end
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
Given the following models:
Business
class Business < ActiveRecord::Base
## OWNERS / EMPLOYEES
has_many :business_users, as: :business, dependent: :destroy
has_many :users, through: :business_users
accepts_nested_attributes_for :business_users, allow_destroy: true, reject_if: lambda { |a| a[:user_id].blank? || a[:role].blank? }
end
BusinessUser
class BusinessUser < ActiveRecord::Base
belongs_to :business, polymorphic: true
belongs_to :user
enum role: {:owner => 0, :employee => 1, :admin => 2}
validates_presence_of :user
validates :user, uniqueness: { scope: :business, message: "Each user can only have one role" }
end
User
class User < ActiveRecord::Base
has_many :business_users
has_many :businesses, through: :business_users, source_type: Business, source: :business
end
How can i make sure that each business has at least one business_user with role :owner?
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?
In an attempt to expand on this guide for creating multiple associations with the same table I came up with the following user class
class User < ActiveRecord::Base
has_secure_password
validates_uniqueness_of :email
validates_presence_of :email
validates_presence_of :name
belongs_to :role
has_many :clients, foreign_key: 'rep_id'
has_many :submitted_jobs, class_name: 'WorkOrder', through: :clients
has_many :processing_tasks, class_name: 'WorkOrder', foreign_key: 'processor_id'
def has_role?(role_sym)
role.name.underscore.to_sym == role_sym
end
end
My goal is to be able to refer to submitted jobs and processing tasks separately depending on the type of user. So far the processing tasks part works as I expected and so far I can get the rep from the workorder, but when I attempt something like rep.submitted_jobs I get the following error:
NoMethodError: undefined method `to_sym' for nil:NilClass
from /usr/local/rvm/gems/ruby-2.1.5#rails4/gems/activerecord-4.1.6/lib/active_record/reflection.rb:100:in `_reflect_on_association'
ect...
Clearly the has_many through relationship works differently than I'm expecting, but I'm not even quite sure what to call this type of relationship so I'm at something of a loss for what to look for.
Its probably also worth noting that
RSpec.describe User, type: :model do
it {should validate_uniqueness_of(:email)}
it {should validate_presence_of(:name)}
it {should belong_to(:role)}
it {should have_many(:submitted_jobs)}
it {should have_many(:processing_tasks)}
end
all pass
EDIT:
class Client < ActiveRecord::Base
has_many :contacts
has_many :addresses, through: :contacts
has_many :permits
has_many :work_orders
validates :clientnumber, format: { with: /\A\d{3}\z/ },
length: { is: 3 }
belongs_to :rep, class_name: 'User'
belongs_to :default_processor, class_name: 'User'
end
EDIT 2:
work order associations
class WorkOrder < ActiveRecord::Base
belongs_to :client
belongs_to :project_type
belongs_to :status
belongs_to :labels
belongs_to :contact
belongs_to :processor, class_name: 'User'
has_one :rep, through: :client, class_name: 'User'
has_one :presort_information
has_one :printing_instructions
has_one :production_details
........
end
Typical has_many_through_association look like
To solve this problem, you could write custom association, a method that will give you related work_orders
Inside User model
def submitted_jobs
WorkOrder.joins(:client).where('clients.rep_id = ?', self.id)
end