Rails Associations - has_one, belongs_to_many - ruby-on-rails

It's been a while since I've worked with Rails (Rails 6) on a new application. I'm failing to recall how to properly set up associations. I have two problems that are rather similar and may help to answer each other. I understand that the model containing belongs_to will hold the foreign key, however, it feels backward in my head.
I have a User model with a pronouns attribute. I have a Model/Table pronouns. A user can have one pronouns record associated with is (has_one?). But a pronouns record can belong to any number of users. So belongs_to won't work in this case, since the foreign key would be on the pronouns table. The way that I've sort of gotten around this is to use belongs_to, but this doesn't feel right because I need to make it optional since pronouns isn't required for a user.
class User < ApplicationRecord
belongs_to :pronouns, optional: true
end
class Pronoun < ApplicationRecord; end
I have the following models: Location, User, Experience, JobPost. In this case, User, Experience, and JobPost can have a single location, but Location can belong_to (have_many?) of each of these. This feels like a case for a polymorphic association. Where I'm feeling confused is what the has_one model (user, experience, job post) looks like. Do they have one? They shouldn't have many.
This is a work in progress, so there isn't much more code-wise I can really add. But if there are any areas where I can share more or elaborate, I'm happy to.

Related

Rails - Creating and Validating Many-to-Many relationship with the same model?

This is probably a really simple question, but I've been searching the web for probably around an hour and I can't really find an answer to my problem. It should be clear by what follows that I am very new to Rails, so my terminology and explanation might be a bit confusing.
Let's say that I were making a social media app on Rails, where one of the models is User. I want to make a many-to-many relationship called "friends", which links two users together. Let's say in this situation I also wanted to make a many-to-many between two users called "enemies".
This is all completely hypothetical, but the idea is the same one that I want to use for something I'm working on.
Because a user can have many friends and enemies, but also be many friends and enemies, I would use:
class User < ActiveRecord::Base
has_and_belongs_to_many :users #this should be the friends association
has_and_belongs_to_many :users #this should be the enemies association
end
Now I'm guessing I can't just do that, because I would have to have two tables both named users_users. So, then I switch to:
class User < ActiveRecord::Base
has_and_belongs_to_many(:users, join_table: 'friends',
foreign_key: 'user_id', associate_foreign_key: 'friend_id')
end
With a similar statement for the enemies table. Now, my problem is that I want to have a form that the user can use when they sign up, where they can input their information (this is the User object details), and also list their friends and enemies.
Because the user won't have the database id key for their friends or enemies, they'll have to input the users' names. This is fine, though because the name is also a unique key, guaranteed by the validation.
However, if the user types in the name of a friend, I can't join the two if the friend happens to not exist. So, I use a custom validation class that looks something like this:
class FriendValidator < ActiveModel::Validator
def validate(object)
#lookup user and throw error if not found.
end
end
which will access the variable (object.friends) and (object.enemies)
With something similar for enemies. So therefore, above my has_and_belongs_to_many statements, I have lines that say:
attr_accessor :friends, :enemies #these are attrs because they don't exist within the model's db
validates_with FriendValidator
When I create the form with erb, I have the standard form_for block
<%= form_for(#user) do |f| %>
It seems to me that I can't just stick
<%= f.text_area :friends %>
because friends isn't actually something that will get passed to the User object, but rather a separate table. (Can I, though? Because the attr_accessor is declared in the user's model class?)
So now, we have my main problem. I have two many-to-many tables with a model to its own model class, and I don't know how to ensure that the validation class will take the two attributes, lookup and throw necessary errors, and then add a row to the join tables using the id of the user, rather than the string inputted. What form fields should I use to pass the input to the right place? Where do I change the controller methods so that the input gets sent to the join table rather than the user object?
This definitely seems like a pretty specific situation, so I can't really find an answer in the Rails documentation, which I've been learning from.
My initial impression of this problem has to do with your associations. To me, a user has_many enemies and has_many friends.
friends belong_to user
enemies belong_to user
Not sure if a many to many relationship makes sense in this case. Maybe that's why you are having such a hard time finding an answer online. Just my two cents.

Is it possible to have a dynamic id column in Rails 3 when using single table inheritance

