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)
Related
I'm running a Rails 5.2.1 app, with Ruby 2.6.7.
I have a Forum-like app, and I'm trying to implement a home-baked Tagging system, similar to what StackOverflow has. The way I chose to do it, is to have a simple join table that will facilitate a has_many through: relationship for my Tag and Question models.
The problem I'm encountering is that I'm not sure how to assign a Tag to a question without creating a new entry in the Tag table (assuming this is even possible to do). Preferably, I'd want the database to only have 1 entry for each unique tag.
For example, when a new question is created, a user would select a tag which already exists, and the question would be linked to the specific ID of the tag in question.
User creates question, with id: 1, chooses tag that in the DB has id: 1
In the Join table, this would look like question_id: 1 | tag_id: 1
User creates another question, now with id: 2, and again chooses tag id: 1
The table for that has question_id: 2 | tag_id: 1
As it is right now, if I do question.tags.create(id: 1), the tag doesn't get created. On the other hand, if I do question.tags.create(name: "tagname"), the tag does get created, but it's created as a new Tag entry and has id: 2, leaving me with two tags with the same name.
My models:
class Question < ApplicationRecord
has_many :question_tags, dependent: :destroy
has_many :tags, through: :question_tags
end
class Tag < ApplicationRecord
has_many :question_tags, dependent: :destroy
has_many :questions, through: :question_tags
end
class QuestionTag < ApplicationRecord
belongs_to :question
belongs_to :tag
end
How could I achieve the result I want? Presumably, I'd use some sort of before_Save or before_create hook to handle it for me, but I'm unsure of how exactly to implement that properly.
You could find all the Tag ids from the user's selection and then just add those. ActiveRecord gives you #tag_ids. In your service or controller or w/e, you could just add them there; ie select the tags for the question, get the id from the select value, in your controller permit the tag_ids and then assign as needed.
# form
<%= select question[tag_ids][], .... %>
# controller
private
def question_params
params.require(:question).permit(:id, ..., tag_ids: [])
end
# Then used similarly whereever you choose
question = Question.find(params[:id])
question.tag_ids << question_params[:tag_ids]
or
question.tag_ids = question_params[:tag_ids]
You can create/update a question with the tag_ids like any attribute
I have these models in my Rails app. My use-case is multilingual data. First I want to store the default language version, then it will be possible to add other language data.
class Category < ApplicationRecord
has_many :category_versions
accepts_nested_attributes_for :category_versions, allow_destroy: true
end
class CategoriesVersion < ApplicationRecord
belongs_to :categories
has_many :categories_contents
has_one :langs
end
class CategoriesContent < ApplicationRecord
belongs_to :categories_versions
end
And I would like to save the record with one toplevel record with associated record in CategoriesVersion and CategoriesContent.
Using data from params and generated inside create method in the controller.
I got first and second table, but I don't know how to add attributes for third table, nested in second one, if it is possible. So the association from CategoriesVersion to CategoriesContent
category_data = { category: {
kind: "user",
featured_image: "test",
featured_image_title: "test",
editorial_id: nil,
category_versions_attributes: [
{lang_id: Lang.find_by_lang("francais").id},
{published: false},
{featured: false} #data["featured"] || false
]
}}
test = Category.create(category_data["category"])
Please any suggestions?
If I understand your issue, I think accepts_nested_attributes_for is the configuration that you need in your models to save the nested data just saving the category.
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.
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.