Foreign Keys and Mongoid - ruby-on-rails

Are foreign keys explicitly required in relationships between two models in Mongoid? For example.
class User
include Mongoid::Document
has_many :posts
end
class Post
include Mongoid::Document
belongs_to :user
# Is this necessary below?
field :user_id, type: Integer
end
The documents on Mongoid's site don't indicate any declarations of fields when discussing relations which is why I ask.

No, generally separate foreign key field declarations are not needed. Mongoid will implicitly create the user_id field on any documents that need it. It follows the same foreign key naming conventions as ActiveRecord.
If those conventions aren't right for your model (e.g. if you have two associations to the same class) then you can override the foreign key name. e.g.
belongs_to :user, foreign_key: :friend_id
Again this is pretty much the same as ActiveRecord (but without the migrations of course).

Model region.rb:
class Region
...
field :title
has_many :users
...
Model user.rb:
class User
...
belongs_to :reg, class_name: "Region", foreign_key: :reg_id
...
You can now use region for user as follows user.reg, for example:
= user.reg.title

Related

accepts_nested_attributes_for is contradicting the relational database logic in Rails

Ruby on Rails model logic conflicts with the relational database logic.
In Ruby on Rails, the model possessing the belongs_to, will have the foreign key in the database. So, the model:
class Student < ApplicationRecord
belongs_to :university
end
class University < ApplicationRecord
has_many :university
end
Will have the migration file:
class CreateStudents < ActiveRecord::Migration[6.0]
t.string :name
t.references :university, foreign_key: true
end
class CreateUniversities < ActiveRecord::Migration[6.0]
t.string :name
end
And, logically, the student will have the University foreign_key in the database, like so:
Student
-id int (PK)
-name varchar
-University_id int (FK)
University
-id int (PK)
-name varchar
So good so far
The problem arises when I want accepts_nested_attributes_for in the student model for referencing a University in the _form.html.erb. In order to use it, the host model, in this case Student, must have a has_one instead of a belongs_to for the referencing table. So it would become:
class Student < ApplicationRecord
has_one :university
accepts_nested_attributes_for :university
end
Notice how, in order to use accepts_nested_attributes_for for referencing the university, the Student model MUST replace its belongs_to :university for a has_one :university, and University would have a belongs_to :student, instead of a has_many :student.
So in the migration file, Student would lose its reference to University, and the latter would have a reference to Student instead, like so:
class CreateStudents < ActiveRecord::Migration[6.0]
t.string :name
end
class CreateUniversities < ActiveRecord::Migration[6.0]
t.string :name
t.references :student, foreign_key: true
end
And the database would forcefully be:
Student
-id int (PK)
-name varchar
University
-id int (PK)
-name varchar
-Student_id int (FK)
Which is wrong, because there should be a one to many relationship from University to Student, not the other way around.
So, is there a way to use accepts_nested_attributes_for, without messing up the relational database logic by having to forcefully inverse the relations in the models ?
accepts_nested_attributes_forNested attributes allow you to save attributes on associated records through the parent.
So you might be having a slight mix up about which one you want to be the parent. If you make the student the parent with nested attributes for the university you would not be referencing the university but essentially creating a new university record or updating an existing one with the nested attributes you used in your student form.
One-to-one
Consider a Member model that has one Avatar:
class Member < ActiveRecord::Base
has_one :avatar
accepts_nested_attributes_for :avatar
end
Enabling nested attributes on a one-to-one association allows you to create the member and avatar in one go:
params = { member: { name: 'Jack', avatar_attributes: { icon: 'smiling' } } }
member = Member.create(params[:member])
member.avatar.id # => 2
member.avatar.icon # => 'smiling'
So you would only be updating/creating new University records for each student you used those nested attributes on.
Here is for one-to-many:
One-to-many
Consider a member that has a number of posts:
class Member < ActiveRecord::Base
has_many :posts
accepts_nested_attributes_for :posts
end
You can now set or update attributes on the associated posts through an attribute hash for a member: include the key :posts_attributes with an array of hashes of post attributes as a value.
For each hash that does not have an id key a new record will be instantiated, unless the hash also contains a _destroy key that evaluates to true.
params = { member: {
name: 'joe', posts_attributes: [
{ title: 'Kari, the awesome Ruby documentation browser!' },
{ title: 'The egalitarian assumption of the modern citizen' },
{ title: '', _destroy: '1' } # this will be ignored
]
}}
member = Member.create(params[:member])
member.posts.length # => 2
member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
You can also see that in the one to many example above. Are you looking to change/create a University record when you post a Student record?
If you are just looking for a reference you essentially have that in your first example and could just change your routes up a bit to have the student referenced by the university.
Then once you have the routes nested you can just do a little work to the form partial. Here is a guy doing just that
So maybe if I am way off here describe why when you create/update a Student you want to create/update a University. If that is what you want to do.
This would helps others with your context a little more and might help others understand your intent with the nested attributes.
Add your form partial for example and explain your goal.
EDIT:
As a punt you can maybe look at has and belongs to many association.
This tutorial talks about nested attributes with a many to many association. But since I'm not certain exactly what your after it may or may not help.
Another punt sounds like you want maybe a student's university record to change when you update them. So you would have the student belong to the university and the university to has many students but also has many university records and the student to has many/has one university record and the record to belong to both.
aka:
this
Then you could have the student have accepts nested attributes for the university record/s.
I see that I have REALLY made a blunder trying to explain my doubts (it is my first question, I have learned from my mistake).
The ultimate goal was to know:
Can I use accepts_nested_attributes_for when there is a belongs_to instead of a has_one or has_many ?
So, the answer is yes:
class Student < ApplicationRecord
belongs_to :university
accepts_nested_attributes_for :university
end
In the student controller, there should be a build_:
def new
#student = Student.new
#student.build_university
end
I REALLY, REALLY, REALLY mistyped my question, and I apologize for it.
Thanks for all your answers.

