Rails exclude a name "prefix" in model relations - ruby-on-rails

Say I have the models Article, ArticleVote and ArticleComment.
Is there any way to directly drop the prefixes like article_ in the relations (rather than setings class_name, foreign_key, etc.)?
class Article
belongs_to :user
has_many :article_votes # Just call this "votes"
has_many :article_comments # Just call this "comments"
end

No there is no way.
You can specify any name you want but need to give the class name when it can not be inferred from the association name (and perhaps the foreign key, depending on how you named it):
class Article
belongs_to :user
has_many :votes, class_name: 'ArticleVote'
has_many :comments, class_name: 'ArticleComment'
end
I don't see how this should be possible in any other way. How would rails know that you want the name built in another way for this specific association?

Related

Rails ActiveRecord equivalent of Laravel ORM `attach()` method for polymorphic has_many :through

I'm transitioning from "Laravel ORM" to "Rails Active Record" and I couldn't find how do you do something like this:
$this->people()->attach($person['id'], ['role' => $role]);
Explanation for Laravel code snippet
People is a polymorphic association to the class that is being accessed via $this via the Role class. The function above, creates a record in the middle table (roles/peopleables) like this:
id: {{generically defined}}
people_id: $person['id']
role: $role
peopleable_type: $this->type
peopleable_id: $this->id
How the association is defined on the Laravel end:
class XYZ {
...
public function people()
{
return $this->morphToMany(People::class, 'peopleable')->withPivot('role','id');
}
...
}
My efforts in Ruby
Here is how I made the association in Ruby:
class Peopleable < ApplicationRecord
belongs_to :people
belongs_to :peopleable, polymorphic: true
end
class People < ApplicationRecord
has_many :peopleables
end
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
I have seen the operation << but I don't know if there is any way to set an additional value on the pivot table while triggering this operation. [in this case the roles or peopleables tables; I use these two terms interchangeably in this app.]
PS. So, basically the question is how to define additional values on the pivot table in a polymorphic-many association in ActiveRecord and dynamically set those values while initiating an attachment relationship
Description of Functionality
Our application has a limitless [generally speaking, not that there is no computational limits!] content type: post, novel, poem, etc.
Each of these content types can be associated to individuals who play certain roles: editor, author, translator, etc.
So, for example:
X is the translator of Post#1. X, Y and Z are authors of Post#1.
There is a distinct People model and each content type has its own unique model [for example: Post, Poem, etc].
The idea of :through is referring to the 'Role class' or 'the pivot table' [whichever way you want to understand it] that the polymorphic association is recorded on it.
In addition to the information regarding a simple polymorphic relationship, there is also the kind of role that is recorded on the pivot table.
For example, X is both the author and the translator for Post#1, so there are two rows with the same people_id, peopleable_type and peopleable_id, however they have different values for role.
From what I understand given your description, I think you have this models (I'll change the names to what I understand they are, hope it's clear enough):
class Person < ApplicationRecord # using singular for models
has_many :person_roles
end
class Poem < ApplicationRecord
has_many :person_roles, as: :content
end
class Novel < ApplicationRecord
has_many :person_roles, as: :content
end
etc...
class PersonRole < ApplicationRecord
belongs_to :person
belongs_to :content, polymorphic: true
# you should have a "role" column on your table
end
So a Person is associated to a "content" (Novel, Poem, etc) via the join model PersonRole with a specific role. A Person that is the author of some novel and the editor of some peom would have two PersonRole records.
So, if you have a person and you want to assign a new role on some content, you can just do:
person.person_roles.create(role: :author, content: some_poem)
or
PersonRole.create(person: person, role: :author, content: some_poem)
or
some_poem.person_roles.create(person: person, role: :author)
You have two things in play here: "belongs_to :content, polymorphic: true" is covers the part of this being a polymorphic association. Then you have the "PersonRole" table that covers the part you know as "pivot table" (join table/model on rails).
Note that :through in rails has other meaning, you may want to get all the poems that a user is an author of, you could then have a "has_many :poems, through: :person_roles" association (that won't actually work, it's more complex than that in this case because you have a polymorphic association, you'll need to configure the association with some extra options like source and scope for this to work, I'm just using it as an example of what we understand as a has many :through association).
Rails is 'convention over configuration'. Models' must be in singular 'Person'.
ActiveRecord has has_many ... through and polymorphic association
"Assignable" and "Assignments" are more natural to read than "peoplable"
class Person < ApplicationRecord
has_many :assignments, as: :assignable
has_many :roles, through: :assignments
end
class Role < ApplicationRecord
has_many :assignments
has_many :people, through: :assignments
end
class Assignment
belongs_to :role
belongs_to :assignable, polymorphic: true
end
You can read more Rails has_many :through Polymorphic Association by Sean C Davis

Understanding use of foreign_key and class_name in association

