Changelog for v2.0
November 24, 2016 ยท View on GitHub
This is a new major release of Ecto that removes previously deprecated features and introduces a series of improvements and features based on db_connection.
Ecto 2.0 requires Elixir 1.2+.
Highlights
Revamped changesets
Due to feedback, we have made three important changes to changesets:
changeset.modelhas been renamed tochangeset.data(we no longer have "models" in Ecto)- Passing required and optional fields to
cast/4is deprecated in favor ofcast/3andvalidate_required/3 - The
:emptyatom incast(source, :empty, required, optional)has been deprecated, please use an empty map or:invalidinstead
To summarize those changes, instead of:
def changeset(user, params \\ :empty) do
user
|> cast(params, [:name], [:age])
end
One should write:
def changeset(user, params \\ %{}) do
user
|> cast(params, [:name, :age])
|> validate_required([:name])
end
Subqueries
Ecto v2.0 introduces Ecto.Query.subquery/1 that will convert any query into a subquery to be used either as part of a from or a join. For example, if you want to calculate the average number of visits per posts, you can write:
query = from p in Post, select: avg(p.visits)
TestRepo.all(query) #=> [#Decimal<1743>]
However, if you want to calculate the average only across the top 10 most visited, you need subqueries:
query = from p in Post, select: [:visits], order_by: [desc: :visits], limit: 10
TestRepo.all(from p in subquery(query), select: avg(p.visits)) #=> [#Decimal<4682>]
Or alternatively, for the particular example where you are calculating aggregates, use the new Repo.aggregate function that handles those concerns automatically for you:
# Average across all
TestRepo.aggregate(Post, :avg, :visits) #=> #Decimal<1743>
# Average across top 10
query = from Post, order_by: [desc: :visits], limit: 10
TestRepo.aggregate(query, :avg, :visits) #=> #Decimal<4682>
Concurrent transactional tests
Ecto has reimplemented the Ecto.Adapters.SQL.Sandbox pool used for testing to use an ownership mechanism. This allows tests to safely run concurrently even when depending on the database. To use, declare sandbox as your pool in your repository configuration, as before:
pool: Ecto.Adapters.SQL.Sandbox
Now at the end of your test_helper.exs, set the sandbox mode to :manual in order to disable the automatic checkout of connections:
Ecto.Adapters.SQL.Sandbox.mode(TestRepo, :manual)
And now checkout the connection in the setup block of every test case that needs the database:
setup do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(TestRepo)
end
The previous sandbox API, which used begin_test_transaction and restart_test_transaction, is no longer supported.
Insert all
Ecto now allows developers to insert multiple entries at once via Ecto.Repo.insert_all/3:
Ecto.Repo.insert_all Post, [%{title: "foo"}, %{title: "bar"}]
Similar to update_all/3, insert_all/3 is meant to be closer to the datastore and it won't automatically handle autogenerated fields like inserted_at or updated_at timestamps. insert_all/3 also adds to Ecto the ability to introduce entries to the database without an underlying Ecto.Schema by simply giving a table name:
Ecto.Repo.insert_all "some_table", [%{hello: "foo"}, %{hello: "bar"}]
Many to many
Ecto 2.0 supports many_to_many associations:
defmodule Post do
use Ecto.Schema
schema "posts" do
many_to_many :tags, Tag, join_through: "posts_tags"
end
end
The join_through option can be a string, representing a table that will have both post_id and tag_id columns, or a regular schema, like PostTag, which would also handle primary keys and autogenerate fields. This is an improvement over has_many :through as has_many :through relationships are read-only, while many_to_many supports also inserting, updating and deleting associated entries through changeset, as we will see next.
Improved association support
Ecto now supports belongs_to and many_to_many associations to be cast or changed via changesets, beyond has_one, has_many and embeds. Not only that, Ecto supports associations and embeds to be defined directly from the struct on insertion. For example, one can call:
Repo.insert! %Permalink{
url: "//root",
post: %Post{
title: "A permalink belongs to a post which we are inserting",
comments: [
%Comment{text: "child 1"},
%Comment{text: "child 2"},
]
}
}
This allows developers to easily insert a tree of structs into the database, be it when seeding data for production or during tests.
Finally, Ecto now allows putting existing records in changesets, and the proper changes will be reflected in both structs and the database. For example, you may retrieve the permalink above and associate it to another existing post:
permalink
|> Ecto.Changeset.change
|> Ecto.Changeset.put_assoc(:post, existing_post)
|> Repo.update!
Application Repo Configuration
Ecto now requires you to explicitly configure your repo's in your top level config. You can avoid this warning by passing the -r flag or by setting the repositories managed by this application in your config/config.exs:
config :my_app, ecto_repos: [...]
The configuration may be an empty list if it does not define any repo.
v2.0.6 (2016-11-24)
Enhancements
- [Ecto.Adapters.MySQL] Update mariaex dependency that supports the new DBConnection callbacks
Bug fixes
- [Ecto.Changeset] Ensure
validates_acceptanceworks even when parameters are nil
v2.0.5 (2016-09-07)
Enhancements
- [Ecto.Associations] Support
on_replace: :updateonbelongs_toandhas_one - [Ecto.Changeset] Allow
validate_numberto compare decimal with number - [Ecto.Changeset] Allow structs to be given to
put_assocto avoid unecessary casting to changesets - [Ecto.LogEntry] Add
:sourcemetadata to log entries - [Ecto.LogEntry] Add
:ansi_colorkey to log entry. This will be used by the Logger in future Elixir versions to customize logging in the console handler - [Ecto.Migration] Support the options in
Ecto.Schema.timestampinEcto.Migration.timestamp - [Ecto.Repo] Support
{source, schema}notation ininsert_all - [Ecto.Schema] Disable lexical tracker for association modules. This will prevent the compiler from creating a compile-time dependency on the association module
Bug fixes
- [Ecto.Changeset] Ensure changing a changeset does not ignore previous changes
- [Ecto.Changeset] Use the default value when an empty value is received on cast
v2.0.4 (2016-08-09)
Enhancements
- [Ecto.Query] Allow macros to be used in joins
- [Ecto.Changeset] Introduce the concept of empty values into changesets so empty strings do not pass through
Bug fixes
- [Ecto.Changeset] Treat
:replacechangesets the same:deleteinget_field
v2.0.3 (2016-07-30)
Enhancements
- [Ecto.Changeset] Support empty fields in Date, Time and DateTime to cast to nil
- [Ecto.Adapters.SQL] Allow to alter primary keys on tables
- [Ecto.Changeset] Allow unique constraints for join tables in
many_to_manyto propagate to association changeset - [Ecto.Changeset] Provide more flexible constraint matching (for example, suffix based)
- [Ecto.Adapters.Postgres] Add support for commenting on PostgreSQL migrations
- [Ecto.Changeset] Add acceptance validation
- [Ecto.Repo] Export
Ecto.Adapters.SQL.query/query!directly in Ecto.Repo module for SQL adapters - [Ecto.Multi] Allow multi names to be any type
Bug fixes
- [Ecto.Associations] Fix bug when association preloading on a schema with composite primary key
- [Ecto.Changeset] Fix changeset having mixed changeset/struct embeds when parent insert/update fails
- [Ecto.Changeset] Properly raise when trying to get/fetch a not loaded association
- [Ecto.DateTime] Raise
Ecto.CastErrorfor errors in date/time instead ofArgumentError - [Ecto.Query] Keep lists of integers as lists when inspecting queries
- [Ecto.Adapters.SQL.Sandbox] Ensure we get a fresh, non-sandboxed checkout when running migrations under ownership
- [Ecto.DateTime] Don't crash on casting dates without year
v2.0.2 (2016-06-27)
Bug fixes
- [Ecto.Time] Add minute, second and microsecond handling to Ecto.Time
- [Ecto.Adapters.SQL.Sandbox] Fix disconnections inside the sandbox
v2.0.1 (2016-06-21)
Bug fixes
- [Postgres] Ensures the
:postgrexapplication is restarted after running migrations
v2.0.0 (2016-06-21)
Backwards incompatible changes
- [Changeset]
changeset.modelhas been renamed tochangeset.data - [Changeset]
changeset.optionalhas been removed - [Changeset]
changeset.errorsnow always returns tuple{String.t, Keyword.t}in its values - [DateTime] The "Z" (UTC) at the end of an ISO 8601 time has been removed as UTC should not be assumed
- [LogEntry] Overhaul log entry and store times in :native units
- [Repo]
Ecto.StaleModelErrorhas been renamed toEcto.StaleEntryError - [Repo] Poolboy now expects
:pool_overflowoption instead of:max_overflow(keep in mind though using such option is discourage altogether as it establishes short-lived connections to the database, likely being worse to performance in both short- and long-term) - [Repo]
Repo.insert/2will now send only non-nil fields from the struct to the storage (in previous versions, all fields from the struct were sent to the database) - [Repo]
Ecto.Pools.PoolboyandEcto.Pools.SojournBrokerhave been removed in favor ofDBConnection.PoolboyandDBConnection.Sojourn - [Repo]
:timeoutinRepo.transactionnow affects the whole transaction block and not only the particular transaction queries - [Repo] Overriding
Repo.log/1is no longer supported. Instead provide custom loggers configuration via:loggers. The default is:[Ecto.LogEntry] - [Schema] Array fields no longer default to an empty list
[]. Previous behaviour can be achieved by passingdefault: []to the field definition - [Schema]
__schema__(:types)now returns map - [SQL]
Ecto.Adapters.SQL.begin_test_transaction,Ecto.Adapters.SQL.restart_test_transactionandEcto.Adapters.SQL.rollback_test_transactionhave been removed in favor of the new ownership-basedEcto.Adapters.SQL.Sandbox - [UUID]
Ecto.UUID.dump/1now returns{:ok, binary}
Soft deprecations (no warnings emitted)
- [Changeset] Deprecate
Ecto.Changeset.cast/4in favor ofEcto.Changeset.cast/3+Ecto.Changeset.validate_required/3
Deprecations
- [Changeset] Deprecate
:emptyinEcto.Changeset.cast - [Repo]
Repo.after_connect/1is deprecated, please pass the:after_connectrepository option instead
Enhancements
- [Adapter] Ensure adapters work on native types, guaranteeing adapters compose better with custom types
- [Adapter] Support prepared queries in adapters
- [Adapter] Add support for loading and dumping structures
- [Changeset] Include the type that failed to cast in changeset errors. Example:
[profile: {"is invalid", [type: :map]}] - [DateTime] Ensure the given date and datetimes are valid
- [Migration] Add support for partial indexes by specifying the
:whereoption when onEcto.Migration.index/2 - [Migration] Allow the migration table name to be configured in the repository via
:migration_source - [Migration] Support
:on_updateforEcto.Migrate.references - [Migration] Use pool of 1 connection for
mix ecto.migrate/rollback - [Mix] Automatically reenable migration and repository management tasks after execution
- [Preloader] Support mixing preloads and assocs
- [Postgres] Add migration and changeset support for PostgreSQL exclusion constraints. Example:
create constraint(:sizes, :cannot_overlap, exclude: ~s|gist (int4range("min", "max", '[]') WITH &&)|)andexclusion_constraint(changeset, :sizes, name: :cannot_overlap, message: "must not overlap") - [Postgres] Support lateral joins (via fragments)
- [Postgres] Add migration and changeset support for PostgreSQL check constraints. Example:
create constraint(:products, "positive_price", check: "price > 0")andcheck_constraint(changeset, :price, name: :positive_price, message: "must be greater than zero") - [Query] Allow the
:onfield to be specified with association joins - [Query] Support expressions in map keys in
selectin queries. Example:from p in Post, select: %{p.title => p.visitors} - [Query] Support map update syntax. Example:
from p in Post, select: %{p | title: "fixed"} - [Query] Allow struct fields to be selected with
struct/2and map fields withmap/2, including support for dynamic fields - [Query] Add
first/2andlast/2 - [Repo] Add
Repo.aggregate/4for easy aggregations - [Repo] Allow custom
selectfield in preload queries - [Repo] Support the
:forceoption in preloads - [Repo] Perform preloads in parallel by default
- [Repo] Add
Repo.in_transaction?to know if the current process is in a transaction - [Repo] Support
:returningoption ininsert_all,update_allanddelete_all - [Schema] Allow
@schema_prefixto be configured per schema. It is used for new structs as well as queries where the given schema is used asfrom - [Schema] Support MFA on autogenerate
- [Schema] Support composite primary keys
- [Type] Add type
{:map, inner_type}
Bug fixes
- [Changeset] The
:requiredoption oncast_assocandcast_embedwill now taghas_manyandembeds_manyrelationships as missing if they contain an empty list - [DateTime] Fix Date/DateTime serialization for years above 9999
- [Postgres] Switch pg storage management away from
psqland use direct database connections, solving many issues like locale and database connection - [Repo] Ensure nested preload works even if intermediate associations were already loaded
- [Repo] Do not attempt to execute insert/update/delete statement for associations if a previous operation failed due to a constraint error
- [Schema] Ensure
inserted_atautogeneration runs beforeupdated_atautogeneration
Previous versions
- See the CHANGELOG.md in the v1.1 branch