I have an object that looks like this in Rails console:
pry(#<User>)> favorite_foods
=> [#<FavoriteFood id: 3, food_id: 1, user_id: 1, removed: false, created_at: "2015-09-06 00:49:35", updated_at: "2015-09-06 00:49:35">,
#<FavoriteFood id: 4, food_id: 2, user_id: 1, removed: true, created_at: "2015-09-06 12:16:56", updated_at: "2015-09-06 12:17:36">]
favorite_foods is in a polymorphic relationship with foods.
has_many :favorite_foods
has_many :favorites, through: :favorite_foods, source: :food
has_many :foods, through: :favorite_foods
In the favorite_food.rb model, I'm setting favorite foods by using this method:
def favorited_foods
favorite_foods.map(&:food_id).to_set
end
I need to be able to remove all instances of favorite_foods where the field "removed" equals true.
For example: from the console session above, I want favorite_foods to include food_id 3, not food_id 4.
What is a clean way to do this in the favorited_foods method? I tried incorporating .reject and it ignored it.
Thanks for any advice!
You can just simply do:
FavoriteFood.where(removed: true).delete_all
which will delete all the FavoriteFood records where removed is true.
If you want to do it using map, then you can do:
favorited_foods.select { |f| f.removed == true }.map { |f| f.delete }
but this is not recommended as it will call delete method once for each record. The better way is deleting them all using delete_all as shown above.
Can't you do something like:
def remove_favorited_foods
favorite_foods.each {|food| Food.destroy(food) if food.removed?}
end
You should also add has_many :foods, dependent: :destroy, through: :favorite_foods to your model to ensure that the favorite foods are destroyed accordingly. See this guide.
Related
I have the following models and relationships. I'm building a form and am wanting to initialize terms of the proposal for the form. How can I select a specific ProposalTerm by it's term_type_id to pass on to my fields_for block?
Proposal
class Proposal < ApplicationRecord
after_initialize :add_terms
has_many :terms, class_name: "ProposalTerm", dependent: :destroy
accepts_nested_attributes_for :terms
def add_terms
terms << ProposalTerm.first_or_initialize(type: TermType.signing_bonus)
end
end
ProposalTerm
class ProposalTerm < ApplicationRecord
include DisableInheritance
belongs_to :proposal
belongs_to :type, class_name: "TermType", foreign_key: "term_type_id"
def self.signing_bonus
find_by(type: TermType.signing_bonus)
end
end
My Attempt
>> #proposal.terms
=> #<ActiveRecord::Associations::CollectionProxy [#<ProposalTerm id: nil, season: nil, value: nil, is_guaranteed: false, term_type_id: 2, proposal_id: nil, created_at: nil, updated_at: nil>]>
>> #proposal.terms.where(term_type_id: 2)
=> #<ActiveRecord::AssociationRelation []>
I was able to figure out an answer. I had tried "select" but I was doing it incorrectly.
I had tried the following,
#proposal.terms.select(term_type_id: 2)
but that wasn't returning anything. I then did the following...
#proposal.terms.select { |t| t.term_type_id = 2 }
If you want to return just the first instance use "detect" ...
#proposal.terms.detect { |t| t.term_type_id = 2 } }
I'm inheriting a codebase for a Rails app that uses a blog engine -- and I am not making sense of how the models interact.
What I want to do is show the author who is associated with a specific article.
There is a table for articles called LinesArticle. An example entry:
<LinesArticle id: 2, title: "Example Article", sub_title: "Example Title",
content: "Example Content", published: true, published_at: "2017-08-22 00:00:00",
created_at: "2017-08-23 06:15:33", updated_at: "2017-08-23 06:15:36", slug: "whatever",
featured: false, document: nil, short_hero_image: "", teaser: "">
Next, there is a table for Authors called LinesAuthor. An example entry that should be associated:
#<LinesAuthor id: 1, name: "John Doe", email: "jd#examplesitedotcom", created_at: "2017-08-19 07:46:04", updated_at: "2017-08-19 07:46:04">
So if I compare these two tables, there isn't a connection between the data that would make sense for the models. So then I found LinesAuthorable that I think connects them. An entry:
<LinesAuthorable id: 2, author_id: 1, article_id: 2,
created_at: "2017-08-23 06:15:33", updated_at: "2017-08-23 06:15:33">
So my thinking is, if my controller calls an article like #articles = LinesArticle.last and I want to show the author who wrote that article by matching article_id to the matching result in LinesAuthorable and then query LinesAuthor for the matching author_id.
Reading through the documentation, I have now created models where an Article model belongs_to :lines_authorable and Author has_many :Lines_Authorable. If that is the right approach, how would you call that in the view to actually show the Author?
The LinesAuthorable table is acting as a through table, you can use a has_many through relationship to connect the data, something like:
class LinesArticle < ApplicationRecord
has_many :lines_authorables, foreign_key: :article_id
has_many :lines_authors, through: :lines_authorables
end
class LinesAuthor < ApplicationRecord
has_many :lines_authorables, foreign_key: :author_id
has_many :lines_articles, through: :lines_authorables
end
class LinesAuthorable < ApplicationRecord
belongs_to :line_article, foreign_key: :article_id
belongs_to :line_author, foreign_key: :author_id
end
You can then access an article's authors, and likewise an author's articles directly through the relationship:
#article = LinesArticle.first
#article.lines_authors #=> #<LinesAuthor::ActiveRecord_Relation...>
#author = LinesAuthor.first
#author.lines_articles #=> #<LinesArticle::ActiveRecord_Relation...>
I created 3 models as below, and used cocoon nested form to create associations between them.
class Unit < ApplicationRecord
has_many :mapping_categories, -> { distinct }, dependent: :destroy, inverse_of: :unit
accepts_nested_attributes_for :mapping_categories,
allow_destroy: true,
reject_if: :all_blank
end
class MappingCategory < ApplicationRecord
belongs_to :unit
has_many :mapping_items, -> { distinct }, dependent: :destroy, inverse_of: :mapping_category
accepts_nested_attributes_for :mapping_items,
allow_destroy: true
end
class MappingItem < ApplicationRecord
belongs_to :mapping_category
has_many :mapping_item_links
has_many :linked_mapping_items, through: :mapping_item_links, dependent: :destroy
end
Each mapping_item can have many other mapping_items through a joint table. In every mapping_item section in Unit form, this association is displayed as a select input.
When creating or updating Unit, there are many mapping_categories tabs in the Unit form, and there are many mapping_items sections in each mapping_category section.
For example, I have Mapping Category A and Mapping Category B. I want to add Mapping Item 1 to Mapping Category A and Mapping Item 2 to Mapping Category B. The question is: How to create the association between Mapping Item 1 and Mapping Item 2, as these two items are not saved yet?
Thanks in advance.
YOU CAN DO IT
You have to write right code
user = User.new(name: 'Jons', email: 'jons#qq.ww')
bank_account = BankAccount.new(number: 'JJ123456', user: user)
bank_account.save
in this way will be saved both raws and user and bank_account
in your case:
unit = Unit.new(mapping_categories: [mapping_category])
mapping_category = MappingCategory.new(mapping_items: [mapping_item])
mapping_item = MappingItem.new
unit.save
and if you wanna use nested_attributes, you just have to build hash with attributes
params = { mapping_categories: [mapping_items: [{.....}]}] }
Unit.create(params)
but you have to figure out with right nesting
From my understanding of your question... You can't. These items don't yet have ids and there for can't be associated with another model.
> contact = Contact.new(full_name: "Steve", email:"example#asdf.com")
=> #<Contact id: nil, full_name: "Steve", email: "example#asdf.com", created_at: nil, updated_at: nil>
> invoice = Invoice.new(contact_id: contact.id, invoice_type: "Something")
=> #<Invoice id: nil, contact_id: nil, invoice_type: "Something" created_at: nil, updated_at: nil>
> invoice.save
=> false
Does Rails allow conditional validations for validates_associated? I'm seeing the following on Rails 4.2.0. Am I trying to do it incorrectly?
Models:
class User < ActiveRecord::Base
has_many :books
validates_associated :books, if: :should_validate_book?
def should_validate_book?
return false
end
end
class Book < ActiveRecord::Base
belongs_to :user
validates_presence_of :title
end
The presence validation on Book's title attribute still runs (Rails console):
> u = User.create!
=> #<User id: 2, created_at: "2015-02-24 19:34:51", updated_at: "2015-02-24 19:34:51">
> u.books.build
=> #<Book id: nil, user_id: 3, title: nil, created_at: nil, updated_at: nil>
> u.valid?
=> false
> u.books.first.errors
=> #<ActiveModel::Errors:0x007fa256b210d8 #base=#<Book id: nil, user_id: 3, title: nil, created_at: nil, updated_at: nil>, #messages={:title=>["can't be blank"]}>
It turns out that validates_associated is ON by default for has_many relationships. To make it conditionally, you'd need to add validate: false to the has_many declaration:
has_many :books, validate: false
In Rails since time immemorial validates_associated has only taken a list of attributes. Besides that, you'd kind of be mixing up behavior between your models from what I gather from the criteria you've pasted. A better approach would be to adjust your validations in the Book model to account for the variation and let Book decide for itself whether an object should be validated or not.
How do I display my polymorphic associations in my view? I have the following model.
class Blog < ActiveRecord::Base
belongs_to :users
has_one :category, :as => :categorizable
end
class Category < ActiveRecord::Base
belongs_to :categorizable, :polymorphic => true
end
class User < ActiveRecord::Base
has_many :blogs
has_many :categories, :as => :categorizable
end
However, my problem is, I get a nil when trying to display the category.
rails console
=> user = User.first
=> user.blogs
[]
=> user.categories
[]
=> category = user.categories
=> category = Category.new
=> category.name = "Education"
=> category.save!
true
=> user.categories
[#<Category id: 1, name: "Education", categorizable_id: 1, categorizable_type: "User", created_at: "...", updated_at: "...">]
=> user.blogs.create(:title => "...", :body => "...", :category => category)
[#<Blog id: 1, user_id: 1, title: "...", body: "...", created_at: "...", updated_at: "...">]
=> blogs = user.blogs.all
=> blogs.each do |blog|
puts blog.category
end
nil
=> blogs.first.category
[#<Category id: 1, name: "Education", categorizable_id: 1, categorizable_type: "User", created_at: "...", updated_at: "...">]
I don't get it, why blog.category is returning nil when I use the each block? How do I display the entries of my polymorphic model through my views?
Update:
The design is, as a user, I want to be able to create categories, and assign them to my blogs. Each blogs has one category on them, which I'd like to access via the Category model as a categorizable blog.
It should be really nice, if it's working as-is as the logic is trying to say, but currently it does not. Polymorphic models are supposed to be elegant, but right now, polymorphic sucks for me and unusable. I'm still waiting for someone to help me provide with a better solution.
There is a flaw in the design. Consider the following:
user = User.create
cat = user.categories.create(:name=>"Education")
Now there is one category:
> cat
# Category id: 1, name: "Education" categorizable_id:1 categorizable_type: "User"
When you add a blog and assign the same category to it:
user.blogs.create(:category=>cat)
It overwrites the polymorphic type and id:
# UPDATE "categories" SET "categorizable_type" = 'Blog', "categorizable_id" = 1,
"updated_at" = '2011-02-27 06:20:29.968336' WHERE ("categories"."id" = 1)
And now the category is no longer associated with the user:
user.reload
user.categories # => []
You're really trying to model a many to many relationship here. I'd suggest adding join tables for UserCategory and BlogCategory, and getting rid of the polymorphism, which isn't helping.
Pardon me for my earlier answer. zetetic's answer makes absolute sense. Try avoiding polymorphs.
Update: I've decided that polymorphic model was not the way to go, and I've solved it with good ol' STI which does the same thing.