I have a Change model that utilizes single table inheritance that has the following attributes:
id
type #this is a single table inheritance type field.
description
dynamic_id
I also have two sub classes, Race which is a subclasses of Change and Workout which is a sub class of Race.
class Race < Change
end
class Workout < Race
end
I have a fourth class called Track and I'd like to create the following four associations by just using the dynamic_id field in the Change object. (i.e. I have not explicitly added race_id and workout_id to the Change table. Instead I want to use the dynamic_id as the race_id for the Race class and the dynamic_id as the workout_id for the Workout class) By doing this, I will avoid having a lot of nil fields in my database.)
Here are the four associations I'm trying to create.
Race Model - belongs_to :track
Workout Model - belongs_to :track
Track Model - has_many :races
Track Model - has_many :workouts
I've been trying to accomplish this with associations using :class_name and :foreign_key, but I can't seem to get it working. Is this actually possible. I realize its probably not a best practice, but I'd still like to see if it doable. Thanks for your input.
What you are looking for are "polymorphic associations". You can find more in the rails guides: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Your case is a bit special because you want to use polymorphic associations with STI. I remember that there was a bug with this combination but it could be fixed by now.
I did not read it completely but this blog post seems to describe the situation: http://www.archonsystems.com/devblog/2011/12/20/rails-single-table-inheritance-with-polymorphic-association/
The problem I encountered with polymorphic associations and STI is described here: Why polymorphic association doesn't work for STI if type column of the polymorphic association doesn't point to the base model of STI?

Rails 3 - A model with a one to one relationship to itself - do I need belongs_to

I have a model named Person. It has two properties - name and parent_person_id
A person will always have a parent person.
Should I be using belongs_to in the model? If so, what are the advantages of doing so.
class Person < ActiveRecord::Base
belongs_to :person
end
I've not tried this code out yet, it seems a bit wrong my normal mysql ways.
I'm looking for opinions here more than anything, I'm quite new to the rails and want to make sure I'm doing things properly, doing things 'the Rails way'.
I'd suggest using a gem like ancestry for a tree structure like that. It gives you your association plus lots of utility methods (finding parent, children, siblings, retrieving a subtree).
If you don't want that, then in your belongs_to association has to look like this:
belongs_to :person, :foreign_key => "parent_person_id"
since without that option, rails would look for a foreign key of person_id and, not finding that, light your CPU on fire throw an error message.
Yes, you would need that belongs_to since this is what will tell rails about this relationship.

Ruby on Rails: Is there a way to temporarily disable polymorphic associations?

I've got an association between models that is polymorphic.
Example:
class Review
belongs_to :review_subject, :polymorphic => true
end
However, I would like to make a call on this :review_subject association where I know all the results will be of a certain type.
In my case, I want to do this so I can join in the review_subject and then impose a condition upon it. Doing so on a polymorphic relation normally causes this to raise an EagerLoadPolymorphicError. The logic behind this is that it's not able to know what model to load in order to perform the join, but that does not apply in my case because I already know only one model will be involved in this query.
Example, where I know that all relevant reviews will belong_to a Book, and I want to only show reviews where the books have authors:
Review.joins(:review_subject)
.where(review_subject_type => "Book")
.where("reviewed.book_author IS NOT NULL")
Is there a way to temporarily disable the polymorphic relationship?
The best solution I've come up with is to add a second associationin the Review model, belongs_to :review_subject_books_only that is not polymorphic, and can be called on only in this situation. However, this is an ugly hack both in the model, and in that it also messes up include calls unless the views also refer to a Review's review_subject_books_only.
Do the query the other way around:
Book.joins(:reviews).where('book_author is not null')

Rails active record model relationship - one model belonging to three models

I have situation where a single model needs to have three foreign ids. This table has the information which belongs to three models.
e.g. -
Three models - Keyboard, Mouse, Monitor
Now i have fourth model details - which has information about what will happen for any combination like keyboard1, mouse1 and monitor1.
So what should be good design pattern for this ?
Right now what i use is Detail.find_by_keyboard_id_and_mouse_id_and_monitor_id(keyboard1id, mouse1id, monitor1id) #=> Detail1
Which of course is not a best way to go...
I don't think what you have is a bad design. There probably aren't too many options for how this should be implemented, but you should think about how you want to use it. Personally, I wouldn't call the model Detail since it doesn't tell you anything about what the model really is. Something like HardwareConfiguration is more explicit. Maybe too lengthy, but you could shorten it to HardwareConfig or Configuration, depending on your taste.
This model should have an id and the three foreign keys. You should add database indexes to each foreign key field as well.
class Detail < ActiveRecord::Base
belongs_to :keyboard
belongs_to :mouse
belongs_to :monitor
end
Then each hardware model would have_many :details like:
class Keyboard < ActiveRecord::Base
has_many :details
end
You could look up the detail combo by its ID, or any combination of foreign keys.
Detail.find(id)
Detail.find_all_by_keyboard_id(keyboard_id)

Resources