How to pass foreign key manually in a join table for rails Has and belongs to many relation?

I have two models namely Leave and Calendar. These two models have has_and_belongs_to many association. I am trying to create the join table for this relation named calendars_leaves.
Now the problem that I am facing is it creates column named leafe_id instead of leave_id.
Any idea how to can I solve this issue.
Here are my models
class Leave < ApplicationRecord
belongs_to :user
has_and_belongs_to_many :calendars, foreign_key: 'calendar_id'
self.table_name = 'leaves'
self.primary_key = 'leave_id'
end
class Calendar < ApplicationRecord
has_and_belongs_to_many :users
has_and_belongs_to_many :leaves, class_name: 'Leave', foreign_key: 'leave_id'
validates :name, :description, presence: true, length: { minimum: 3 }
end
ActiveRecord resolves tables with 'ves' to singular model class with 'f' by default. Hence a table named leaves is resolved to ActiveRecord model class Leaf
So, for custom behaviour of these incorrect assumed translations, you need to configure it on your app's config/initializers/inflections.rb file.
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'leave', 'leaves'
end
After configuring your app with this inflection and restarting your app's web server, rails will recognize Leave as the ActiveRecord model defined in app/models/leave.rb representing db table leaves and you can use leave_id as foreign key for defining associations between your models.
Read more about inflections here

How to scope Rails model id which is dependent on belongs_to association?

I have next structure of models in my app:
class Company
has_many :employees
end
class Employee
belongs_to :company
end
Is there a way to make it possible for employees to have unique ids (default primary keys) depending on belongs_to Company association?
These should return different Employee models:
/companies/1/employees/1
/companies/2/employees/1
Thanks!
Try the sequenced gem, it does exactly what you're asking. There is one consideration to keep in mind though.
Your requirement deprives Employee's id field of uniqueness which it needs to be a primary key. Therefore you'd either need to have a composite key in Employee, namely [:company_id, :employee_id] or use the Employee's acts_as_sequenced field not as the primary key but more like a slug.
Just in case you care to explore the composite key approach, there is composite_primary_key gem which aims to support ActiveRecord associations on top of composite keys. I haven't tried it myself though.
According to its docs, your associations could look something like this:
class Company < ActiveRecord::Base
has_many :employees, :foreign_key => [:company_id, :employee_id]
end
class Employee < ActiveRecord::Base
self.primary_keys = :user_id, :employee_id
belongs_to :company
end
But its quite likely this is an overkill approach for your goal.

ActiveRecord foreign key set to null

