1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activerecord/CHANGELOG.md
eileencodes d6e12ffb7d
Enable scoping to apply to all queries
Similar to https://github.com/rails/rails/pull/40720
and https://github.com/rails/rails/pull/40805 this change allows for the
`scoping` method to apply to all queries in the block. Previously this
would only apply to queries on the class and not the instance. Ie
`Post.create`, Post.all`, but not `post.update`, or `post.delete`.

The change here will create a global scope that is applied to all
queries for a relation for the duration of the block.

Benefits:

This change allows applications to add a scope to any query for the
duration of a block. This is useful for applications using sharding to
be able to control the query without requiring a `default_scope`. This
is useful if you want to have more control over when a `scoping` is used
on a relation. This also brings `scoping` in parity with the behavior of
`default_scope` so there are less surprises between the behavior of
these two methods.

There are a caveats to this behavior:

1) The `scoping` only applies to objects of the same type. IE you cannot
scope `Post.where(blog_id: 1).scoping` and then expect `post.comments`
will apply `blog_id = 1` to the `Comment` query. This is not possible
because the scope is `posts.blog_id = 1` and we can't apply the `posts`
scope to a `comments` query. To solve this, scopes must be nested.
2) If a block is scoped to `all_queries` it cannot be unscoped without
exiting the block. I couldn't find a way around this but ActiveRecord
scoping is a bit complex and turning off `all_queries` when it's already
on in nested scoping blocks had interesting behavior that I decided was
best left out.
2021-02-11 16:03:55 -05:00

8.8 KiB

  • Add ability to apply scoping to all_queries.

    Some applications may want to use the scoping method but previously it only worked on certain types of queries. This change allows the scoping method to apply to all queries for a model in a block.

    Post.where(blog_id: post.blog_id).scoping(all_queries: true) do
      post.update(title: "a post title") # adds `posts.blog_id = 1` to the query
    end
    

    Eileen M. Uchitelle

  • Switch to database adapter return type for ActiveRecord::Calculations.calculate when called with :average (aliased as ActiveRecord::Calculations.average)

    Previously average aggregation returned BigDecimal for everything which supported to_d. This was especially weird for floating point numbers.

    Database adapters today map database column types to Ruby types more accurately than 10 years ago. So now we simply pass them on (except for special cases which are not Numeric).

    # With the following schema:
    create_table "measurements" do |t|
      t.float "temperature"
    end
    
    # Before:
    Measurement.average(:temperature).class
    # => BigDecimal
    
    # After:
    Measurement.average(:temperature).class
    # => Float
    

    Josua Schmid

  • PostgreSQL: handle timestamp with time zone columns correctly in schema.rb.

    Previously they dumped as t.datetime :column_name, now they dump as t.timestamptz :column_name, and are created as timestamptz columns when the schema is loaded.

    Alex Ghiculescu

  • Add ActiveRecord::Base.connection.with_advisory_lock.

    This method allow applications to obtain an exclusive session level advisory lock, if available, for the duration of the block.

    If another session already have the lock, the method will return false and the block will not be executed.

    Rafael Mendonça França

  • Removing trailing whitespace when matching columns in ActiveRecord::Sanitization.disallow_raw_sql!.

    Gannon McGibbon, Adrian Hirt

  • Expose a way for applications to set a primary_abstract_class

    Multiple database applications that use a primary abstract class that is not named ApplicationRecord can now set a specific class to be the primary_abstract_class.

    class PrimaryApplicationRecord
      self.primary_abstract_class
    end
    

    When an application boots it automatically connects to the primary or first database in the database configuration file. In a multiple database application that then call connects_to needs to know that the default connection is the same as the ApplicationRecord connection. However some applications have a differently named ApplicationRecord. This prevents Active Record from opening duplicate connections to the same database.

    Eileen M. Uchitelle, John Crepezzi

  • Support hash config for structure_dump_flags and structure_load_flags flags Now that Active Record supports multiple databases configuration we need a way to pass specific flags for dump/load databases since the options are not the same for different adapters. We can use in the original way:

    ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = ['--no-defaults', '--skip-add-drop-table']
    #or
    ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = '--no-defaults --skip-add-drop-table'
    

    And also use it passing a hash, with one or more keys, where the key is the adapter

    ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = {
      mysql2: ['--no-defaults', '--skip-add-drop-table'],
      postgres: '--no-tablespaces'
    }
    

    Gustavo Gonzalez

  • Connection specification now passes the "url" key as a configuration for the adapter if the "url" protocol is "jdbc", "http", or "https". Previously only urls with the "jdbc" prefix were passed to the Active Record Adapter, others are assumed to be adapter specification urls.

    Fixes #41137.

    Jonathan Bracy

  • Allow to opt-out of strict_loading mode on a per-record base.

    This is useful when strict loading is enabled application wide or on a model level.

    class User < ApplicationRecord
      has_many :bookmarks
      has_many :articles, strict_loading: true
    end
    
    user = User.first
    user.articles                        # => ActiveRecord::StrictLoadingViolationError
    user.bookmarks                       # => #<ActiveRecord::Associations::CollectionProxy>
    
    user.strict_loading!(true)           # => true
    user.bookmarks                       # => ActiveRecord::StrictLoadingViolationError
    
    user.strict_loading!(false)          # => false
    user.bookmarks                       # => #<ActiveRecord::Associations::CollectionProxy>
    user.articles.strict_loading!(false) # => #<ActiveRecord::Associations::CollectionProxy>
    

    Ayrton De Craene

  • Add FinderMethods#sole and #find_sole_by to find and assert the presence of exactly one record.

    Used when you need a single row, but also want to assert that there aren't multiple rows matching the condition; especially for when database constraints aren't enough or are impractical.

    Product.where(["price = %?", price]).sole
    # => ActiveRecord::RecordNotFound      (if no Product with given price)
    # => #<Product ...>                    (if one Product with given price)
    # => ActiveRecord::SoleRecordExceeded  (if more than one Product with given price)
    
    user.api_keys.find_sole_by(key: key)
    # as above
    

    Asherah Connor

  • Makes ActiveRecord::AttributeMethods::Query respect the getter overrides defined in the model.

    Before:

    class User
      def admin
        false # Overriding the getter to always return false
      end
    end
    
    user = User.first
    user.update(admin: true)
    
    user.admin # false (as expected, due to the getter overwrite)
    user.admin? # true (not expected, returned the DB column value)
    

    After this commit, user.admin? above returns false, as expected.

    Fixes #40771.

    Felipe

  • Allow delegated_type to be specified primary_key and foreign_key.

    Since delegated_type assumes that the foreign_key ends with _id, singular_id defined by it does not work when the foreign_key does not end with id. This change fixes it by taking into account primary_key and foreign_key in the options.

    Ryota Egusa

  • Expose an invert_where method that will invert all scope conditions.

    class User
      scope :active, -> { where(accepted: true, locked: false) }
    end
    
    User.active
    # ... WHERE `accepted` = 1 AND `locked` = 0
    
    User.active.invert_where
    # ... WHERE NOT (`accepted` = 1 AND `locked` = 0)
    

    Kevin Deisz

  • Restore possibility of passing false to :polymorphic option of belongs_to.

    Previously, passing false would trigger the option validation logic to throw an error saying :polymorphic would not be a valid option.

    glaszig

  • Remove deprecated database kwarg from connected_to.

    Eileen M. Uchitelle, John Crepezzi

  • Allow adding nonnamed expression indexes to be revertible.

    Fixes #40732.

    Previously, the following code would raise an error, when executed while rolling back, and the index name should be specified explicitly. Now, the index name is inferred automatically.

    add_index(:items, "to_tsvector('english', description)")
    

    fatkodima

  • Only warn about negative enums if a positive form that would cause conflicts exists.

    Fixes #39065.

    Alex Ghiculescu

  • Add option to run default_scope on all queries.

    Previously, a default_scope would only run on select or insert queries. In some cases, like non-Rails tenant sharding solutions, it may be desirable to run default_scope on all queries in order to ensure queries are including a foreign key for the shard (ie blog_id).

    Now applications can add an option to run on all queries including select, insert, delete, and update by adding an all_queries option to the default scope definition.

    class Article < ApplicationRecord
      default_scope -> { where(blog_id: Current.blog.id) }, all_queries: true
    end
    

    Eileen M. Uchitelle

  • Add where.associated to check for the presence of an association.

    # Before:
    account.users.joins(:contact).where.not(contact_id: nil)
    
    # After:
    account.users.where.associated(:contact)
    

    Also mirrors where.missing.

    Kasper Timm Hansen

  • Allow constructors (build_association and create_association) on has_one :through associations.

    Santiago Perez Perret

Please check 6-1-stable for previous changes.