# `Ash.Changeset`
[🔗](https://github.com/ash-project/ash/blob/v3.17.0/lib/ash/changeset/changeset.ex#L5)

Changesets are used to create and update data in Ash.

Create a changeset with `new/1` or `new/2`, and alter the attributes
and relationships using the functions provided in this module.  Nothing in this module
actually incurs changes in a data layer. To commit a changeset, see `Ash.create/2`
and `Ash.update/2`.

# Changeset lifecycle

## Action Lifecycle

The following example illustrates the hook lifecycle of a changeset.

```elixir
defmodule AshChangesetLifeCycleExample do
  def change(changeset, _, _) do
    changeset
    # execute code both before and after the transaction
    |> Ash.Changeset.around_transaction(fn changeset, callback ->
      callback.(changeset)
    end)
    # execute code before the transaction is started. Use for things like external calls
    |> Ash.Changeset.before_transaction(fn changeset -> changeset end)
    # execute code in the transaction, before and after the data layer is called
    |> Ash.Changeset.around_action(fn changeset, callback ->
      callback.(changeset)
    end)
    # execute code in the transaction, before the data layer is called
    |> Ash.Changeset.before_action(fn changeset -> changeset end)
    # execute code in the transaction, after the data layer is called, only if the action is successful
    |> Ash.Changeset.after_action(fn changeset, result -> {:ok, result} end)
    # execute code after the transaction, both in success and error cases
    |> Ash.Changeset.after_transaction(fn changeset, success_or_error_result -> success_or_error_result end
  end
end
```

# `after_action_fun`

```elixir
@type after_action_fun() :: (t(), Ash.Resource.record() -&gt;
                         {:ok, Ash.Resource.record()}
                         | {:ok, Ash.Resource.record(),
                            [Ash.Notifier.Notification.t()]}
                         | {:error, any()})
```

Function type for after action hooks.

Receives the changeset and the successfully created/updated record, and can return
the record optionally with notifications, or an error.

# `after_transaction_fun`

```elixir
@type after_transaction_fun() :: (t(),
                            {:ok, Ash.Resource.record()}
                            | {:error, any()} -&gt;
                              {:ok, Ash.Resource.record()} | {:error, any()})
```

Function type for after transaction hooks.

Receives the changeset and the result (success or failure) of the action,
and returns the result (potentially modified).

# `around_action_callback`

```elixir
@type around_action_callback() :: (t() -&gt; around_action_result())
```

Callback function type for around action hooks.

A function that takes a changeset and returns an around action result.

# `around_action_fun`

```elixir
@type around_action_fun() :: (t(), around_action_callback() -&gt; around_action_result())
```

Function type for around action hooks.

Receives a changeset and a callback function that must be called with the changeset.

# `around_action_result`

```elixir
@type around_action_result() ::
  {:ok, Ash.Resource.record(), t(),
   %{notifications: [Ash.Notifier.Notification.t()]}}
  | {:error, Ash.Error.t()}
```

Result type for around action callbacks.

Contains the successful result with record, changeset, and notifications, or an error.

# `around_transaction_callback`

```elixir
@type around_transaction_callback() :: (t() -&gt; around_transaction_result())
```

Callback function type for around transaction hooks.

A function that takes a changeset and returns an around transaction result.

# `around_transaction_fun`

```elixir
@type around_transaction_fun() :: (t(), around_transaction_callback() -&gt;
                               around_transaction_result())
```

Function type for around transaction hooks.

Receives a changeset and a callback function that must be called with the changeset.

# `around_transaction_result`

```elixir
@type around_transaction_result() :: {:ok, Ash.Resource.record()} | {:error, any()}
```

Result type for around transaction callbacks.

Contains either a successful result with the record or an error.

# `before_action_fun`

```elixir
@type before_action_fun() :: (t() -&gt;
                          t()
                          | {t(),
                             %{notifications: [Ash.Notifier.Notification.t()]}})
```

Function type for before action hooks.

Receives a changeset and returns a modified changeset, optionally with notifications.

# `before_transaction_fun`

```elixir
@type before_transaction_fun() :: (t() -&gt; t())
```

Function type for before transaction hooks.

Receives a changeset and returns a modified changeset.

# `error_info`

```elixir
@type error_info() :: Ash.Error.error_input()
```

# `manage_relationship_type`

```elixir
@type manage_relationship_type() ::
  :append_and_remove | :append | :remove | :direct_control | :create
```

The type of relationship management strategy to use.

Defines how related records should be handled when managing relationships.

# `phase`

```elixir
@type phase() ::
  :around_transaction
  | :around_action
  | :after_transaction
  | :before_transaction
  | :after_action
  | :before_action
  | :validate
  | :pending
  | :atomic
```

The current phase of changeset processing.

Represents where the changeset is in its lifecycle, from pending through various hook phases.

# `t`

```elixir
@type t() :: %Ash.Changeset{
  __validated_for_action__: atom() | nil,
  action: Ash.Resource.Actions.action() | nil,
  action_failed?: boolean(),
  action_select: term(),
  action_type: Ash.Resource.Actions.action_type() | nil,
  added_filter: Ash.Filter.t() | nil,
  after_action: [after_action_fun() | {after_action_fun(), map()}],
  after_transaction: [
    after_transaction_fun() | {after_transaction_fun(), map()}
  ],
  arguments: %{optional(atom()) =&gt; any()},
  around_action: [around_action_fun() | {around_action_fun(), map()}],
  around_transaction: [
    around_transaction_fun() | {around_transaction_fun(), map()}
  ],
  atomic_after_action: term(),
  atomic_after_transaction: term(),
  atomic_changes: term(),
  atomic_validations: term(),
  atomics: Keyword.t(),
  attribute_changes: term(),
  attributes: %{optional(atom()) =&gt; any()},
  before_action: [before_action_fun() | {before_action_fun(), map()}],
  before_transaction: [
    before_transaction_fun() | {before_transaction_fun(), map()}
  ],
  casted_arguments: term(),
  casted_attributes: term(),
  context: map(),
  context_changes: term(),
  create_atomics: term(),
  data: Ash.Resource.record() | nil,
  defaults: [atom()],
  dirty_hooks: term(),
  domain: module() | nil,
  errors: [Ash.Error.t()],
  filter: Ash.Filter.t() | nil,
  handle_errors:
    nil
    | (t(), error :: any() -&gt;
         :ignore | t() | (error :: any()) | {error :: any(), t()}),
  invalid_keys: MapSet.t(),
  load: keyword(keyword()),
  no_atomic_constraints: term(),
  params: %{optional(atom() | binary()) =&gt; any()},
  phase: phase(),
  relationships: %{
    optional(atom()) =&gt;
      %{optional(atom() | binary()) =&gt; any()}
      | [%{optional(atom() | binary()) =&gt; any()}]
  },
  resource: module(),
  select: [atom()] | nil,
  tenant: term(),
  timeout: pos_integer() | nil,
  to_tenant: term(),
  valid?: boolean()
}
```

The changeset struct containing all the information about a pending action.

This struct tracks changes to attributes, arguments for the action, validation state,
hooks to run at various points, and other metadata needed to execute an action.

# `accessing`

Returns a list of attributes, aggregates, relationships, and calculations that are being loaded

Provide a list of field types to narrow down the returned results.

# `add_error`

```elixir
@spec add_error(t(), Ash.Error.error_input(), path :: Ash.Error.path_input()) :: t()
```

Add an error to the errors list and mark the changeset as invalid.

See `Ash.Error.to_ash_error/3` for more on supported values for `error`

# `after_action`

```elixir
@spec after_action(
  changeset :: t(),
  fun :: after_action_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds an after_action hook to the changeset.

After action hooks run within the database transaction after the data layer action succeeds.
They are perfect for:

- Creating related records that depend on the main action's result
- Sending notifications with the final record data
- Updating derived data or maintaining referential integrity
- Triggering business logic that needs the persisted record
- Creating audit trails with the actual saved data

The hook receives both the changeset and the successfully created/updated record.
Any errors returned will roll back the entire transaction.

Provide the option `prepend?: true` to place the hook before all other hooks instead of after.

## Atomic Operations

This hook works with atomic bulk operations. When implementing a change that supports both
stream and atomic strategies, add the hook in both the `change/3` and `atomic/3` callbacks.
See the `Ash.Resource.Change` documentation for examples of atomic-compatible changes.

## Examples

    # Create related audit record with the final data
    iex> changeset = Ash.Changeset.for_update(user, :update_profile, %{email: "new@example.com"})
    iex> changeset = Ash.Changeset.after_action(changeset, fn changeset, updated_user ->
    ...>   {:ok, _audit} = MyApp.AuditLog.create(%{
    ...>     action: "profile_updated",
    ...>     user_id: updated_user.id,
    ...>     changes: changeset.attributes,
    ...>     actor_id: changeset.context.actor.id,
    ...>     timestamp: DateTime.utc_now()
    ...>   })
    ...>   {:ok, updated_user}
    ...> end)

    # Send notification with the final record
    iex> changeset = Ash.Changeset.for_create(MyApp.Order, :create, %{total: 100.00})
    iex> changeset = Ash.Changeset.after_action(changeset, fn _changeset, order ->
    ...>   NotificationService.send_order_confirmation(%{
    ...>     order_id: order.id,
    ...>     customer_email: order.customer_email,
    ...>     total: order.total,
    ...>     order_number: order.number  # Generated by the database
    ...>   })
    ...>   {:ok, order}
    ...> end)

    # Update related records that depend on the main record
    iex> changeset = Ash.Changeset.for_create(MyApp.Post, :publish, %{title: "My Post"})
    iex> changeset = Ash.Changeset.after_action(changeset, fn _changeset, post ->
    ...>   # Update author's post count
    ...>   {:ok, _author} = MyApp.User
    ...>   |> Ash.Changeset.for_update(:increment_post_count, %{})
    ...>   |> Ash.update()
    ...>
    ...>   {:ok, post}
    ...> end)

## See also

- `before_action/3` for hooks that run before the data layer action
- `after_transaction/3` for hooks that run after the transaction commits
- `around_action/2` for hooks that wrap the data layer action

# `after_transaction`

```elixir
@spec after_transaction(
  changeset :: t(),
  fun :: after_transaction_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds an after_transaction hook to the changeset. Cannot be called within other hooks.

After transaction hooks run outside the database transaction and are called regardless
of whether the action succeeded or failed. They are essential for:

- Cleanup operations that must happen regardless of success/failure
- External service notifications that shouldn't be rolled back
- Logging final outcomes for auditing purposes
- Releasing external resources or locks
- Sending emails or notifications that should persist even if the action failed

The hook receives the changeset and either `{:ok, result}` or `{:error, error}`,
allowing you to handle both success and failure cases appropriately.

Provide the option `prepend?: true` to place the hook before all other hooks instead of after.

## Atomic Operations

This hook works with atomic bulk operations. When implementing a change that supports both
stream and atomic strategies, add the hook in both the `change/3` and `atomic/3` callbacks.
See the `Ash.Resource.Change` documentation for examples of atomic-compatible changes.

## Examples

    # Send notification regardless of order creation success/failure
    iex> changeset = Ash.Changeset.for_create(MyApp.Order, :create, %{total: 100.00})
    iex> changeset = Ash.Changeset.after_transaction(changeset, fn changeset, result ->
    ...>   case result do
    ...>     {:ok, order} ->
    ...>       NotificationService.order_created_successfully(order.id, changeset.context.actor)
    ...>     {:error, _error} ->
    ...>       NotificationService.order_creation_failed(changeset.attributes, changeset.context.actor)
    ...>   end
    ...>   result  # Always return the original result
    ...> end)

    # Release external resources or cleanup
    iex> changeset = Ash.Changeset.for_update(file, :process, %{status: "processing"})
    iex> changeset = Ash.Changeset.after_transaction(changeset, fn changeset, result ->
    ...>   # Always release the file lock, regardless of processing outcome
    ...>   ExternalFileService.release_lock(changeset.data.file_path)
    ...>
    ...>   case result do
    ...>     {:ok, processed_file} ->
    ...>       Logger.info("File processing completed successfully: #{processed_file.id}")
    ...>     {:error, error} ->
    ...>       Logger.error("File processing failed: #{inspect(error)}")
    ...>       # Could trigger retry mechanism here
    ...>   end
    ...>   result
    ...> end)

    # Audit final outcome for compliance
    iex> changeset = Ash.Changeset.for_update(sensitive_record, :update, %{classification: "top_secret"})
    iex> changeset = Ash.Changeset.after_transaction(changeset, fn changeset, result ->
    ...>   outcome = case result do
    ...>     {:ok, _} -> "success"
    ...>     {:error, _} -> "failure"
    ...>   end
    ...>
    ...>   ComplianceLogger.log_security_action(%{
    ...>     action: :update_classification,
    ...>     actor: changeset.context.actor,
    ...>     resource_id: changeset.data.id,
    ...>     outcome: outcome,
    ...>     timestamp: DateTime.utc_now()
    ...>   })
    ...>   result
    ...> end)

## See also

- `after_action/3` for hooks that run within the transaction on success only
- `before_transaction/3` for hooks that run before the transaction starts
- `around_transaction/2` for hooks that wrap the entire transaction

# `apply_attributes`

```elixir
@spec apply_attributes(t(), opts :: Keyword.t()) ::
  {:ok, Ash.Resource.record()} | {:error, t()}
```

Returns the original data with attribute changes merged, if the changeset is valid.

Options:

* force? - applies current attributes even if the changeset is not valid

# `around_action`

```elixir
@spec around_action(
  changeset :: t(),
  fun :: around_action_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds an around_action hook to the changeset.

Around action hooks wrap the data layer action execution within the transaction.
This is a specialized hook primarily useful for:

- Debugging and development tooling that needs to wrap action execution
- Action timing and performance monitoring for profiling
- Testing frameworks that need to intercept action execution
- Advanced error handling that requires wrapping the action itself

Your function receives the changeset and a callback that must be called with a changeset.
The callback returns `{:ok, result, changeset, instructions}` or `{:error, error}`.
You can modify these values, but must return the same structure.

**Warning**: This is an advanced hook that runs within the database transaction.
You *must* call the callback function. For most use cases, `before_action/3` and
`after_action/3` are simpler and more appropriate.

## Examples

    # Monitor action execution time for debugging
    iex> changeset = Ash.Changeset.for_create(MyApp.Order, :create, %{total: 100.00})
    iex> changeset = Ash.Changeset.around_action(changeset, fn changeset, callback ->
    ...>   start_time = System.monotonic_time(:microsecond)
    ...>   result = callback.(changeset)
    ...>   duration = System.monotonic_time(:microsecond) - start_time
    ...>
    ...>   Logger.debug("Action #{changeset.action.name} took #{duration}μs")
    ...>   result
    ...> end)

## See also

- `before_action/3` and `after_action/3` for most workflow needs
- `around_transaction/2` for wrapping the entire transaction
- Multi-step actions guide for complex workflow patterns

# `around_transaction`

```elixir
@spec around_transaction(
  changeset :: t(),
  fun :: around_transaction_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds an around_transaction hook to the changeset.

Around transaction hooks wrap the entire database transaction execution.
This is a specialized hook primarily useful for:

- Debugging and development tooling that needs to wrap transaction execution
- Transaction timing and monitoring for performance analysis
- Testing frameworks that need to intercept transaction behavior
- Advanced transaction management that requires wrapping all hook execution

Your function receives the changeset and a callback that must be called with a changeset.
The callback returns `{:ok, result}` or `{:error, error}`. You can modify these values,
but must return the same structure.

**Warning**: This is an advanced hook that controls transaction execution.
You *must* call the callback function. For most use cases, `before_transaction/3`
and `after_transaction/3` are simpler and more appropriate.

## Examples

    # Monitor transaction execution time
    iex> changeset = Ash.Changeset.for_create(MyApp.Order, :create, %{total: 100.00})
    iex> changeset = Ash.Changeset.around_transaction(changeset, fn changeset, callback ->
    ...>   tx_start = System.monotonic_time(:microsecond)
    ...>   result = callback.(changeset)
    ...>   tx_duration = System.monotonic_time(:microsecond) - tx_start
    ...>
    ...>   Logger.debug("Transaction for #{changeset.action.name} took #{tx_duration}μs")
    ...>   result
    ...> end)

## See also

- `before_transaction/3` and `after_transaction/3` for most workflow needs
- `around_action/2` for wrapping the data layer action
- Multi-step actions guide for complex workflow patterns

# `atomic_defaults`

# `atomic_ref`

Gets a reference to a field, or the current atomic update expression of that field.

# `atomic_set`

```elixir
@spec atomic_set(t(), map() | Keyword.t()) :: t()
```

Adds an atomic set to the changeset for the create phase.

Unlike `atomic_update/3`, this is for setting values during the INSERT phase of create
actions, not for updating existing values. You can use `atomic_ref/1` in create actions -
it returns `nil` or the latest value that the attribute is being changed to.

When used on update actions, this behaves the same as `atomic_update/3`.

## Examples

    # Set timestamp at database level during create
    iex> changeset = Ash.Changeset.for_create(post, :create)
    iex> changeset = Ash.Changeset.atomic_set(changeset, :created_at, expr(now()))

    # Use a database function for UUID
    iex> changeset = Ash.Changeset.atomic_set(changeset, :uuid, expr(fragment("gen_random_uuid()")))

## See also

- `atomic_update/3` for updating existing records

# `atomic_set`

# `atomic_update`

```elixir
@spec atomic_update(t(), map() | Keyword.t()) :: t()
```

Adds multiple atomic changes to the changeset

See `atomic_update/3` for more information.

# `atomic_update`

Adds an atomic change to the changeset.

Atomic changes are applied by the data layer, and as such have guarantees that are not
given by changes that are based on looking at the previous value and updating it. Here
is an example of a change that is not safe to do concurrently:

```elixir
change fn changeset, _ ->
  Ash.Changeset.change_attribute(changeset, :score, changeset.data.score + 1)
end
```

If two processes run this concurrently, they will both read the same value of `score`, and
set the new score to the same value. This means that one of the increments will be lost.
If you were to instead do this using `atomic_update`, you would get the correct result:

```elixir
Ash.Changeset.atomic_update(changeset, :score, expr(score + 1))
```

There are drawbacks/things to consider, however. The first is that atomic update results
are not known until after the action is run. The following functional validation would not
be able to enforce the score being less than 10, because the atomic happens after the validation.

```elixir
validate fn changeset, _ ->
  if Ash.Changeset.get_attribute(changeset, :score) < 10 do
    :ok
  else
    {:error, field: :score, message: "must be less than 10"}
  end
end
```

If you want to use atomic updates, it is suggested to write module-based validations & changes,
and implement the appropriate atomic callbacks on those modules. All builtin validations and changes
implement these callbacks in addition to the standard callbacks. Validations will only be run atomically
when the entire action is being run atomically or if one of the relevant fields is being updated atomically.

## Examples

    # Basic atomic increment
    iex> changeset = Ash.Changeset.for_update(post, :update)
    iex> changeset = Ash.Changeset.atomic_update(changeset, :view_count, expr(view_count + 1))
    iex> changeset.atomics
    [view_count: %Ash.Expr{...}]

    # Multiple atomic updates
    iex> changeset = Ash.Changeset.for_update(user, :update)
    iex> changeset = changeset
    ...> |> Ash.Changeset.atomic_update(:login_count, expr(login_count + 1))
    ...> |> Ash.Changeset.atomic_update(:last_login, expr(now()))
    iex> length(changeset.atomics)
    2

    # Atomic update with conditional logic
    iex> changeset = Ash.Changeset.for_update(post, :update)
    iex> changeset = Ash.Changeset.atomic_update(changeset, :status,
    ...>   expr(if view_count > 1000, do: "popular", else: "normal"))

    # Using the map/keyword syntax for multiple atomics
    iex> changeset = Ash.Changeset.for_update(user, :update)
    iex> changeset = Ash.Changeset.atomic_update(changeset, %{
    ...>   login_count: expr(login_count + 1),
    ...>   last_seen: expr(now())
    ...> })

## See also

- `atomic_ref/2` for referencing atomic values in expressions
- `fully_atomic_changeset/4` for creating fully atomic changesets
- `change_attribute/3` for regular (non-atomic) attribute changes

# `attribute_present?`

Checks if an attribute is not nil, either in the original data, or that it is not being changed to a `nil` value if it is changing.

This also accounts for the `accessing_from` context that is set when using `manage_relationship`, so it is aware that a particular value
*will* be set by `manage_relationship` even if it isn't currently being set.

# `before_action`

```elixir
@spec before_action(
  changeset :: t(),
  fun :: before_action_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds a before_action hook to the changeset.

Before action hooks are essential for multi-step workflows where you need to:

- Validate complex business logic that requires database queries
- Set computed attributes based on other changes
- Prepare data for external service calls
- Implement conditional logic that depends on the current state

The hook runs after validations and changes but before the data layer action is executed.
This gives you access to the final validated changeset while still being able to modify it.

Provide the option `prepend?: true` to place the hook before all other hooks instead of after.

## Examples

    # Set computed fields based on other attributes
    # Note: Use Ash.Changeset.force_change_attribute/2 instead of Ash.Changeset.change_attribute/2,
    # as the latter will log a warning saying that validations have already been run.
    iex> changeset = Ash.Changeset.for_create(MyApp.Order, :create, %{items: [%{price: 10}, %{price: 15}]})
    iex> changeset = Ash.Changeset.before_action(changeset, fn changeset ->
    ...>   total = changeset.attributes.items |> Enum.map(& &1.price) |> Enum.sum()
    ...>   tax = total * 0.08
    ...>   changeset
    ...>   |> Ash.Changeset.force_change_attribute(:subtotal, total)
    ...>   |> Ash.Changeset.force_change_attribute(:tax, tax)
    ...>   |> Ash.Changeset.force_change_attribute(:total, total + tax)
    ...> end)

    # Assign resources based on complex business logic
    iex> changeset = Ash.Changeset.for_create(MyApp.Ticket, :create, %{title: "Help needed", priority: :urgent})
    iex> changeset = Ash.Changeset.before_action(changeset, fn changeset ->
    ...>   case changeset.attributes.priority do
    ...>     :urgent ->
    ...>       # Query for available senior agents
    ...>       agent = MyApp.Agent |> MyApp.Query.for_read(:available_senior) |> MyApp.read_one!()
    ...>       Ash.Changeset.force_change_attribute(changeset, :assigned_agent_id, agent.id)
    ...>     _ ->
    ...>       changeset
    ...>   end
    ...> end)

    # Validate complex business rules that require database access
    iex> changeset = Ash.Changeset.for_update(user, :upgrade_plan, %{plan: :premium})
    iex> changeset = Ash.Changeset.before_action(changeset, fn changeset ->
    ...>   existing_subscription = MyApp.Subscription
    ...>   |> MyApp.Query.filter(user_id == ^user.id, status == :active)
    ...>   |> MyApp.read_one()
    ...>
    ...>   case existing_subscription do
    ...>     {:ok, %{plan: :premium}} ->
    ...>       Ash.Changeset.add_error(changeset, field: :plan, message: "already on premium plan")
    ...>     _ ->
    ...>       changeset
    ...>   end
    ...> end)

## See also

- `after_action/3` for hooks that run after the action succeeds
- `around_action/2` for hooks that wrap the entire action
- `before_transaction/3` for hooks that run before the database transaction
- Multi-step actions guide for complex workflow patterns

# `before_transaction`

```elixir
@spec before_transaction(
  changeset :: t(),
  fun :: before_transaction_fun(),
  opts :: Keyword.t()
) :: t()
```

Adds a before_transaction hook to the changeset.

Before transaction hooks run outside the database transaction and are ideal for:

- Making external API calls that shouldn't be rolled back
- Validating external resources or permissions
- Setting up external state that the action depends on
- Logging or auditing that should happen regardless of transaction success
- Preparing data from external services

These hooks run before any database transaction is started, so they can't access
the final result but can prepare the changeset with external data.

Provide the option `prepend?: true` to place the hook before all other hooks instead of after.

## Examples

    # Validate external service availability before processing
    iex> changeset = Ash.Changeset.for_create(MyApp.Order, :create, %{items: [%{sku: "ABC123"}]})
    iex> changeset = Ash.Changeset.before_transaction(changeset, fn changeset ->
    ...>   case ExternalInventoryService.check_availability(changeset.attributes.items) do
    ...>     {:ok, _} ->
    ...>       changeset
    ...>     {:error, :out_of_stock} ->
    ...>       Ash.Changeset.add_error(changeset, field: :items, message: "items out of stock")
    ...>   end
    ...> end)

    # Fetch and set data from external service
    iex> changeset = Ash.Changeset.for_create(MyApp.User, :create, %{email: "user@example.com"})
    iex> changeset = Ash.Changeset.before_transaction(changeset, fn changeset ->
    ...>   case UserService.get_profile_data(changeset.attributes.email) do
    ...>     {:ok, profile} ->
    ...>       changeset
    ...>       |> Ash.Changeset.change_attribute(:display_name, profile.name)
    ...>       |> Ash.Changeset.change_attribute(:avatar_url, profile.avatar)
    ...>     {:error, _} ->
    ...>       changeset  # Continue without external data
    ...>   end
    ...> end)

## See also

- `before_action/3` for hooks that run within the transaction
- `after_transaction/3` for hooks that run after the transaction completes
- `around_transaction/2` for hooks that wrap the entire transaction

# `change_attribute`

```elixir
@spec change_attribute(t(), atom(), any()) :: t()
```

Adds a change to the changeset, unless the value matches the existing value.

## Examples

    # Basic attribute change
    iex> changeset = Ash.Changeset.for_create(MyApp.Post, :create)
    iex> changeset = Ash.Changeset.change_attribute(changeset, :title, "New Title")
    iex> changeset.attributes
    %{title: "New Title"}

    # Change multiple attributes in sequence
    iex> changeset = Ash.Changeset.for_create(MyApp.User, :create)
    iex> changeset = changeset
    ...> |> Ash.Changeset.change_attribute(:name, "John Doe")
    ...> |> Ash.Changeset.change_attribute(:email, "john@example.com")
    iex> changeset.attributes
    %{name: "John Doe", email: "john@example.com"}

    # Value is cast according to the attribute type
    iex> changeset = Ash.Changeset.for_create(MyApp.Post, :create)
    iex> changeset = Ash.Changeset.change_attribute(changeset, :view_count, "42")
    iex> changeset.attributes
    %{view_count: 42}

## See also

- `change_attributes/2` for changing multiple attributes at once
- `force_change_attribute/3` for writing attributes not accepted by the action, or changing attributes in hooks
- `change_new_attribute/3` for changing an attribute unless its already being changed
- `update_change/3` for updating existing changes

# `change_attributes`

```elixir
@spec change_attributes(t(), map() | Keyword.t()) :: t()
```

Calls `change_attribute/3` for each key/value pair provided.

## Examples

    # Change multiple attributes with a map
    iex> changeset = Ash.Changeset.for_create(MyApp.Post, :create)
    iex> changeset = Ash.Changeset.change_attributes(changeset, %{title: "Hello World", content: "Post content"})
    iex> changeset.attributes
    %{title: "Hello World", content: "Post content"}

    # Change multiple attributes with a keyword list
    iex> changeset = Ash.Changeset.for_create(MyApp.User, :create)
    iex> changeset = Ash.Changeset.change_attributes(changeset, [name: "John Doe", email: "john@example.com", age: 30])
    iex> changeset.attributes
    %{name: "John Doe", email: "john@example.com", age: 30}

    # Values are cast according to their attribute types
    iex> changeset = Ash.Changeset.for_create(MyApp.Post, :create)
    iex> changeset = Ash.Changeset.change_attributes(changeset, %{view_count: "42", published: "true"})
    iex> changeset.attributes
    %{view_count: 42, published: true}

## See also

- `change_attribute/3` for changing individual attributes
- `force_change_attribute/3` for writing attributes not accepted by the action, or changing attributes in hooks
- `set_arguments/2` for setting action arguments

# `change_default_attribute`

```elixir
@spec change_default_attribute(t(), atom(), any()) :: t()
```

The same as `change_attribute`, but annotates that the attribute is currently holding a default value.

This information can be used in changes to see if a value was explicitly set or if it was set by being the default.
Additionally, this is used in `upsert` actions to not overwrite existing values with the default.

# `change_new_attribute`

```elixir
@spec change_new_attribute(t(), atom(), term()) :: t()
```

Change an attribute only if is not currently being changed

# `change_new_attribute_lazy`

```elixir
@spec change_new_attribute_lazy(t(), atom(), (-&gt; any())) :: t()
```

Change an attribute if is not currently being changed, by calling the provided function.

Use this if you want to only perform some expensive calculation for an attribute value
only if there isn't already a change for that attribute.

# `changing_attribute?`

```elixir
@spec changing_attribute?(t(), atom()) :: boolean()
```

Returns true if an attribute exists in the changes.

## Examples

    # Check if an attribute is being changed
    iex> changeset = Ash.Changeset.for_create(MyApp.Post, :create)
    iex> changeset = Ash.Changeset.change_attribute(changeset, :title, "New Title")
    iex> Ash.Changeset.changing_attribute?(changeset, :title)
    true
    iex> Ash.Changeset.changing_attribute?(changeset, :content)
    false

    # Works with atomic updates too
    iex> changeset = Ash.Changeset.for_update(post, :update)
    iex> changeset = Ash.Changeset.atomic_update(changeset, :view_count, {:atomic, expr(view_count + 1)})
    iex> Ash.Changeset.changing_attribute?(changeset, :view_count)
    true

    # Check multiple attributes
    iex> changeset = Ash.Changeset.for_create(MyApp.User, :create)
    iex> changeset = Ash.Changeset.change_attributes(changeset, %{name: "John", email: "john@example.com"})
    iex> Ash.Changeset.changing_attribute?(changeset, :name)
    true
    iex> Ash.Changeset.changing_attribute?(changeset, :age)
    false

## See also

- `changing_attributes?/1` for checking if any attributes are changing
- `changing_relationship?/2` for checking relationship changes
- `present?/2` for checking if a value is present
- `fetch_change/2` for getting the changed value

# `changing_attributes?`

```elixir
@spec changing_attributes?(t()) :: boolean()
```

Returns true if any attributes on the resource are being changed.

# `changing_relationship?`

```elixir
@spec changing_relationship?(t(), atom()) :: boolean()
```

Returns true if a relationship exists in the changes

# `clear_change`

Clears an attribute or relationship change off of the changeset.

# `delete_argument`

Remove an argument from the changeset

# `deselect`

```elixir
@spec deselect(t(), [atom()] | atom()) :: t()
```

Ensure the the specified attributes are `nil` in the changeset results.

# `ensure_selected`

```elixir
@spec ensure_selected(t(), [atom()] | atom()) :: t()
```

Ensures that the given attributes are selected.

The first call to `select/2` will *limit* the fields to only the provided fields.
Use `ensure_selected/2` to say "select this field (or these fields) without deselecting anything else".

See `select/2` for more.

# `expand_upsert_fields`

Turns the special case {:replace, fields}, :replace_all and {:replace_all_except, fields} upsert_fields
options into a list of fields

# `fetch_argument`

```elixir
@spec fetch_argument(t(), atom()) :: {:ok, term()} | :error
```

Fetches the value of an argument provided to the changeset or `:error`.

# `fetch_argument_or_attribute`

```elixir
@spec fetch_argument_or_attribute(t(), atom()) :: {:ok, term()} | :error
```

Fetches the value of an argument provided to the changeset, falling back to `Ash.Changeset.fetch_attribute/2` if nothing was provided.

## Example

    iex> changeset = Ash.Changeset.for_update(post, :update, %{title: "New Title"})
    iex> Ash.Changeset.fetch_argument_or_attribute(changeset, :title)
    {:ok, "New Title"}
    iex> Ash.Changeset.fetch_argument_or_attribute(changeset, :content)
    :error

# `fetch_argument_or_change`

```elixir
@spec fetch_argument_or_change(t(), atom()) :: {:ok, any()} | :error
```

Gets the value of an argument provided to the changeset, falling back to `Ash.Changeset.fetch_change/2` if nothing was provided.

# `fetch_attribute`

```elixir
@spec fetch_attribute(t(), atom()) :: {:ok, term()} | :error
```

Fetches the changing value or the original value of an attribute.

## Example

    iex> changeset = Ash.Changeset.for_update(post, :update, %{title: "New Title"})
    iex> Ash.Changeset.fetch_attribute(changeset, :title)
    {:ok, "New Title"}
    iex> Ash.Changeset.fetch_attribute(changeset, :content)
    :error

# `fetch_change`

```elixir
@spec fetch_change(t(), atom()) :: {:ok, any()} | :error
```

Gets the new value for an attribute, or `:error` if it is not being changed.

# `fetch_data`

```elixir
@spec fetch_data(t(), atom()) :: {:ok, term()} | :error
```

Gets the original value for an attribute, or `:error` if it is not available.

## Example

    iex> changeset = Ash.Changeset.for_update(post, :update, %{title: "New Title"})
    iex> Ash.Changeset.fetch_data(changeset, :title)
    {:ok, "Original Title"}
    iex> Ash.Changeset.fetch_data(changeset, :content)
    :error

# `filter`

```elixir
@spec filter(t(), Ash.Expr.t()) :: t()
```

Adds a filter for a record being updated or destroyed.

Used by optimistic locking. See `Ash.Resource.Change.Builtins.optimistic_lock/1` for more.

# `for_action`

Constructs a changeset for a given action, and validates it.

Calls `for_create/4`, `for_update/4` or `for_destroy/4` based on the type of action passed in.

See those functions for more explanation.

# `for_create`

Constructs a changeset for a given create action, and validates it.

Anything that is modified prior to `for_create/4` is validated against the rules of the action, while *anything after it is not*.
This runs any `change`s contained on your action. To have your logic execute *only* during the action, you can use `after_action/2`
or `before_action/2`.

Multitenancy is *not* validated until an action is called. This allows you to avoid specifying a tenant until just before calling
the domain action.

### Params
`params` may be attributes, relationships, or arguments. You can safely pass user/form input directly into this function.
Only public attributes and relationships are supported. If you want to change private attributes as well, see the
Customization section below. `params` are stored directly as given in the `params` field of the changeset, which can be
used to retrieve the originally input value.

## Options

* `:require?` (`t:boolean/0`) - If set to `false`, values are only required when the action is run (instead of immediately). The default value is `false`.

* `:actor` (`t:term/0`) - set the actor, which can be used in any `Ash.Resource.Change`s configured on the action. (in the `context` argument)

* `:authorize?` (`t:term/0`) - set authorize?, which can be used in any `Ash.Resource.Change`s configured on the action. (in the `context` argument)

* `:tracer` (one or a list of module that adopts `Ash.Tracer`) - A tracer to use. Will be carried over to the action. For more information see `Ash.Tracer`.

* `:tenant` (value that implements the `Ash.ToTenant` protocol) - set the tenant on the changeset

* `:skip_unknown_inputs` - A list of inputs that, if provided, will be ignored if they are not recognized by the action. Use `:*` to indicate all unknown keys.

* `:load` (`t:term/0`) - Data to load on the result after running the action.

* `:context` (`t:map/0`) - Context to set on the query, changeset, or input

* `:scope` (`t:term/0`) - A value that implements the `Ash.Scope.ToOpts` protocol, for passing around actor/tenant/context in a single value. See `Ash.Scope.ToOpts` for more.

* `:private_arguments` (`t:map/0`) - Private argument values to set before validations and changes. The default value is `%{}`.

* `:return_skipped_upsert?` (`t:boolean/0`) - If `true`, and a record was *not* upserted because its filter prevented the upsert, the original record (which was *not* upserted) will be returned. The default value is `false`.

### Customization

A changeset can be provided as the first argument, instead of a resource, to allow
setting specific attributes ahead of time.

For example:

    MyResource
    |> Ash.Changeset.new()
    |> Ash.Changeset.change_attribute(:foo, 1)
    |> Ash.Changeset.for_create(:create, ...opts)

Once a changeset has been validated by `for_create/4` (or `for_update/4`), it isn't validated again in the action.
New changes added are validated individually, though. This allows you to create a changeset according
to a given action, and then add custom changes if necessary.

### What does this function do?

The following steps are run when calling `Ash.Changeset.for_create/4`.

- Cast input params | This is any arguments in addition to any accepted attributes
- Set argument defaults
- Require any missing arguments
- Validate all provided attributes are accepted
- Require any accepted attributes that are `allow_nil?` false
- Set any default values for attributes
- Run action changes & validations
- Run validations, or add them in `before_action` hooks if using `d:Ash.Resource.Dsl.actions.create.validate|before_action?`. Any global validations are skipped if the action has `skip_global_validations?` set to `true`.

## Examples

    # Basic create changeset with attributes
    iex> changeset = Ash.Changeset.for_create(MyApp.Post, :create, %{title: "Hello World", content: "This is my first post"})
    iex> changeset.valid?
    true

    # Create changeset with custom actor and tenant
    iex> changeset = Ash.Changeset.for_create(MyApp.Comment, :create,
    ...>   %{body: "Great post!"},
    ...>   actor: current_user,
    ...>   tenant: "org_123"
    ...> )
    iex> changeset.tenant
    "org_123"

## See also

- `for_action/4` for a generic action constructor
- `for_update/4` for updating existing records
- `for_destroy/4` for destroying records
- `Ash.create/2` to execute the changeset
- `d:Ash.Resource.Dsl.actions.create` for defining create actions
- [Create Actions Guide](/documentation/topics/actions/create-actions.md) for understanding create operations
- [Actions Guide](/documentation/topics/actions/actions.md) for general action concepts

# `for_destroy`

Constructs a changeset for a given destroy action, and validates it.

### Opts

* `:actor` - set the actor, which can be used in any `Ash.Resource.Change`s configured on the action. (in the `context` argument)
* `:tenant` - set the tenant on the changeset
* `:private_arguments` - set private arguments on the changeset before validations and changes are run

Anything that is modified prior to `for_destroy/4` is validated against the rules of the action, while *anything after it is not*.

Once a changeset has been validated by `for_destroy/4`, it isn't validated again in the action.
New changes added are validated individually, though. This allows you to create a changeset according
to a given action, and then add custom changes if necessary.

### What does this function do?

The following steps are run when calling `Ash.Changeset.for_destroy/4`.

- Cast input params | This is any arguments in addition to any accepted attributes
- Set argument defaults
- Require any missing arguments
- Validate all provided attributes are accepted
- Require any accepted attributes that are `allow_nil?` false
- Set any default values for attributes
- Run action changes & validations
- Run validations, or add them in `before_action` hooks if using `d:Ash.Resource.Dsl.actions.destroy.validate|before_action?`. Any global validations are skipped if the action has `skip_global_validations?` set to `true`.

## Examples

    # Basic destroy changeset
    iex> post = %MyApp.Post{id: 1, title: "My Post", content: "Content"}
    iex> changeset = Ash.Changeset.for_destroy(post, :destroy)
    iex> changeset.valid?
    true

    # Destroy changeset with inputs for the action
    iex> user = %MyApp.User{id: 1, name: "John", email: "john@example.com"}
    iex> changeset = Ash.Changeset.for_destroy(user, :archive, %{reason: "User requested deletion"}, actor: current_user)

    # Soft delete changeset (if action is configured as soft)
    iex> comment = %MyApp.Comment{id: 1, body: "My comment", post_id: 1}
    iex> changeset = Ash.Changeset.for_destroy(comment, :soft_delete, %{}, actor: current_user, tenant: "org_123")
    iex> changeset.tenant
    "org_123"

## See also

- `for_action/4` for a generic action constructor
- `for_create/4` for creating new records
- `for_update/4` for updating records
- `Ash.destroy/2` to execute the changeset
- `d:Ash.Resource.Dsl.actions.destroy` for defining destroy actions
- [Destroy Actions Guide](/documentation/topics/actions/destroy-actions.md) for understanding destroy operations
- [Actions Guide](/documentation/topics/actions/actions.md) for general action concepts

# `for_update`

Constructs a changeset for a given update action, and validates it.

Anything that is modified prior to `for_update/4` is validated against the rules of the action, while *anything after it is not*.

### What does this function do?

The following steps are run when calling `Ash.Changeset.for_update/4`.

- Cast input params | This is any arguments in addition to any accepted attributes
- Set argument defaults
- Require any missing arguments
- Validate all provided attributes are accepted
- Require any accepted attributes that are `allow_nil?` false
- Set any default values for attributes
- Run action changes & validations
- Run validations, or add them in `before_action` hooks if using `d:Ash.Resource.Dsl.actions.update.validate|before_action?`. Any global validations are skipped if the action has `skip_global_validations?` set to `true`.

## Examples

    # Basic update changeset with attributes
    iex> post = %MyApp.Post{id: 1, title: "Original Title", content: "Original content"}
    iex> changeset = Ash.Changeset.for_update(post, :update, %{title: "Updated Title"})
    iex> changeset.valid?
    true

    # Update changeset with custom actor and load related data
    iex> comment = %MyApp.Comment{id: 1, body: "Original comment", post_id: 1}
    iex> changeset = Ash.Changeset.for_update(comment, :update,
    ...>   %{body: "Updated comment"},
    ...>   actor: current_user,
    ...>   load: [:post, :author]
    ...> )
    iex> changeset.load
    [:post, :author]

## See also

- `for_action/4` for a generic action constructor
- `for_create/4` for creating new records
- `for_destroy/4` for destroying records
- `Ash.update/2` to execute the changeset
- `d:Ash.Resource.Dsl.actions.update` for defining update actions
- [Update Actions Guide](/documentation/topics/actions/update-actions.md) for understanding update operations
- [Actions Guide](/documentation/topics/actions/actions.md) for general action concepts

# `force_change_attribute`

```elixir
@spec force_change_attribute(t(), atom(), any()) :: t()
```

Changes an attribute even if it isn't writable

# `force_change_attributes`

```elixir
@spec force_change_attributes(t(), map() | Keyword.t()) :: t()
```

Calls `force_change_attribute/3` for each key/value pair provided.

# `force_change_new_attribute`

```elixir
@spec force_change_new_attribute(t(), atom(), term()) :: t()
```

Force change an attribute if it is not currently being changed.

See `change_new_attribute/3` for more.

# `force_change_new_attribute_lazy`

```elixir
@spec force_change_new_attribute_lazy(t(), atom(), (-&gt; any())) :: t()
```

Force change an attribute if it is not currently being changed, by calling the provided function.

See `change_new_attribute_lazy/3` for more.

# `force_delete_argument`

Remove an argument from the changeset, not warning if the changeset has already been validated.

# `force_set_argument`

Add an argument to the changeset, which will be provided to the action.

Does not show a warning when used in before/after action hooks.

# `force_set_arguments`

Merge a map of arguments to the arguments list.

Does not show a warning when used in before/after action hooks.

# `fully_atomic_changeset`

```elixir
@spec fully_atomic_changeset(
  resource :: Ash.Resource.t(),
  action :: atom() | Ash.Resource.Actions.action(),
  params :: map(),
  opts :: Keyword.t()
) :: t() | {:not_atomic, String.t()}
```

# `get_argument`

```elixir
@spec get_argument(t(), atom()) :: term()
```

Gets the value of an argument provided to the changeset.

## Examples

    # Get an argument that was set
    iex> changeset = MyApp.Post
    ...> |> Ash.Changeset.new()
    ...> |> Ash.Changeset.set_argument(:auto_publish, true)
    iex> Ash.Changeset.get_argument(changeset, :auto_publish)
    true

    # Get an argument that doesn't exist (returns nil)
    iex> changeset = Ash.Changeset.new(MyApp.Post)
    iex> Ash.Changeset.get_argument(changeset, :nonexistent)
    nil

    # Get argument with string key
    iex> changeset = MyApp.User
    ...> |> Ash.Changeset.new()
    ...> |> Ash.Changeset.set_argument(:send_email, false)
    iex> Ash.Changeset.get_argument(changeset, "send_email")
    false

## See also

- `set_argument/3` for setting argument values
- `fetch_argument/2` for getting arguments with error handling
- `get_argument_or_attribute/2` for fallback to attributes

# `get_argument_or_attribute`

```elixir
@spec get_argument_or_attribute(t(), atom()) :: term()
```

Gets the value of an argument provided to the changeset, falling back to `Ash.Changeset.get_attribute/2` if nothing was provided.

# `get_attribute`

```elixir
@spec get_attribute(t(), atom()) :: term()
```

Gets the changing value or the original value of an attribute.

# `get_data`

```elixir
@spec get_data(t(), atom()) :: term()
```

Gets the original value for an attribute

# `handle_errors`

```elixir
@spec handle_errors(
  t(),
  (t(), error :: term() -&gt;
     :ignore | t() | (error :: term()) | {error :: term(), t()})
  | {module(), atom(), [term()]}
) :: t()
```

Sets a custom error handler on the changeset.

The error handler should be a two argument function or an mfa, in which case the first two arguments will be set
to the changeset and the error, w/ the supplied arguments following those. The changeset will be marked
as invalid regardless of the outcome of this callback.

Any errors generated are passed to `handle_errors`, which can return any of the following:

* `:ignore` - the error is discarded.
* `changeset` - a new (or the same) changeset. The error is not added.
* `{changeset, error}` - a new (or the same) error and changeset. The error is added to the changeset.
* `anything_else` - is treated as a new, transformed version of the error. The result is added as an error to the changeset.

# `is_valid`
*macro* 

```elixir
@spec is_valid(t()) :: Macro.output()
```

A guard which checks if the Changeset is valid.

# `load`

```elixir
@spec load(t(), term()) :: t()
```

Calls the provided load statement on the result of the action at the very end of the action.

## Examples

    # Load a single relationship
    iex> changeset = Ash.Changeset.for_create(MyApp.Post, :create, %{title: "My Post"})
    iex> changeset = Ash.Changeset.load(changeset, :comments)
    iex> changeset.load
    [:comments]

    # Load multiple relationships
    iex> changeset = Ash.Changeset.for_update(user, :update, %{name: "John Doe"})
    iex> changeset = Ash.Changeset.load(changeset, [:posts, :profile])
    iex> changeset.load
    [:posts, :profile]

    # Load nested relationships
    iex> changeset = Ash.Changeset.for_create(MyApp.Comment, :create, %{body: "Great post!"})
    iex> changeset = Ash.Changeset.load(changeset, [post: [:author, :comments]])
    iex> changeset.load
    [[post: [:author, :comments]]]

    # Chain multiple load calls (they accumulate)
    iex> changeset = Ash.Changeset.for_update(post, :update, %{title: "Updated Title"})
    iex> changeset = changeset
    ...> |> Ash.Changeset.load(:author)
    ...> |> Ash.Changeset.load(:comments)
    iex> changeset.load
    [:author, :comments]

## See also

- `select/3` for controlling which attributes are returned
- `loading?/2` for checking if something is being loaded
- `Ash.Query.load/2` for loading in queries
- `Ash.ActionInput.load/2` for loading in generic actions

# `loading?`

Returns true if the field/relationship or path to field/relationship is being loaded.

It accepts an atom or a list of atoms, which is treated for as a "path", i.e:

    Resource |> Ash.Changeset.load(friends: [enemies: [:score]]) |> Ash.Changeset.loading?([:friends, :enemies, :score])
    iex> true

    Resource |> Ash.Changeset.load(friends: [enemies: [:score]]) |> Ash.Changeset.loading?([:friends, :score])
    iex> false

    Resource |> Ash.Changeset.load(friends: [enemies: [:score]]) |> Ash.Changeset.loading?(:friends)
    iex> true

# `manage_relationship`

Manages the related records by creating, updating, or destroying them as necessary.

Keep in mind that the default values for all `on_*` are `:ignore`, meaning nothing will happen
unless you provide instructions.

The input provided to `manage_relationship` should be a map, in the case of to_one relationships, or a list of maps
in the case of to_many relationships. The following steps are followed for each input provided:

- The input is checked against the currently related records to find any matches. The primary key and unique identities are used to find matches.
- For any input that had a match in the current relationship, the `:on_match` behavior is triggered
- For any input that does not have a match:
  - if there is `on_lookup` behavior:
    - we try to find the record in the data layer.
    - if the record is found, the on_lookup behavior is triggered
    - if the record is not found, the `on_no_match` behavior is triggered
  - if there is no `on_lookup` behavior:
    - the `on_no_match` behavior is triggered
- finally, for any records present in the *current relationship* that had no match *in the input*, the `on_missing` behavior is triggered

## Options

* `:type` - If the `type` is specified, the default values of each option is modified to match that `type` of operation.  
  This allows for specifying certain operations much more succinctly. The defaults that are modified are listed below:  
  - `:append_and_remove`  
        [
          on_lookup: :relate,
          on_no_match: :error,
          on_match: :ignore,
          on_missing: :unrelate
        ]  
  - `:append`  
        [
          on_lookup: :relate,
          on_no_match: :error,
          on_match: :ignore,
          on_missing: :ignore
        ]  
  - `:remove`  
        [
          on_no_match: :error,
          on_match: :unrelate,
          on_missing: :ignore
        ]  
  - `:direct_control`  
        [
          on_lookup: :ignore,
          on_no_match: :create,
          on_match: :update,
          on_missing: :destroy
        ]  
  - `:create`  
        [
          on_no_match: :create,
          on_match: :ignore
        ]
   Valid values are :append_and_remove, :append, :remove, :direct_control, :create

* `:authorize?` (`t:boolean/0`) - Authorize reads and changes to the destination records, if the primary change is being authorized as well. The default value is `true`.

* `:eager_validate_with` (`t:atom/0`) - Validates that any referenced entities exist *before* the action is being performed, using the provided domain for the read. The default value is `false`.

* `:on_no_match` (`t:term/0`) - Instructions for handling records where no matching record existed in the relationship.  
  * `:ignore` (default) - those inputs are ignored
  * `:match` - For `has_one` and `belongs_to` only, any input is treated as a match for an existing value. For `has_many` and `many_to_many`, this is the same as `:ignore`.
  * `:create` - the records are created using the destination's primary create action
  * `{:create, :action_name}` - the records are created using the specified action on the destination resource
  * `{:create, :action_name, :join_table_action_name, [:list, :of, :join_table, :params]}` - Same as `{:create, :action_name}` but takes
      the list of params specified out and applies them when creating the join record, with the provided join_table_action_name.
  * `:error`  - an error is returned indicating that a record would have been created
    *  If `on_lookup` is set, and the data contained a primary key or identity, then the error is a `NotFound` error
    * Otherwise, an `InvalidRelationship` error is returned The default value is `:ignore`.

* `:value_is_key` (`t:atom/0`) - Configures what key to use when a single value is provided.  
  This is useful when you use things like a list of strings i.e `friend_emails` to manage the relationship, instead of a list of maps.  
  By default, we assume it is the primary key of the destination resource, unless it is a composite primary key.

* `:order_is_key` (`t:atom/0`) - If set, the order that each input appears in the list will be added to the input as this key.  
  This is useful when you want to accept an ordered list of related records and write that order to the entity.
  This should only currently be used with `type: :direct_control` or `type: :create` when there are no currently
  existing related records (like when creating the source record).  
  If you have an identity on the field and relationship id on the destination, and you are using
  AshPostgres, you will want to use the `deferrable` option to ensure that conflicting orders are temporarily
  allowed within a single transaction.

* `:identity_priority` (list of `t:atom/0`) - The list, in priority order, of identities to use when looking up records for `on_lookup`, and matching records with `on_match`.  
  Use `:_primary_key` to prioritize checking a match with the primary key.
  All identities, along with `:_primary_key` are checked regardless, this only allows ensuring that some are checked first.
  Defaults to the list provided by `use_identities`, so you typically won't need this option.

* `:use_identities` (list of `t:atom/0`) - A list of identities that may be used to look up and compare records. Use `:_primary_key` to include the primary key. By default, only `[:_primary_key]` is used.

* `:on_lookup` (`t:term/0`) - Before creating a record (because no match was found in the relationship), the record can be looked up and related.  
  * `:ignore` (default) - Does not look for existing entries (matches in the current relationship are still considered updates)
  * `:relate` - Same as calling `{:relate, primary_action_name}`
  * `{:relate, :action_name}` - the records are looked up by primary key/the first identity that is found (using the primary read action), and related. The action should be:
      * `many_to_many` - a create action on the join resource
      * `has_many` - an update action on the destination resource
      * `has_one` - an update action on the destination resource
      * `belongs_to` - an update action on the source resource
  * `{:relate, :action_name, :read_action_name}` - Same as the above, but customizes the read action called to search for matches.
  * `:relate_and_update` - Same as `:relate`, but the remaining parameters from the lookup are passed into the action that is used to change the relationship key
  * `{:relate_and_update, :action_name}` - Same as the above, but customizes the action used. The action should be:
      * `many_to_many` - a create action on the join resource
      * `has_many` - an update action on the destination resource
      * `has_one` - an update action on the destination resource
      * `belongs_to` - an update action on the source resource
  * `{:relate_and_update, :action_name, :read_action_name}` - Same as the above, but customizes the read action called to search for matches.
  * `{:relate_and_update, :action_name, :read_action_name, [:list, :of, :join_table, :params]}` - Same as the above, but uses the provided list of parameters when creating
      the join row (only relevant for many to many relationships). Use `:*` to *only* update the join record, and pass all parameters to its action The default value is `:ignore`.

* `:on_match` (`t:term/0`) - Instructions for handling records where a matching record existed in the relationship already.  
  * `:ignore` (default) - those inputs are ignored
  * `:update` - the record is updated using the destination's primary update action
  * `{:update, :action_name}` - the record is updated using the specified action on the destination resource
  * `{:update, :action_name, :join_table_action_name, [:list, :of, :params]}` - Same as `{:update, :action_name}` but takes
      the list of params specified out and applies them as an update to the join record (only valid for many to many)
  * `:update_join` - update only the join record (only valid for many to many)
  * `{:update_join, :join_table_action_name}` - use the specified update action on a join resource
  * `{:update_join, :join_table_action_name, [:list, :of, :params]}` - pass specified params from input into a join resource update action
  * `{:destroy, :action_name}` - the record is destroyed using the specified action on the destination resource. The action should be:
    * `many_to_many` - a destroy action on the join record
    * `has_many` - a destroy action on the destination resource
    * `has_one` - a destroy action on the destination resource
    * `belongs_to` - a destroy action on the destination resource
  * `:error`  - an error is returned indicating that a record would have been updated
  * `:no_match` - follows the `on_no_match` instructions with these records
  * `:missing` - follows the `on_missing` instructions with these records
  * `:unrelate` - the related item is not destroyed, but the data is "unrelated". The action should be:
    * `many_to_many` - the join resource row is destroyed
    * `has_many` - the `destination_attribute` (on the related record) is set to `nil`
    * `has_one` - the `destination_attribute` (on the related record) is set to `nil`
    * `belongs_to` - the `source_attribute` (on this record) is set to `nil`
  * `{:unrelate, :action_name}` - the record is unrelated using the provided update action. The action should be:
    * `many_to_many` - a destroy action on the join resource
    * `has_many` - an update action on the destination resource
    * `has_one` - an update action on the destination resource
    * `belongs_to` - an update action on the source resource The default value is `:ignore`.

* `:on_missing` (`t:term/0`) - Instructions for handling records that existed in the current relationship but not in the input.  
  * `:ignore` (default) - those inputs are ignored
  * `:destroy` - the record is destroyed using the destination's primary destroy action
  * `{:destroy, :action_name}` - the record is destroyed using the specified action on the destination resource
  * `{:destroy, :action_name, :join_resource_action_name}` - the record is destroyed using the specified action on the destination resource,
    but first the join resource is destroyed with its specified action
  * `:error`  - an error is returned indicating that a record would have been updated
  * `:unrelate` - the related item is not destroyed, but the data is "unrelated". The action should be:
    * `many_to_many` - the join resource row is destroyed
    * `has_many` - the `destination_attribute` (on the related record) is set to `nil`
    * `has_one` - the `destination_attribute` (on the related record) is set to `nil`
    * `belongs_to` - the `source_attribute` (on this record) is set to `nil`
  * `{:unrelate, :action_name}` - the record is unrelated using the provided update action. The action should be:
    * `many_to_many` - a destroy action on the join resource
    * `has_many` - an update action on the destination resource
    * `has_one` - an update action on the destination resource
    * `belongs_to` - an update action on the source resource The default value is `:ignore`.

* `:error_path` (`t:term/0`) - By default, errors added to the changeset will use the path `[:relationship_name]`, or `[:relationship_name, <index>]`.
  If you want to modify this path, you can specify `error_path`, e.g if had a `change` on an action that takes an argument
  and uses that argument data to call `manage_relationship`, you may want any generated errors to appear under the name of that
  argument, so you could specify `error_path: :argument_name` when calling `manage_relationship`.

* `:join_keys` (list of `t:atom/0`) - For many to many relationships specifies the parameters to pick from the input and pass into a join resource action.
  Applicable in cases like `on_no_match: :create`, `on_match: :update` and `on_lookup: :relate`.
  Can be overwritten by a full form instruction tuple which contains join parameters at the end.

* `:meta` (`t:term/0`) - Freeform data that will be retained along with the options, which can be used to track/manage the changes
  that are added to the `relationships` key. Use the `meta[:order]` option to specify the order in which multiple
  calls to `manage_relationship` should be executed.

* `:ignore?` (`t:term/0`) - This tells Ash to ignore the provided inputs when actually running the action. This can be useful for
  building up a set of instructions that you intend to handle manually. The default value is `false`.

* `:debug?` (`t:boolean/0`) - Logs queries executed by relationship. The default value is `false`.

Each call to this function adds new records that will be handled according to their options. For example,
if you tracked "tags to add" and "tags to remove" in separate fields, you could input them like so:

```elixir
changeset
|> Ash.Changeset.manage_relationship(
  :tags,
  [%{name: "backend"}],
  on_lookup: :relate, #relate that tag if it exists in the database
  on_no_match: :error # error if a tag with that name doesn't exist
)
|> Ash.Changeset.manage_relationship(
  :tags,
  [%{name: "frontend"}],
  on_no_match: :error, # error if a tag with that name doesn't exist in the relationship
  on_match: :unrelate # if a tag with that name is related, unrelate it
)
```

When calling this multiple times with the `on_missing` option set, the list of records that are considered missing are checked
after each set of inputs is processed. For example, if you manage the relationship once with `on_missing: :unrelate`, the records
missing from your input will be removed, and *then* your next call to `manage_relationship` will be resolved (with those records unrelated).
For this reason, it is suggested that you don't call this function multiple times with an `on_missing` instruction, as you may be
surprised by the result.

If you want the input to update existing entities, you need to ensure that the primary key (or unique identity) is provided as
part of the input. See the example below:

    changeset
    |> Ash.Changeset.manage_relationship(
      :comments,
      [%{rating: 10, contents: "foo"}],
      on_no_match: {:create, :create_action},
      on_missing: :ignore
    )
    |> Ash.Changeset.manage_relationship(
      :comments,
      [%{id: 10, rating: 10, contents: "foo"}],
      on_match: {:update, :update_action},
      on_no_match: {:create, :create_action})

This is a simple way to manage a relationship. If you need custom behavior, you can customize the action that is
called, which allows you to add arguments/changes. However, at some point you may want to forego this function
and make the changes yourself. For example:

    input = [%{id: 10, rating: 10, contents: "foo"}]

    changeset
    |> Ash.Changeset.after_action(fn _changeset, result ->
      # An example of updating comments based on a result of other changes
      for comment <- input do
        comment = Ash.get(Comment, comment.id)

        comment
        |> Map.update(:rating, 0, &(&1 * result.rating_weight))
        |> Ash.update!()
      end

      {:ok, result}
    end)

## Using records as input

Records can be supplied as the input values. If you do:

* if it would be looked up due to `on_lookup`, the record is used as-is
* if it would be created due to `on_no_match`, the record is used as-is
* Instead of specifying `join_keys`, those keys must go in `__metadata__.join_keys`. If `join_keys` is specified in the options, it is ignored.

For example:

```elixir
post1 =
  changeset
  |> Ash.create!()
  |> Ash.Resource.put_metadata(:join_keys, %{type: "a"})

post2 =
  changeset2
  |> Ash.create!()
  |> Ash.Resource.put_metadata(:join_keys, %{type: "b"})

author = Ash.create!(author_changeset)

Ash.Changeset.manage_relationship(
  author,
  :posts,
  [post1, post2],
  on_lookup: :relate
)
```

# `new`

```elixir
@spec new(Ash.Resource.t() | Ash.Resource.record()) :: t()
```

Returns a new changeset over a resource.

*Warning*: You almost always want to use `for_action` or `for_create`, etc. over this function if possible.

You can use this to start a changeset and make changes prior to calling `for_action`. This is not typically
necessary, but can be useful as an escape hatch.

## Examples

    # Create a changeset for a new record
    iex> changeset = Ash.Changeset.new(MyApp.Post)
    iex> changeset.action_type
    :create

    # Create a changeset for updating an existing record
    iex> post = %MyApp.Post{id: 1, title: "Original Title"}
    iex> changeset = Ash.Changeset.new(post)
    iex> changeset.action_type
    :update

    # Use as an escape hatch before calling for_action
    iex> MyApp.Post
    ...> |> Ash.Changeset.new()
    ...> |> Ash.Changeset.change_attribute(:title, "Draft Title")
    ...> |> Ash.Changeset.for_action(:create, %{content: "Post content"})

## See also

- `for_action/4` for the recommended way to create changesets
- `for_create/4` for creating new records
- `for_update/4` for updating existing records
- `for_destroy/4` for destroying records

# `prepare_changeset_for_action`

# `present?`

Checks if an argument is not nil or an attribute is not nil, either in the original data, or that it is not being changed to a `nil` value if it is changing.

This also accounts for the `accessing_from` context that is set when using `manage_relationship`, so it is aware that a particular value
*will* be set by `manage_relationship` even if it isn't currently being set.

## Examples

    # Check if attribute is present in original data
    iex> post = %MyApp.Post{id: 1, title: "My Post", content: nil}
    iex> changeset = Ash.Changeset.for_update(post, :update)
    iex> Ash.Changeset.present?(changeset, :title)
    true
    iex> Ash.Changeset.present?(changeset, :content)
    false

    # Check if attribute is being changed to a non-nil value
    iex> post = %MyApp.Post{id: 1, title: nil, content: "Content"}
    iex> changeset = Ash.Changeset.for_update(post, :update, %{title: "New Title"})
    iex> Ash.Changeset.present?(changeset, :title)
    true

    # Check if argument is present
    iex> changeset = MyApp.User
    ...> |> Ash.Changeset.new()
    ...> |> Ash.Changeset.set_argument(:send_email, true)
    iex> Ash.Changeset.present?(changeset, :send_email)
    true
    iex> Ash.Changeset.present?(changeset, :other_arg)
    false

## See also

- `attribute_present?/2` for checking only attributes (not arguments)
- `changing_attribute?/2` for checking if an attribute is being changed
- `get_argument/2` and `get_attribute/2` for retrieving values

# `put_context`

```elixir
@spec put_context(t(), atom(), term()) :: t()
```

Puts a key/value in the changeset context that can be used later.

Do not use the `private` key in your custom context, as that is reserved for internal use.

# `run_before_transaction_hooks`

# `select`

```elixir
@spec select(t(), [atom()] | atom(), Keyword.t()) :: t()
```

Ensure that only the specified attributes are present in the results.

The first call to `select/2` will replace the default behavior of selecting
all attributes. Subsequent calls to `select/2` will combine the provided
fields unless the `replace?` option is provided with a value of `true`.

If a field has been deselected, selecting it again will override that (because a single list of fields is tracked for selection)

Primary key attributes always selected and cannot be deselected.

When attempting to load a relationship (or manage it with `Ash.Changeset.manage_relationship/3`),
if the source field is not selected on the query/provided data an error will be produced. If loading
a relationship with a query, an error is produced if the query does not select the destination field
of the relationship.

Datalayers currently are not notified of the `select` for a changeset(unlike queries), and creates/updates select all fields when they are performed.
A select provided on a changeset sets the unselected fields to `nil` before returning the result.

Use `ensure_selected/2` if you wish to make sure a field has been selected, without deselecting any other fields.

## Examples

    # Select specific fields
    iex> changeset = Ash.Changeset.for_create(MyApp.Post, :create)
    iex> changeset = Ash.Changeset.select(changeset, [:title, :content])
    iex> changeset.select
    [:title, :content]

    # Combine multiple select calls (default behavior)
    iex> changeset = Ash.Changeset.for_create(MyApp.User, :create)
    iex> changeset = changeset
    ...> |> Ash.Changeset.select([:name, :email])
    ...> |> Ash.Changeset.select([:created_at])
    iex> changeset.select
    [:name, :email, :created_at]

    # Replace previous selection
    iex> changeset = Ash.Changeset.for_create(MyApp.Post, :create)
    iex> changeset = changeset
    ...> |> Ash.Changeset.select([:title, :content])
    ...> |> Ash.Changeset.select([:title, :published_at], replace?: true)
    iex> changeset.select
    [:title, :published_at]

## See also

- `ensure_selected/2` for adding fields without replacing existing selection
- `deselect/2` for removing fields from selection
- `selecting?/2` for checking if a field is selected
- `load/2` for loading relationships and calculations

# `selecting?`

# `set_argument`

Add an argument to the changeset, which will be provided to the action.

Arguments can be set on a changeset before calling `for_action/4` (or the specific `for_*` functions).
When `for_action/4` is called, arguments are validated and cast according to the action's argument
definitions. Use this function to set arguments that will be available during the action's execution.

## Examples

    # Set arguments before calling for_action
    iex> changeset = MyApp.Post
    ...> |> Ash.Changeset.new()
    ...> |> Ash.Changeset.set_argument(:auto_publish, true)
    ...> |> Ash.Changeset.set_argument(:slug_prefix, "blog")
    iex> changeset.arguments
    %{auto_publish: true, slug_prefix: "blog"}

    # Arguments are validated and cast when for_action is called
    iex> changeset = MyApp.User
    ...> |> Ash.Changeset.new()
    ...> |> Ash.Changeset.set_argument(:age, "25")  # String will be cast to integer
    ...> |> Ash.Changeset.for_action(:create, %{name: "John"})
    iex> changeset.arguments
    %{age: 25}

    # Chain argument setting with attribute changes
    iex> changeset = MyApp.Post
    ...> |> Ash.Changeset.new()
    ...> |> Ash.Changeset.set_argument(:generate_excerpt, true)
    ...> |> Ash.Changeset.change_attribute(:title, "My Post")
    ...> |> Ash.Changeset.for_action(:create, %{content: "Post content"})

## See also

- `set_arguments/2` for setting multiple arguments at once
- `get_argument/2` for retrieving argument values
- `force_set_argument/3` for setting arguments in hooks without warnings
- `delete_argument/2` for removing arguments

# `set_arguments`

Merge a map of arguments to the arguments list.

# `set_context`

```elixir
@spec set_context(t(), map() | nil) :: t()
```

Deep merges the provided map into the changeset context that can be used later.

Do not use the `private` key in your custom context, as that is reserved for internal use.

# `set_private_argument`

```elixir
@spec set_private_argument(t(), atom(), term()) :: t()
```

Add a private argument to the changeset, which will be provided to the action.

# `set_result`

```elixir
@spec set_result(t(), term()) :: t()
```

Set the result of the action. This will prevent running the underlying datalayer behavior

# `set_tenant`

```elixir
@spec set_tenant(t(), Ash.ToTenant.t()) :: t()
```

# `timeout`

```elixir
@spec timeout(t(), nil | pos_integer(), nil | pos_integer()) :: t()
```

# `update_change`

```elixir
@spec update_change(t(), atom(), (any() -&gt; any())) :: t()
```

Updates an existing attribute change by applying a function to it.

This is useful for applying some kind of normalization to the attribute.

```elixir
Ash.Changeset.update_change(changeset, :serial, &String.downcase/1)
```

The update function gets called with the value already cast to the correct type.

```elixir
changeset
|> Ash.Changeset.change_attribute(:integer_attribute, "3")
|> Ash.Changeset.update_change(:integer_attribute, fn x -> x + 1 end)
```

## Invalid value handling

If `update_change` is called with a changeset that has not been validated yet, the update
function must handle potentially invalid and `nil` values.

To only deal with valid values, you can call `update_change` in a `before_action` hook.

# `with_hooks`

```elixir
@spec with_hooks(
  t(),
  (t() -&gt;
     {:ok, term(), %{notifications: [Ash.Notifier.Notification.t()]}}
     | {:error, term()}),
  Keyword.t()
) ::
  {:ok, term(), t(), %{notifications: [Ash.Notifier.Notification.t()]}}
  | {:error, term()}
```

Wraps a function in the before/after action hooks of a changeset.

The function takes a changeset and if it returns
`{:ok, result}`, the result will be passed through the after
action hooks.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