I am using Ruby 1.8.7 and Rails 2.3.8 and I have the following root resource:
class PointOfInterest < ActiveRecord::Base
set_primary_key "Id"
set_table_name "POI"
has_many :attributes, :foreign_key => 'POIId'
end
The point of interest can have several attributes:
class Attribute < ActiveRecord::Base
set_primary_key "Id"
set_table_name "Attribute"
belongs_to :point_of_interest, :foreign_key => 'POIId'
has_one :multimedia, :foreign_key => 'Id', :primary_key => 'Value'
end
The attribute class may have media associated with it:
class Multimedia < ActiveRecord::Base
set_primary_key "Id"
set_table_name "Multimedia"
end
I am trying to insert a point of interest in my database like so:
poi = PointOfInterest.new
attr = poi.attributes.new
attr.SomeAttribute = 1
attr.build_multimedia(:content => 'test')
poi.save
This is properly persisting both the root (PointOfInterest) and the Multimedia record. The Attribute, however, is not being properly persisted. While the foreign key to the point of interest is properly set (POIId), the foreign key to the Multimedia record remains null.
Any clue as to why this is very much appreciated!
Thanks!
Your relationship / foreign key are set at cross purposes.
If Attribute has_one Multimedia, then you need the Multimedia model to declare belongs_to :attribute, and the Multimedia table to contain a foreign key for Attribute.
I'm guessing that you can't change your database schema (otherwise, I'd have to ask why you're using non-Rails-standard table and key names); in which case you want to switch the sense of the relation. Make Attribute belongs_to :multimedia, and Multimedia has_one :attribute. Then the Multimedia FK in the Attribute table is pointing in the right direction.

What's the difference between belongs_to and has_one?

What is the difference between a belongs_to and a has_one?
Reading the Ruby on Rails guide hasn't helped me.
They essentially do the same thing, the only difference is what side of the relationship you are on. If a User has a Profile, then in the User class you'd have has_one :profile and in the Profile class you'd have belongs_to :user. To determine who "has" the other object, look at where the foreign key is. We can say that a User "has" a Profile because the profiles table has a user_id column. If there was a column called profile_id on the users table, however, we would say that a Profile has a User, and the belongs_to/has_one locations would be swapped.
here is a more detailed explanation.
It's about where the foreign key sits.
class Foo < AR:Base
end
If foo belongs_to :bar, then the foos table has a bar_id column
If foo has_one :bar, then the bars table has a foo_id column
On the conceptual level, if your class A has a has_one relationship with class B then class A is the parent of class B hence your class B will have a belongs_to relationship with class A since it is the child of class A.
Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class declaring the belongs_to relationship.
class User < ActiveRecord::Base
# I reference an account.
belongs_to :account
end
class Account < ActiveRecord::Base
# One user references me.
has_one :user
end
The tables for these classes could look something like:
CREATE TABLE users (
id int(11) NOT NULL auto_increment,
account_id int(11) default NULL,
name varchar default NULL,
PRIMARY KEY (id)
)
CREATE TABLE accounts (
id int(11) NOT NULL auto_increment,
name varchar default NULL,
PRIMARY KEY (id)
)
has_one and belongs_to generally are same in a sense that they point to the other related model. belongs_to make sure that this model has the foreign_key defined.
has_one makes sure that the other model has_foreign key defined.
To be more specific, there are two sides of relationship, one is the Owner and other is Belongings. If only has_one is defined we can get its Belongings but cannot get the Owner from the belongings. To trace the Owner we need to define the belongs_to as well in the belonging model.
One additional thing that I want to add is, suppose we have the following models association.
class Author < ApplicationRecord
has_many :books
end
If we only write the above association, then we can get all books of a particular author with
#books = #author.books
but, for a particular book, we can't get the corresponding author with
#author = #book.author
To make the above code work we need to add an association to the Book model as well, like this
class Book < ApplicationRecord
belongs_to :author
end
This will add method 'author' to the Book model. For mode details see guides
has_one
This method should only be used if the other class contains the foreign key.
belongs_to
This method should only be used if the current class contains the foreign key.
From a simplicity standpoint, belongs_to is better than has_one because in has_one, you would have to add the following constraints to the model and table that has the foreign key to enforce the has_one relationship:
validates :foreign_key, presence: true, uniqueness: true
add a database unique index on the foreign key.

Resources