I am new to rails and this is a very basic question. I am trying to understand the need of foreign key and class_name.
has_many :task, foreign_key: "created_by"
has_many :memberships, class_name: "TaskMembership"
Can anyone explain the need of foreign_key & class_name.
Here is the answer of my question
Suppose you have a User model and Post model.And you have to set an association like User has many post
User Model
has_many :posts
Post Model
belongs_to :user
Now suppose your user is some author so we have to set some meaningful name so instead of user we will use author but have to specify which class it is referring
Post Model
belongs_to :author, class_name: 'User'
Now problem will occur because rails will look for author_id column in posts table .So here foreign key will come into picture.We will have to find user_id
Post Model
belongs_to :author, class_name: 'User', foreign_key: 'user_id'
See more better explanation association
has_many association is used for for one-to-many type relationships in rails. For instance, if you have a model User which can has many profiles, your User to Profile association will be has many.
class User < ActiveRecord::Base
has_many :profiles
end
class Profile < ActiveRecord::Base
belongs_to :user
end
If you have a foreign key different than user_id in profiles table, you explicitly specify foreign_key. Same is the case with class name. If your association name is different than actual model name, you explicitly specify class name after association (as you did for memberships).
Hope it helps.
in your model
class First < ActiveRecord::Base
has_many :seconds
end
class Second < ActiveRecord::Base
belongs_to :first
end
and in your second class table,create first_id column

Understanding customized has_many association

Given association in User class:
has_many :followers, through: :follows_as_fallowable,
source: :user
It returns user instances that follow a given user. But when i started to dig deeper i realized that i don't completely understand why does this association(user.followers) returns User objects.
Based on what? I know that it can deduce by name of the association or class_name hash argument, but neither of these actually matters in this case.
I don't have Follower model and i have not provided class_name attribute.
Source parameter just say that it should search by user column in join table.
So how does Rails know that it should select from Users table?
EDIT:
follow_as_followable is another association in User model:
has_many :follows_as_fallowable, class_name: 'Follow', as: :followable
Rails would be picking up knowledge of the User through the follows_as_fallowable relationship, using the association name specified by :source, which is :user (e.g. the User model).
While it's not show in the question, it's likely that the model containing follows_as_fallowable has a belongs_to :user relationship defined, hence the use of source: :user to specify which relationship through which to navigate.
You can see more information in The has_many :through Association section of the Active Record Associations guide.

Rails HABTM or has_many?

I have two models: Person, and Property. In the Person model I have a field which stores the role of the person(tenant, owner, admin, contractor, etc). Since each property will belong to an owner and potentially have one or more tenants, I thought this would be a good opportunity to use the HABTM model relation.
Do I have this right?
Also, how do I reference the attached model? Assuming my join model is named PropertiesPeople, and I wanted to fetch the tenants for a particular property, would that be the following?
#property.people.where(:role => "tenant")
If the same Person can have more than one Property, you should can use HABTM. Something like this:
class Person < ActiveRecord::Base
# in the people table you are storing the 'role' value
has_and_belongs_to_many :properties, join_table: 'people_properties'
end
class Property < ActiveRecord::Base
has_and_belongs_to_many :people, join_table: 'people_properties'
end
You should create the intermidiate table people_properties with the foreign keys, person_id and property_id.
The problem of this approach is that if a Person can be "tenant" in one property and "contractor" in another, for example, you can't store that information. In that case I will suggest using an intermidiate model, like this
class Person < ActiveRecord::Base
has_many :property_people
has_many :properties, through: :property_people
end
class PropertyPerson
# now you store here the 'role' value
belongs_to :person
belongs_to :property
end
class Property < ActiveRecord::Base
has_many :property_people
has_many :people, through: :property_people
end
I don't know for sure if the class names are successfully inferred from the relationships names, in that case you can always indicate class_name or even the foreign_key for the associations. Also you can indicate the table for a model, using self.table_name=

What inverse_of does mean in mongoid?

What inverse_of does mean in mongoid associations? What I can get by using it instead of just association without it?
In a simple relation, two models can only be related in a single way, and the name of the relation is automatically the name of the model it is related to. This is fine in most cases, but isn't always enough.
inverse_of allows you to specify the relation you are referring to. This is helpful in cases where you want to use custom names for your relations. For example:
class User
include Mongoid::Document
has_many :requests, class_name: "Request", inverse_of: :requester
has_many :assignments, class_name: "Request", inverse_of: :worker
end
class Request
include Mongoid::Document
belongs_to :requester, class_name: "User", inverse_of: :requests
belongs_to :worker, class_name: "User", inverse_of: :assignments
end
In this example, users can both request and be assigned to tickets. In order to represent these two distinct relationships, we need to define two relations to the same model but with different names. Using inverse_of lets Mongoid know that "requests" goes with "requester" and "assignments" goes with "worker." The advantage here is twofold, we get to use meaningful names for our relation, and we can have two models related in multiple ways. Check out the Mongoid Relations documentation for more detailed information.

Resources