I have a model Task, and each task has_many other tasks:
Class Task < ActiveRecord::Base
belongs_to :sub_task, class_name: Task.name, touch: true
has_many :sub_tasks, class_name: Task.name, foreign_key: :sub_task_id, dependent: :destroy
end
Can I add a counter cache to the number of sub_tasks each task has? How?
Yes you can add the counter cache.
class Task < ActiveRecord::Base
belongs_to :sub_task, class_name: Task.name, touch: true, counter_cache: :sub_tasks_count
has_many :sub_tasks, class_name: Task.name, foreign_key: :sub_task_id, dependent: :destroy
end
You need to create a migration to add a new column named sub_tasks_count to the Tasks table.
There's no need for doing what #Rubysmith wrote, you can just:
class Task < ActiveRecord::Base
belongs_to :task, counter_cache: true
has_many :tasks, dependent: :destroy
end
Migration:
class AddTaskCounterToTasks < ActiveRecord::Migration
def change
add_column :tasks, :tasks_count, :integer, default: 0, null: false
end
end
Related
My Models are like:
class ThreeDModel < ApplicationRecord
has_many :three_d_model_animations
has_many :animations, through: :three_d_model_animations
has_many :three_d_model_images, dependent: :destroy
has_many :three_d_garments
has_one :model_efm
has_one :model_edm
end
2nd:
class ThreeDModelAnimation < ApplicationRecord
belongs_to :animation
belongs_to :three_d_model
validates_presence_of :animation_file
#validates_presence_of :motion
mount_uploader :animation_file, ThreeDModelAnimationUploader
enum animation_motions: {
'Run': 'run',
'Turn Aroun (100 Frames)': 'turn_around_100_frames',
'Turn Around (300 Frames)': 'turn_around_300_frames',
'Walk (58 Frames)': 'walk_58_frames',
'Walk (92 Frames)': 'walk_92_frames'
}
end
3rd:
class Animation < ApplicationRecord
has_many :three_d_model_animations
has_many :three_d_models, through: :three_d_model_animations
mount_uploader :video, AnimationVideoUploader
validates_presence_of :video
validates :name, :frames, :loop_start, :loop_end, presence: true
end
Now my query is:
#model = ThreeDModel.where(id: params[:id]).includes(:model_efm, :model_edm, :three_d_model_images, :three_d_model_animations, :animations).last
which gives the following message by the bullet gem:
AVOID eager loading detected
ThreeDModel => [:three_d_model_animations, :animations]
is there a better way so that n+1 could be avoided. Thanks in advance
I have a model Product which has_many :taxons, through: :classifications. How to implement acts_as_taggable_on to implement the tagging of products with the names of taxons instead of the the tags from the tag table? Is it possible or not?
#app/models/product.rb
class Product < ActiveRecord::Base
translates :name
accepts_nested_attributes_for :translations, allow_destroy: true, reject_if: :all_blank
# Associations
has_many :classifications, inverse_of: :product, dependent: :delete_all
has_many :taxons, through: :classifications
end
#app/models/classification.rb
class Classification < ActiveRecord::Base
self.table_name = 'products_taxons'
# Associations
belongs_to :product, inverse_of: :classifications
belongs_to :taxon, inverse_of: :classifications, touch: true
end
#app/models/taxon.rb
class Taxon < ActiveRecord::Base
translates :name
globalize_accessors
has_many :classifications, inverse_of: :taxon, dependent: :delete_all
has_many :products, through: :classifications
end
I'm deleting a Place and it's cascading the rows of PlaceUpload, but I also would like to cascade the rows of Match and TagCostumer while I am deleting a Place. How can I do that?
class Place < ActiveRecord::Base
has_many :place_uploads
end
class PlaceUpload < ActiveRecord::Base
belongs_to :place
has_many :matches
has_many :tags_customers
end
class TagsCustomer < ActiveRecord::Base
belongs_to :place_upload
belongs_to :tag
end
class Match < ActiveRecord::Base
belongs_to :place_upload
belongs_to :customer
end
The solution was to use destroy and create a callback to automatically do a deep cascading.
class Place < ActiveRecord::Base
before_destroy :delete_children_objects
has_many :place_uploads, :dependent => :destroy
protected
def delete_children_objects
#places = PlaceUpload.where(place_id: id)
#places.each do |place|
TagsCustomer.where(place_upload_id: place.id).destroy_all
Match.where(place_upload_id: place.id).destroy_all
end
end
end
I have models - Test, Question, and TeacherAnswers.
test.rb
class Test < ActiveRecord::Base
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions
end
question.rb
class Question < ActiveRecord::Base
belongs_to :test
has_many :teacher_answers, dependent: :destroy
accepts_nested_attributes_for :teacher_answers
end
teacher_answer.rb
class TeacherAnswer < ActiveRecord::Base
belongs_to :question
end
and controllers
test_controller.rb
---
standard scaffold code
---
def test_params
params.require(:test).permit(:title,
questions_attributes: [:question_text, :test_id, teacher_answers_attributes: [:teacher_answer_text, :correct, :question_id]],)
end
question_controller.rb
---
standard scaffold code
---
def question_params
params.require(:question).permit(:question_text, :test_id,
teacher_answers_attributes: [:teacher_answer_text, :correct, :question_id])
end
When creating new test with questions and answers it creates everything correctly, but when updating:
can't remove questions
when saving it doesn't update the questions and answers, but saves new ones with already old ones i.e. when updating test with 2 questions, after saving it will have 4 questions.
You should permit :id and :_destroy in the test_params like below for the update and delete to work correctly.
def test_params
params.require(:test).permit(:title, questions_attributes: [:id, :question_text, :test_id, :_destroy, teacher_answers_attributes: [:id, :teacher_answer_text, :correct, :question_id, :_destroy]])
end
Update
You should also add allow_destroy: true for test.rb and question.rb
class Test < ActiveRecord::Base
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions, allow_destroy: true
end
class Question < ActiveRecord::Base
belongs_to :test
has_many :teacher_answers, dependent: :destroy
accepts_nested_attributes_for :teacher_answers, allow_destroy: true
end
accepts_nested_attributes_for by default does not allow deletion, use delete: true
Pass id to form, via hidden tag
I have a Store model, Product model, and a StoreProduct model to form a many-to-many relationship between stores and products.
The StoreProduct model, on top of store_id and product_id, has an attribute price:float.
I'm struggling to figure out how I can design a form to create a new product given the store. Is it better off making a form for a new StoreProduct object? What is the conventional way of doing this?
store.rb
class Store < ActiveRecord::Base
validates :name, presence: true
has_many :store_products
has_many :products, through: :store_products
end
product.rb
class Product < ActiveRecord::Base
validates :name, presence: true
validates_numericality_of :price, on: :create
has_many :reviews
has_many :users, through: :reviews
has_many :store_products
has_many :stores, through: :store_products
end
store_product.rb
class StoreProduct < ActiveRecord::Base
belongs_to :store
belongs_to :product
end