aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations
Commit message (Collapse)AuthorAgeFilesLines
* Association scope's own order should be prioritized over through scope's orderRyuta Kamizono2018-02-181-2/+2
| | | | | | | | | | 3acc5d6 was changed the order of scope evaluation from through scope to the association's own scope to be prioritized over the through scope. But the sorting order will be prioritized that is evaluated first. It is unintentional effect, association scope's sorting order should be prioritized as well. Fixes #32008.
* Rails 6 requires Ruby 2.3+Jeremy Daer2018-02-171-5/+1
|
* Fix marshaling of models with `has_many :through` associationsfatkodima2018-02-081-2/+2
|
* Revert "✂️"Ryuta Kamizono2018-02-071-1/+2
| | | | | | | | This reverts commit 487a1061cc496455dfe5ee84d1e49d509c1675b5. This `#--` is necessary for the doc of `distinct`. [ci skip]
* ✂️schneems2018-02-061-2/+1
|
* Avoid extra calls to to_sDaniel Colson2018-01-292-3/+3
| | | | | | | With #31615 `type_for_attribute` accepts either a symbol as well as a string. `has_attribute?` and `attribute_alias` also accept either. Since these methods call `to_s` on the argument, we no longer need to do that at the call site.
* Avoid passing unnecessary arguments to relationDaniel Colson2018-01-242-2/+2
| | | | | | | | | | | | Most of the time the table and predicate_builder passed to Relation.new are exactly the arel_table and predicate builder of the given klass. This uses klass.arel_table and klass.predicate_builder as the defaults, so we don't have to pass them in most cases. This does change the signaure of both Relation and AssocationRelation. Are we ok with that?
* Fix building has_one through recordRyuta Kamizono2018-01-233-14/+10
| | | | Fixes #31762.
* Don't update counter cache when through record was not destroyedEugene Kenny2018-01-141-1/+1
| | | | | | When removing a record from a has many through association, the counter cache was being updated even if the through record halted the callback chain and prevented itself from being destroyed.
* Don't pass garbage args to alias trackerRyuta Kamizono2018-01-141-10/+2
| | | | | | | | | | | | | This is a complete fix to #30995. Originally alias tracker will only track table aliases on `Arel::Nodes::Join`, other args are ignored. Since c5ab6e5, parent aliases hash will be passed then it caused the regression #30995. It is enough to pass list of `Arel::Nodes::Join` simply, not need to pass garbage args which will be ignored.
* Merge pull request #23146 from piotrj/issue_18424Ryuta Kamizono2018-01-111-0/+1
|\ | | | | | | When deleting through records, take into account association conditions
| * When deleting through records, take into account association conditionsPiotr Jakubowski2016-05-041-8/+9
| | | | | | | | | | | | | | | | Fixes #18424. When deleting through records, it didn't take into account the conditions that may have been affecting join model table, but was defined in association definition.
* | Fix `stale_state` for nested `has_many :through` associationsRyuta Kamizono2018-01-101-2/+13
| | | | | | | | Need reloading when through record has replaced.
* | Merge pull request #16314 from ↵Ryuta Kamizono2018-01-101-1/+1
|\ \ | | | | | | | | | | | | | | | zoltankiss/allow-nested-has-many-associations-on-unpersisted-parent-instances fix nested `has many :through` associations on unpersisted parent instances
| * | Fix nested `has many :through` associations on unpersisted instancesZoltan Kiss2015-03-261-1/+1
| | | | | | | | | | | | Fixes: #16313
* | | Bring back passing single record support for `Preloader`Ryuta Kamizono2018-01-101-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I removed redundant `Array.wrap(records)` since `Preloader` is nodoc class and Active Record always pass `records` as an array to `Preloader`. But if users relies on that behavior, it is not worth dropping its behavior. Fixes #31661.
* | | Fix deleting through records when using has_many through with `source_type`Ryuta Kamizono2018-01-081-8/+6
| | | | | | | | | | | | | | | | | | | | | | | | Currently deleting through records doesn't respect `source_type`. It should not be ignored in that case. Related #23209. Fixes #24116.
* | | Simply use `scope.delete_all` instead of constructing delete managerRyuta Kamizono2018-01-071-8/+1
| | |
* | | Remove passing argument to singular and collection association readersRyuta Kamizono2018-01-051-2/+2
| | | | | | | | | | | | Follow up of 09cac8c67afdc4b2a1c6ae07931ddc082629b277.
* | | Merge pull request #27561 from fishbrain/count-all-in-has-many-associationRyuta Kamizono2018-01-031-1/+1
|\ \ \ | | | | | | | | | | | | Use `count(:all)` in HasManyAssociation#count_records
| * | | Use `count(:all)` in HasManyAssociation#count_recordsKlas Eskilson2017-02-071-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: Calling `count` on an association can cause invalid SQL queries to be created where the `SELECT COUNT(a, b, c)` function receives multiple columns. This will cause a `StatementInvalid` exception later on. Solution: Use `count(:all)`, which generates a `SELECT COUNT(*)...` query independently of the association. This also includes a test case that, before the fix, broke.
* | | | Remove `association_primary_key_type` from `AssociationReflection` and ↵Ryuta Kamizono2018-01-011-2/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `ThroughReflection` This method was introduced in #26718, which is internally used only in `CollectionAssociation`. There is no need to be in the reflection classes.
* | | | Bugfix foreign key replacement in inverse associationBogdan Gusiev2017-12-272-13/+8
| | | | | | | | | | | | | | | | when model is added to collection association
* | | | Fix conflicts `counter_cache` with `touch: true` by optimistic locking.bogdanvlviv2017-12-122-5/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ``` # create_table :posts do |t| # t.integer :comments_count, default: 0 # t.integer :lock_version # t.timestamps # end class Post < ApplicationRecord end # create_table :comments do |t| # t.belongs_to :post # end class Comment < ApplicationRecord belongs_to :post, touch: true, counter_cache: true end ``` Before: ``` post = Post.create! # => begin transaction INSERT INTO "posts" ("created_at", "updated_at", "lock_version") VALUES ("2017-12-11 21:27:11.387397", "2017-12-11 21:27:11.387397", 0) commit transaction comment = Comment.create!(post: post) # => begin transaction INSERT INTO "comments" ("post_id") VALUES (1) UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1, "lock_version" = COALESCE("lock_version", 0) + 1 WHERE "posts"."id" = 1 UPDATE "posts" SET "updated_at" = '2017-12-11 21:27:11.398330', "lock_version" = 1 WHERE "posts"."id" = 1 AND "posts"."lock_version" = 0 rollback transaction # => ActiveRecord::StaleObjectError: Attempted to touch a stale object: Post. Comment.take.destroy! # => begin transaction DELETE FROM "comments" WHERE "comments"."id" = 1 UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) - 1, "lock_version" = COALESCE("lock_version", 0) + 1 WHERE "posts"."id" = 1 UPDATE "posts" SET "updated_at" = '2017-12-11 21:42:47.785901', "lock_version" = 1 WHERE "posts"."id" = 1 AND "posts"."lock_version" = 0 rollback transaction # => ActiveRecord::StaleObjectError: Attempted to touch a stale object: Post. ``` After: ``` post = Post.create! # => begin transaction INSERT INTO "posts" ("created_at", "updated_at", "lock_version") VALUES ("2017-12-11 21:27:11.387397", "2017-12-11 21:27:11.387397", 0) commit transaction comment = Comment.create!(post: post) # => begin transaction INSERT INTO "comments" ("post_id") VALUES (1) UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1, "lock_version" = COALESCE("lock_version", 0) + 1, "updated_at" = '2017-12-11 21:37:09.802642' WHERE "posts"."id" = 1 commit transaction comment.destroy! # => begin transaction DELETE FROM "comments" WHERE "comments"."id" = 1 UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) - 1, "lock_version" = COALESCE("lock_version", 0) + 1, "updated_at" = '2017-12-11 21:39:02.685520' WHERE "posts"."id" = 1 commit transaction ``` Fixes #31199.
* | | | Provide arguments to RecordNotFoundNikita Misharin2017-11-251-1/+7
| | | |
* | | | Consolidate duplicated `to_ary`/`to_a` definitions in `Relation` and ↵Ryuta Kamizono2017-11-101-4/+6
| | | | | | | | | | | | | | | | `CollectionProxy`
* | | | Remove useless preloader classesRyuta Kamizono2017-11-1010-104/+15
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | They are only different by one line of code which doesn't deserve a hierarchy of 7 classes. Closes #31079. [Ryuta Kamizono & Bogdan Gusiev]
* | | | Don't expose accessors which are internal used onlyRyuta Kamizono2017-11-082-3/+6
| | | |
* | | | Don't expose internal methods in `Preloader::ThroughAssociation`Ryuta Kamizono2017-11-081-8/+7
| | | | | | | | | | | | | | | | `through_reflection` and `source_reflection` are used only in the class.
* | | | Remove useless `associated_records_by_owner`Ryuta Kamizono2017-11-072-16/+10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `associated_records_by_owner` had returned customizing result before calling `associate_records_to_owner` for through association subclasses. Since #22115, `associate_records_to_owner` is called in the method and not returned owner and result pairs. Removing the method will reduce method call and block call nesting.
* | | | Refactor Preloader CodeBogdan Gusiev2017-11-063-81/+40
| | | |
* | | | Fix preloading polymorphic multi-level through associationRyuta Kamizono2017-11-061-1/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This is partially fixed by e617fb57 when through association has already loaded. Otherwise, second level through association should respect `preload_scope`. Fixes #30242. Closes #30076. [Ryuta Kamizono & CicholGricenchos]
* | | | Fix preloading polymorphic association when through association has already ↵Ryuta Kamizono2017-11-061-4/+16
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | loaded If through association has already loaded, `source_type` is ignored to loaded through records. The loaded records should be filtered by `source_type` in that case. Fixes #30904.
* | | | Merge pull request #31005 from shuheiktgw/remove_unnecessary_semicolonsMatthew Draper2017-10-281-1/+1
| | | | | | | | | | | | | | | | Removed unnecessary semicolons
* | | | fix initial countpavel2017-10-271-1/+1
| | | |
* | | | Ensure associations doesn't table name collide with aliased joinsRyuta Kamizono2017-10-241-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | Currently alias tracker only refer a table name, doesn't respect an alias name. Should use `join.left.name` rather than `join.left.table_name`.
* | | | Ensure associations doesn't table name collide with string joinsRyuta Kamizono2017-10-231-4/+6
| | | | | | | | | | | | | | | | | | | | Currently we have no test for alias tracking with string joins. I've add test case for that to catch a future regression.
* | | | [Active Record] require => require_relativeAkira Matsuda2017-10-213-3/+3
| | | | | | | | | | | | | | | | This basically reverts 9d4f79d3d394edb74fa2192e5d9ad7b09ce50c6d
* | | | Remove association(true) references from docs [ci skip]Eugene Kenny2017-10-161-4/+0
| | | | | | | | | | | | | | | | | | | | Passing `true` to force an association to reload its records from the database was deprecated in 5.0 and removed in 5.1.
* | | | Joined tables in association scope doesn't use the same aliases with the ↵Ryuta Kamizono2017-10-093-13/+18
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | parent relation's aliases Building association scope in join dependency should respect the parent relation's aliases to avoid using the same alias name more than once. Fixes #30681.
* | | | Decouple building `AliasTracker` from `JoinDependency`Ryuta Kamizono2017-10-083-14/+7
| | | | | | | | | | | | | | | | | | | | This is preparation to respect parent relation's alias tracking for fixing #30681.
* | | | Move duplicated code to `delete_or_destroy` in `CollectionAssociation`Ryuta Kamizono2017-10-061-4/+2
| | | |
* | | | Ensure `AliasTracker` respects a custom table nameRyuta Kamizono2017-09-302-2/+2
| | | |
* | | | PERF: Partially recover some performance when preloading.Guo Xiang Tan2017-09-261-9/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Benchmark Script: ``` require 'active_record' require 'benchmark/ips' ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL')) ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table :users, force: true do |t| t.string :name, :email t.integer :topic_id t.timestamps null: false end create_table :topics, force: true do |t| t.string :title t.timestamps null: false end end attributes = { name: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', email: 'foobar@email.com' } class Topic < ActiveRecord::Base has_many :users end class User < ActiveRecord::Base belongs_to :topic end 100.times do User.create!(attributes) end users = User.first(50) Topic.create!(title: 'This is a topic', users: users) Benchmark.ips do |x| x.config(time: 10, warmup: 5) x.report("preload") do User.includes(:topic).all.to_a end end ``` Before: ``` Calculating ------------------------------------- preload 40.000 i/100ms ------------------------------------------------- preload 407.962 (± 1.5%) i/s - 4.080k ``` After: ``` alculating ------------------------------------- preload 43.000 i/100ms ------------------------------------------------- preload 427.567 (± 1.6%) i/s - 4.300k ```
* | | | Remove unused delegation to `reflection.options` in `Preloader::Association`Ryuta Kamizono2017-09-182-4/+1
| | | |
* | | | The name of the key on the associated record is abstracted as ↵Ryuta Kamizono2017-09-184-14/+5
| | | | | | | | | | | | | | | | `reflection.join_primary_key`
* | | | The name of the key on the owner is abstracted as `reflection.join_foreign_key`Ryuta Kamizono2017-09-184-17/+5
| | | |
* | | | Extract `associate_records_to_owner` to refactor `Preloader::Association`Ryuta Kamizono2017-09-183-20/+14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Since we have `Preloader#preload`, `Preloader::Association#preload` is a little confusing. And also, since the `preload` method is an abstract method, it is hard to read where `associated_records_by_owner` is called. This refactors `Preloader::Association` to ease to read where `associated_records_by_owner` is called.
* | | | Remove the code that swapping `scope` and `options`Ryuta Kamizono2017-09-181-5/+0
| | | | | | | | | | | | | | | | `options` is never assigned to `scope` as long as using splat hash.
* | | | Early return if `records.empty?` in `Preloader#preload`Ryuta Kamizono2017-09-181-3/+3
| | | |