I'm having trouble implementing polymorphism in between my Rails models.
The intended structure is that I have an object/model called a 'Test'. Each Test has multiple Scenarios and multiple Runs. Each Scenario and each Run has multiple Events. On my 'show' view for Tests, I want to have a list of all events associated with it (through the Scenarios/Runs).
What I have as class definitions:
class Test < ActiveRecord::Base
has_many :scenarios
has_many :runs
end
class Scenario < ActiveRecord::Base
belongs_to :test
has_many :events, :as => :eventhaver
end
class Run < ActiveRecord::Base
belongs_to :test
has_many :events, :as => :eventhaver
end
class Event < ActiveRecord::Base
belongs_to :eventhaver, :polymorphic => true
end
The database is fully set up and contains a selection of records - 1 Test with 4 Scenarios and 2 Runs, each of which has 1 or 2 events. But when I try to go to the show view for the Test:
ActiveRecord::StatementInvalid in Tests#show
Mysql2::Error: Unknown column 'events.eventhaver_id' in 'where clause': SELECT events.* FROM events WHERE events.eventhaver_id = 1 AND events.eventhaver_type = 'Scenario'
This tells me that something isn't working with the polymorphism definitions, because it is looking for the abstract class/model 'eventhaver' instead of the child classes 'Scenario' and 'Run'. What else do I have to do so that Rails makes the connection and looks for either scenario_id or run_id?
You need to have a integer column eventhaver_id and a string column eventhaver_type in your events table.
The error tells you, that at least the column eventhaver_id is missing.
If you have the coloumns scenario_id and run_id in your events table, you can delete them. Polymorphic associations only use the *abstract*_id,*abstract*_type interface.
See: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Edit:
If you can't change your database you can use :has_many, :through plus a events method to access all event belonging to a test.
class Test < ActiveRecord::Base
has_many :scenarios
has_many :runs
has_many :scenario_events, through: :scenarios, source: :events
has_many :run_events, through: :runs, source: :events
def events
scenario_events + run_events
end
end
class Scenario < ActiveRecord::Base
belongs_to :test
has_many :events
end
class Run < ActiveRecord::Base
belongs_to :test
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :run
belongs_to :scenario
end
Related
Running rails 6.0.3.2 / ruby 2.7.1
I have a has_many through-relationship that isn't behaving as expected.
My models look like this:
class Item < ApplicationRecord
has_many :item_permissions
has_many :users, :through => :item_permissions
end
class User < ApplicationRecord
has_many :item_permissions
has_many :items, :through => :item_permissions
end
class ItemPermission < ApplicationRecord
belongs_to :items
belongs_to :users
end
Now I want to retrieve all Items that a certain User has permission to:
u = User.find(1)
u.items
gives me an error:
NameError (uninitialized constant User::Items)
I can get the permission entries with
u.item_permissions
Is there any way to retrieve the items for a certain user or also the other way round receiving all users that are linked to a specific item?
The mistake is in your belongs_to declarations, in both your are using the plural word instead of the singular one to reference the associated model. Try with:
class ItemPermission < ApplicationRecord
belongs_to :item # :item instead of :items
belongs_to :user # :user instead of :users
end
More information here.
I have a model User and a model Group, between the two I want to create a many-to-many association with a join-table, using through.
user.rb:
class User < ActiveRecord::Base
has_many :groups_user
has_many :groups, :through => :groups_user
end
group.rb:
class Group < ActiveRecord::Base
has_many :groups_user
has_many :users, :through => :groups_user
end
groups_user.rb
class GroupsUser < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
I initially tried to do the same thing naming the joining model class GroupsUsers in groups_users.rb but I got an uninitialized constant error unless I used :class_name to specify the class name.
My question is: What is the logic behind pluralizing the first name, but not the second? The association is many-to-many so both models are on equal footing. In this case group comes first merely because of the lexical order. So why pluralize one and not the other? This makes no sense to me.
The more conventional naming approach would be:
#user.rb:
class User < ActiveRecord::Base
has_many :group_users
has_many :groups, :through => :group_users
end
#group.rb:
class Group < ActiveRecord::Base
has_many :group_users
has_many :users, :through => :group_users
end
#group_user.rb
class GroupUser < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
Conventionally, the model has the singular form. So GroupsUser, not GroupsUsers. But, an instance of the join model has only one Group and one User, So, GroupUser, not GroupsUser.
If you look at the The has_many :through Association section of the Active Record Associations guide, for instance, you will see that the join model (Appointment) is singular. This is the pattern you want to follow.
If you decide to do things unconventionally, then rails needs you to help it - for instance by requiring that you specify class_name.
I have the following active record models:
class Jobs
has_many :leads
end
class Lead
has_many :messages, through :notification
end
class Notification
has_many :messages
end
class Message
# has a type column (varchar) containing either: email, sms
end
I am unsure about how to find all jobs which through its associations have messages which are not of type sms. Is this even possible to do?
You could join your tables and set a where clause in order to filter those messages that are not sms.
May be something like this:
Jobs.joins(leads: [{ notifications: :messages }]).where("type <> 'sms'")
Check https://guides.rubyonrails.org/active_record_querying.html#joining-tables for more information.
As Bruno and Jedi pointed out, something like this may work:
Job.joins(leads: [{ notifications: :message }]).where.not(messages: {msg_type: 'sms'})
Note this uses singular model name for Job (not Jobs). And associations like:
class Job < ApplicationRecord
has_many :leads
end
class Lead < ApplicationRecord
belongs_to :job
has_many :notifications
has_many :messages, through: :notifications
end
class Notification < ApplicationRecord
belongs_to :lead
belongs_to :message
end
class Message < ApplicationRecord
has_many :notifications
has_many :leads, through: :notifications
end
Not clear from your question whether you have all those associations set properly.
A bit hard to explain with a title.
I am making a test grades app with Ruby on Rails, and can't figure out the best ActiveRecord Association setup.
Ideally: there are many Users, and there are many Tests. I need to store the each User's Scores from each Test. Right now I have this:
class User < ActiveRecord::Base
has_many :tests
has_many :scores, :through => :tests
end
class Test < ActiveRecord::Base
has_many :scores
end
class Scores < ActiveRecord::Base
belongs_to :users
belongs_to :tests
end
Doesn't seem right though. I would like to know the convention for this. Thanks.
I would use three tables/models:
test
user (or, possibly better, student)
test_result, having a test_id, user_id and score
Corresponding Ruby code:
class User < ActiveRecord::Base
has_many :tests, through: :test_results
end
class Test < ActiveRecord::Base
has_many :users, through: :test_results
end
class TestResult < ActiveRecord::Base
belongs_to :test
belongs_to :user
end
I'm trying to model my database in Ruby and can't figure out how to do it.
This is what I have so far:
class Course < ActiveRecord::Base
has_many :enrolled_ins
has_many :users, :through => :enrolled_ins
has_many :events, :dependent => :destroy
end
class User < ActiveRecord::Base
has_many :enrolled_ins
has_many :courses, :through => :enrolled_ins
end
class EnrolledIn < ActiveRecord::Base
belongs_to :users
belongs_to :courses
end
class Event < ActiveRecord::Base
belongs_to :courses
end
I want to add that when a user picks a course, they can select the different events that they want with that course, and those are assigned to them instead of them getting all the events.
I would add a UserEvents join. When you add a course, you would see a list of available events. Assuming you have a form with checkboxes, you would create the UserEvent records. I don't think you would need all 3 ID values (user, event, course). Course is just a way to group the various events.
class UserEvent < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
I'd also add a dependent destroy on user and on event do destroy the join records if either side is removed.