In my project, a user has a hotel, and each hotel has room types associated with it. Briefly, the models are
class User < ActiveRecord::Base
has_many :hotels
class Hotel < ActiveRecord::Base
belongs_to :user
has_many :roomtypes
class Roomtype < ActiveRecord::Base
attr_accessible :maxguests, :name
belongs_to :hotel
If I do the commands:
#user = User.find(1)
#user.hotels.find(1).roomtypes.build(name: "something", maxguests: "2")
The console returns:
#<Roomtype id: nil, name: "something", maxguests: 2, created_at: nil, updated_at: nil, hotel_id: 1>
For some reason, the Roomtype id and the timestamps are nil. Any ideas why?
You have built the roomtype (which creates a new instance of the object), but you not saved it yet. You have to explicitly save roomtype for it to go into the db (and thus get an id).
Instead of build use create or create! to accomplish this with a single method call
Replace build with create and you'll have your persisted object with id and timestamp.
Would not make sense to have this before.
Related
I have three models associated with the following ways:
User model
has_many :project_memberships
has_many :projects, through: :project_memberships
Project model
has_many :project_memberships
has_many :users, through: :project_memberships
Project membership model
belongs_to :user
belongs_to :project
The project membership model also has additional fields like user_role, invitation_accepted etc.
I want to get all the users in a specified project, with all the project membership fields.
Example:
# user json response
[
{
id: user_id,
name: user_name,
user_role: "admin",
invitation_accepted: true
},
{
// Etc
}
]
Currently, I have something like:
def index
#project = Project.find(params[:project_id])
#team_members = #project.project_memberships
end
The team_members only returns
#<ActiveRecord::Associations::CollectionProxy [#<ProjectMembership id: "42087cd2-31f5-4453-b620-5b47a82de422", user_id: "4f428880-48d0-40d0-b6d6-eed9172ce78d", project_id: "3e758d26-7625-4cbd-8980-77085f8d38a0", role: "admin", invitation_accepted: true, job_title: nil, created_at: "2020-10-24 05:48:38", updated_at: "2020-10-24 05:48:38">]>
I am getting the user_id, but don't know how to merge the actual user fields in the above query.
You can use includes to preload data (behind the scenes, it is performing a join).
#team_members = #project.project_memberships.includes(:user)
Now you can call #team_members[0].user.name (or extract the user name from all of them) and it doesn't fire an additional database query to load the user.
Note that this does work without includes, but it will be slower, and introduces a common pitfall known as "N+1 queries"
I have three models: User, Company, and Subscription. What I am trying to accomplish is a Subscription belongs to either a User OR a Company.
To try accomplish this, I referenced this guide, but I have been unsuccessful as the record creation keeps rolling back.
here's my Company model:
# app/models/company.rb
class Company < ApplicationRecord
has_many :subscriptions, dependent: :destroy, as: :imageable
end
here's my User model:
# app/models/user.rb
class User < ApplicationRecord
has_many :subscriptions, dependent: :destroy, as: :imageable
end
and finally, here's my Subscription model:
class Subscription < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
Now as far as the migration file, this is my Subscription migration file:
class CreateSubscriptions < ActiveRecord::Migration[5.1]
def change
create_table :subscriptions do |t|
t.references :imageable, polymorphic: true, index: true
t.date :start_date
t.date :stop_date
t.timestamps
end
end
end
As far as what I can see, this is pretty much exactly like the guide shows, but it keeps rolling back. Here's the output of the rails console:
Loading development environment (Rails 5.1.6)
2.5.1 :001 > Subscription.create(imageable_id: 1, start_date: Time.now, stop_date: 2.days.from_now)
(8.6ms) SET NAMES utf8, ##SESSION.sql_mode = CONCAT(CONCAT(##sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), ##SESSION.sql_auto_is_null = 0, ##SESSION.wait_timeout = 2147483
(0.2ms) BEGIN
(0.3ms) ROLLBACK
=> #<Subscription id: nil, imageable_type: nil, imageable_id: 1, start_date: "2018-10-10", stop_date: "2018-10-12", created_at: nil, updated_at: nil>
2.5.1 :002 >
Here are the questions that I have:
Why is there an imageable_type field? Is that created by t.references and if so, do I need this? Can I just use imageable_id instead of t.references as the other part of the suggestion shows?
Why is it rolling back? Are polymorphic associations done differently in Rails 5.x or something by chance?
According to the graph shown in the guide, it looks like if a picture belongs to imageable_id 4, then if there is an employee AND a production with the ID of 4, then a picture would belongs to both instead of one or the other like I'm trying to accomplish. Correct?
In your association, Imageable type will contain the class name and imageble id will contain the id of that class. So if you want to create subscription for user you can do like below
User.first.subcriptions.create(start_date: Time.now, stop_date: 2.days.from_now)
So it will automatically pick up First user's id in imageable id and take "User" as imageable type.
If you want to create subscription manually, you must have to pass both fields imageable type and imageble id like below,
Subscription.create(imageable_id: 1, imageable_type: "User", start_date: Time.now, stop_date: 2.days.from_now)
Why is there an imageable_type field? Is that created by
t.references and if so, do I need this? Can I just use imageable_id
instead of t.references as the other part of the suggestion shows?
=> imageable_type will contain the class of associate model like "User" or "Company"
Why is it rolling back? Are polymorphic associations done
differently in Rails 5.x or something by chance?
=> No, you setup it correctly
According to the graph shown in the guide, it looks like if a picture belongs to imageable_id 4, then if there is an employee AND a production with the ID of 4, then a picture would belongs to both instead of one or the other like I'm trying to accomplish. Correct ?
=> It depends on both imageable_id and imageble_type , so by combination of both this you will get record. If imageable_id is 4 and imageable_type is "Picture" then it will take Picture with id 4.
Please check this link for understaing
For polymorphic association, you should also pass imageable_type along with imageable_id. You don't do it and that's why it doesn't work, most probably (i.e. there might be other reasons, I don't know, but this one is pretty obvious).
imageable_type holds the name of the class of the record given Subscription is associated to.
I have two ActiveRecord models like so:
class User < ActiveRecord::Base
has_one :contact_information
end
class ContactInformation < ActiveRecord::Base
belongs_to :user
end
This is my setup for a One-to-One relationship between the user and contact information table.
The issue I am encountering is when I create a new contact_information entry that points to a user that already has a existing contact_information, the new record is created including the relationship, meaning I have two contact_information records pointing to the same user, even though it is a one-to-one relationship.
Rails seems to pick the first record and returns it.
For example in irb:
> User.create
> ContactInformation.create(user: User.last)
> ContactInformation.create(user: User.last)
> ContactInformation.all
=> #<ActiveRecord::Relation [#<ContactInformation id: 3, created_at: "2016-01-04 22:28:11", updated_at: "2016-01-04 22:28:11", user_id: 2>, #<ContactInformation id: 4, created_at: "2016-01-04 22:28:18", updated_at: "2016-01-04 22:28:18", user_id: 2>]>
Both with user_id set to 2.
The ideal scenario would be a validation failure from contact_information when a record is created for a user that already has a contact_information.
I came up with this initial custom validation method for the Contact Information model that kind of works for creation (Need to make one for update)
validate :check_parent, on: :create
def check_parent
if user.contact_information.id.nil?
errors.add(:user, "already has contact information")
end
end
I am wondering if there is a more rails-y way of solving this problem? Or will I need to create a custom validator like this for all has_one relationships?
Thanks
A few options are available to you.
You could use validates_uniqueness_of on your ContactInformation model:
class ContactInformation < ActiveRecord::Base
validates_uniqueness_of :user_id
end
However, this will query the database to ensure that no duplicates exist on every save and could be very slow.
Another option is to have your database handle this for you. Depending on what you're using ActiveRecord with, adding a uniqueness constraint on the user_id column of your contact_informations table could be valuable.
I would suggest not using validates_uniqueness_of if your dataset is large, otherwise it might be perfect.
Make sure you at least add an index to the user_id column on that table:
class AddIndexOnContactInformationToUserId < ActiveRecord::Migration
add_index :contact_information, :user_id
end
You will need to have a validation but you don't need a custom one:
class ContactInformation < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :user_id
end
You may also want to add a unique index in the database for that column. It would not only make the query performant but will also protect your app against race conditions in case you are running multiple instances of it in parallel.
I'm attempting to use global ids to specify polymorphic associations. However, when I want to reference the object through the association, no object is found.
Consider the following models:
class Person < ActiveRecord::Base
has_many :optionees, as: :entity
belongs_to :company
end
and
class Optionee < ActiveRecord::Base
belongs_to :entity, polymorphic: true
belongs_to :option
end
If I seed the above as follows:
Person.create(fname: "John", lname:"Smith", email:"john.smith#email.com", telephone:"555-555-5555", street:"333 Street St", city:"Salt Lake City", state:"UT", zip:"99999", company_id:"1")
(Giving the Person an ID of 1)
and
Optionee.create total_shares: "332", exercise_price: "33.22" option_id: "1" entity_id: "gid://legal/Person/1"
(Giving the Optionee an ID of 1)
I am unable to look up the person with the following command:
Optionee.find(1).entity
Instead it gives me a nill result.
I realize that the traditional way to do polymorphic associations is to provide the model name AND id; is there somthing I'm missing to do this with global ids? Many thanks in advance.
You need to add entity_type filed in Optionee table as well. And if person id is 1, then entity_id is equals to 1:-
Optionee.create total_shares: "332", exercise_price: "33.22" option_id: "1" entity_id: "1", entity_type: "Person"
Or you can create record for person in optionee table by doing this:-
#person = Person.find(params[:id])
#person.optionees.create(params)
It will set entity_id and entity_type for person. By doing this, no need to set entity_id and entity_type explicitly.
I'm kind of new in rails and I have a problem I cannot solve.
I have a model Recipe
class Recipe < ActiveRecord::Base
has_many :items, :dependent => :destroy
accepts_nested_attributes_for :items,**
and a model Item
class Item < ActiveRecord::Base
belongs_to :recipe
end
And I'm having problems accessing Item attributes through Recipe.Example:
I have a item (#i) with a description field with a string "test_" and id = 1
And I have a Recipe (#r) with id=2 and description "test_recipe";
I'm able to associate correctly the item to the recipe using
#i.recipe_id = 2
And if I do #i, I have the result
#<Recipe id: 2, description: "test_recipe", created_at: "2012-04-14 15:11:00", updated_at: "2012-04-14 15:11:00"`
But if I do #r.items, I have the result
Item id: 1,recipe_id: 2, updated_at: "2012-04-14 15:11:00" , description: nil)
He cant access the description field of items. Why? And this is avoiding me to build a proper form for recipes cause rails doesn't build the items field.
I would guess this is because you're attempting to do something like this:
#recipe.items.description
When, if you just want the description of each of the items:
#recipe.items.map(&:description)