changeset.ex (126020B)
1 defmodule Ecto.Changeset do 2 @moduledoc ~S""" 3 Changesets allow filtering, casting, validation and 4 definition of constraints when manipulating structs. 5 6 There is an example of working with changesets in the introductory 7 documentation in the `Ecto` module. The functions `cast/4` and 8 `change/2` are the usual entry points for creating changesets. 9 The first one is used to cast and validate external parameters, 10 such as parameters sent through a form, API, command line, etc. 11 The second one is used to change data directly from your application. 12 13 The remaining functions in this module, such as validations, 14 constraints, association handling, are about manipulating 15 changesets. Let's discuss some of this extra functionality. 16 17 ## External vs internal data 18 19 Changesets allow working with both kinds of data: 20 21 * internal to the application - for example programmatically generated, 22 or coming from other subsystems. This use case is primarily covered 23 by the `change/2` and `put_change/3` functions. 24 25 * external to the application - for example data provided by the user in 26 a form that needs to be type-converted and properly validated. This 27 use case is primarily covered by the `cast/4` function. 28 29 ## Validations and constraints 30 31 Ecto changesets provide both validations and constraints which 32 are ultimately turned into errors in case something goes wrong. 33 34 The difference between them is that most validations can be 35 executed without a need to interact with the database and, therefore, 36 are always executed before attempting to insert or update the entry 37 in the database. Validations run immediately when a validation function 38 is called on the data that is contained in the changeset at that time. 39 40 Some validations may happen against the database but 41 they are inherently unsafe. Those validations start with a `unsafe_` 42 prefix, such as `unsafe_validate_unique/4`. 43 44 On the other hand, constraints rely on the database and are always safe. 45 As a consequence, validations are always checked before constraints. 46 Constraints won't even be checked in case validations failed. 47 48 Let's see an example: 49 50 defmodule User do 51 use Ecto.Schema 52 import Ecto.Changeset 53 54 schema "users" do 55 field :name 56 field :email 57 field :age, :integer 58 end 59 60 def changeset(user, params \\ %{}) do 61 user 62 |> cast(params, [:name, :email, :age]) 63 |> validate_required([:name, :email]) 64 |> validate_format(:email, ~r/@/) 65 |> validate_inclusion(:age, 18..100) 66 |> unique_constraint(:email) 67 end 68 end 69 70 In the `changeset/2` function above, we define three validations. 71 They check that `name` and `email` fields are present in the 72 changeset, the e-mail is of the specified format, and the age is 73 between 18 and 100 - as well as a unique constraint in the email 74 field. 75 76 Let's suppose the e-mail is given but the age is invalid. The 77 changeset would have the following errors: 78 79 changeset = User.changeset(%User{}, %{age: 0, email: "mary@example.com"}) 80 {:error, changeset} = Repo.insert(changeset) 81 changeset.errors #=> [age: {"is invalid", []}, name: {"can't be blank", []}] 82 83 In this case, we haven't checked the unique constraint in the 84 e-mail field because the data did not validate. Let's fix the 85 age and the name, and assume that the e-mail already exists in the 86 database: 87 88 changeset = User.changeset(%User{}, %{age: 42, name: "Mary", email: "mary@example.com"}) 89 {:error, changeset} = Repo.insert(changeset) 90 changeset.errors #=> [email: {"has already been taken", []}] 91 92 Validations and constraints define an explicit boundary when the check 93 happens. By moving constraints to the database, we also provide a safe, 94 correct and data-race free means of checking the user input. 95 96 ### Deferred constraints 97 98 Some databases support deferred constraints, i.e., constraints which are 99 checked at the end of the transaction rather than at the end of each statement. 100 101 Changesets do not support this type of constraints. When working with deferred 102 constraints, a violation while invoking `c:Ecto.Repo.insert/2` or `c:Ecto.Repo.update/2` won't 103 return `{:error, changeset}`, but rather raise an error at the end of the 104 transaction. 105 106 ## Empty values 107 108 Many times, the data given on cast needs to be further pruned, specially 109 regarding empty values. For example, if you are gathering data to be 110 cast from the command line or through an HTML form or any other text-based 111 format, it is likely those means cannot express nil values. For 112 those reasons, changesets include the concept of empty values. 113 114 When applying changes using `cast/4`, an empty value will be automatically 115 converted to the field's default value. If the field is an array type, any 116 empty value inside the array will be removed. 117 118 Empty values are stored in the changeset's `:empty_values` field and 119 default to `[""]`. You can also pass the `:empty_values` option to 120 `cast/4` in case you want to change how a particular `cast/4` work. 121 122 ## Associations, embeds and on replace 123 124 Using changesets you can work with associations as well as with embedded 125 structs. There are two primary APIs: 126 127 * `cast_assoc/3` and `cast_embed/3` - those functions are used when 128 working with external data. In particular, they allow you to change 129 associations and embeds alongside the parent struct, all at once. 130 131 * `put_assoc/4` and `put_embed/4` - it allows you to replace the 132 association or embed as a whole. This can be used to move associated 133 data from one entry to another, to completely remove or replace 134 existing entries. 135 136 See the documentation for those functions for more information. 137 138 ### The `:on_replace` option 139 140 When using any of those APIs, you may run into situations where Ecto sees 141 data is being replaced. For example, imagine a Post has many Comments where 142 the comments have IDs 1, 2 and 3. If you call `cast_assoc/3` passing only 143 the IDs 1 and 2, Ecto will consider 3 is being "replaced" and it will raise 144 by default. Such behaviour can be changed when defining the relation by 145 setting `:on_replace` option when defining your association/embed according 146 to the values below: 147 148 * `:raise` (default) - do not allow removing association or embedded 149 data via parent changesets 150 * `:mark_as_invalid` - if attempting to remove the association or 151 embedded data via parent changeset - an error will be added to the parent 152 changeset, and it will be marked as invalid 153 * `:nilify` - sets owner reference column to `nil` (available only for 154 associations). Use this on a `belongs_to` column to allow the association 155 to be cleared out so that it can be set to a new value. Will set `action` 156 on associated changesets to `:replace` 157 * `:update` - updates the association, available only for `has_one`, `belongs_to` 158 and `embeds_one`. This option will update all the fields given to the changeset 159 including the id for the association 160 * `:delete` - removes the association or related data from the database. 161 This option has to be used carefully (see below). Will set `action` on associated 162 changesets to `:replace` 163 * `:delete_if_exists` - like `:delete` except that it ignores any stale entry 164 error. For instance, if you set `on_replace: :delete` but the replaced 165 resource was already deleted by a separate request, it will raise a 166 `Ecto.StaleEntryError`. `:delete_if_exists` makes it so it will only delete 167 if the entry still exists 168 169 The `:delete` and `:delete_if_exists` options must be used carefully as they allow 170 users to delete any associated data by simply setting it to `nil` or an empty list. 171 If you need deletion, it is often preferred to add a separate boolean virtual field 172 in the schema and manually mark the changeset for deletion if the `:delete` field is 173 set in the params, as in the example below. Note that we don't call `cast/4` in this 174 case because we don't want to prevent deletion if a change is invalid (changes are 175 irrelevant if the entity needs to be deleted). 176 177 defmodule Comment do 178 use Ecto.Schema 179 import Ecto.Changeset 180 181 schema "comments" do 182 field :body, :string 183 field :delete, :boolean, virtual: true 184 end 185 186 def changeset(comment, %{"delete" => "true"}) do 187 %{Ecto.Changeset.change(comment, delete: true) | action: :delete} 188 end 189 190 def changeset(comment, params) do 191 cast(comment, params, [:body]) 192 end 193 end 194 195 ## Schemaless changesets 196 197 In the changeset examples so far, we have always used changesets to validate 198 and cast data contained in a struct defined by an Ecto schema, such as the `%User{}` 199 struct defined by the `User` module. 200 201 However, changesets can also be used with "regular" structs too by passing a tuple 202 with the data and its types: 203 204 user = %User{} 205 types = %{first_name: :string, last_name: :string, email: :string} 206 changeset = 207 {user, types} 208 |> Ecto.Changeset.cast(params, Map.keys(types)) 209 |> Ecto.Changeset.validate_required(...) 210 |> Ecto.Changeset.validate_length(...) 211 212 where the user struct refers to the definition in the following module: 213 214 defmodule User do 215 defstruct [:name, :age] 216 end 217 218 Changesets can also be used with data in a plain map, by following the same API: 219 220 data = %{} 221 types = %{name: :string} 222 params = %{name: "Callum"} 223 changeset = 224 {data, types} 225 |> Ecto.Changeset.cast(params, Map.keys(types)) 226 |> Ecto.Changeset.validate_required(...) 227 |> Ecto.Changeset.validate_length(...) 228 229 Such functionality makes Ecto extremely useful to cast, validate and prune data even 230 if it is not meant to be persisted to the database. 231 232 ### Changeset actions 233 234 Changesets have an action field which is usually set by `Ecto.Repo` 235 whenever one of the operations such as `insert` or `update` is called: 236 237 changeset = User.changeset(%User{}, %{age: 42, email: "mary@example.com"}) 238 {:error, changeset} = Repo.insert(changeset) 239 changeset.action 240 #=> :insert 241 242 This means that when working with changesets that are not meant to be 243 persisted to the database, such as schemaless changesets, you may need 244 to explicitly set the action to one specific value. Frameworks such as 245 Phoenix [use the action value to define how HTML forms should 246 act](https://hexdocs.pm/phoenix_html/Phoenix.HTML.Form.html#module-a-note-on-errors). 247 248 Instead of setting the action manually, you may use `apply_action/2` that 249 emulates operations such as `c:Ecto.Repo.insert`. `apply_action/2` will return 250 `{:ok, changes}` if the changeset is valid or `{:error, changeset}`, with 251 the given `action` set in the changeset in case of errors. 252 253 ## The Ecto.Changeset struct 254 255 The public fields are: 256 257 * `valid?` - Stores if the changeset is valid 258 * `data` - The changeset source data, for example, a struct 259 * `params` - The parameters as given on changeset creation 260 * `changes` - The `changes` from parameters that were approved in casting 261 * `errors` - All errors from validations 262 * `required` - All required fields as a list of atoms 263 * `action` - The action to be performed with the changeset 264 * `types` - Cache of the data's field types 265 * `empty_values` - A list of values to be considered empty 266 * `repo` - The repository applying the changeset (only set after a Repo function is called) 267 * `repo_opts` - A keyword list of options given to the underlying repository operation 268 269 The following fields are private and must not be accessed directly. 270 271 * `validations` 272 * `constraints` 273 * `filters` 274 * `prepare` 275 276 ### Redacting fields in inspect 277 278 To hide a field's value from the inspect protocol of `Ecto.Changeset`, mark 279 the field as `redact: true` in the schema, and it will display with the 280 value `**redacted**`. 281 """ 282 283 require Logger 284 require Ecto.Query 285 alias __MODULE__ 286 alias Ecto.Changeset.Relation 287 alias Ecto.Schema.Metadata 288 289 @empty_values [""] 290 291 # If a new field is added here, def merge must be adapted 292 defstruct valid?: false, data: nil, params: nil, changes: %{}, 293 errors: [], validations: [], required: [], prepare: [], 294 constraints: [], filters: %{}, action: nil, types: nil, 295 empty_values: @empty_values, repo: nil, repo_opts: [] 296 297 @type t(data_type) :: %Changeset{ 298 valid?: boolean(), 299 repo: atom | nil, 300 repo_opts: Keyword.t(), 301 data: data_type, 302 params: %{optional(String.t()) => term} | nil, 303 changes: %{optional(atom) => term}, 304 required: [atom], 305 prepare: [(t -> t)], 306 errors: [{atom, error}], 307 constraints: [constraint], 308 validations: [{atom, term}], 309 filters: %{optional(atom) => term}, 310 action: action, 311 types: nil | %{atom => Ecto.Type.t() | {:assoc, term()} | {:embed, term()}} 312 } 313 314 @type t :: t(Ecto.Schema.t | map | nil) 315 @type error :: {String.t, Keyword.t} 316 @type action :: nil | :insert | :update | :delete | :replace | :ignore | atom 317 @type constraint :: %{type: :check | :exclusion | :foreign_key | :unique, 318 constraint: String.t, match: :exact | :suffix | :prefix, 319 field: atom, error_message: String.t, error_type: atom} 320 @type data :: map() 321 @type types :: map() 322 323 @number_validators %{ 324 less_than: {&</2, "must be less than %{number}"}, 325 greater_than: {&>/2, "must be greater than %{number}"}, 326 less_than_or_equal_to: {&<=/2, "must be less than or equal to %{number}"}, 327 greater_than_or_equal_to: {&>=/2, "must be greater than or equal to %{number}"}, 328 equal_to: {&==/2, "must be equal to %{number}"}, 329 not_equal_to: {&!=/2, "must be not equal to %{number}"}, 330 } 331 332 @relations [:embed, :assoc] 333 @match_types [:exact, :suffix, :prefix] 334 335 @doc """ 336 Wraps the given data in a changeset or adds changes to a changeset. 337 338 `changes` is a map or keyword where the key is an atom representing a 339 field, association or embed and the value is a term. Note the `value` is 340 directly stored in the changeset with no validation whatsoever. For this 341 reason, this function is meant for working with data internal to the 342 application. 343 344 When changing embeds and associations, see `put_assoc/4` for a complete 345 reference on the accepted values. 346 347 This function is useful for: 348 349 * wrapping a struct inside a changeset 350 * directly changing a struct without performing castings nor validations 351 * directly bulk-adding changes to a changeset 352 353 Changed attributes will only be added if the change does not have the 354 same value as the field in the data. 355 356 When a changeset is passed as the first argument, the changes passed as the 357 second argument are merged over the changes already in the changeset if they 358 differ from the values in the struct. 359 360 When a `{data, types}` is passed as the first argument, a changeset is 361 created with the given data and types and marked as valid. 362 363 See `cast/4` if you'd prefer to cast and validate external parameters. 364 365 ## Examples 366 367 iex> changeset = change(%Post{}) 368 %Ecto.Changeset{...} 369 iex> changeset.valid? 370 true 371 iex> changeset.changes 372 %{} 373 374 iex> changeset = change(%Post{author: "bar"}, title: "title") 375 iex> changeset.changes 376 %{title: "title"} 377 378 iex> changeset = change(%Post{title: "title"}, title: "title") 379 iex> changeset.changes 380 %{} 381 382 iex> changeset = change(changeset, %{title: "new title", body: "body"}) 383 iex> changeset.changes.title 384 "new title" 385 iex> changeset.changes.body 386 "body" 387 388 """ 389 @spec change(Ecto.Schema.t | t | {data, types}, %{atom => term} | Keyword.t) :: t 390 def change(data, changes \\ %{}) 391 392 def change({data, types}, changes) when is_map(data) do 393 change(%Changeset{data: data, types: Enum.into(types, %{}), valid?: true}, changes) 394 end 395 396 def change(%Changeset{types: nil}, _changes) do 397 raise ArgumentError, "changeset does not have types information" 398 end 399 400 def change(%Changeset{changes: changes, types: types} = changeset, new_changes) 401 when is_map(new_changes) or is_list(new_changes) do 402 {changes, errors, valid?} = 403 get_changed(changeset.data, types, changes, new_changes, 404 changeset.errors, changeset.valid?) 405 %{changeset | changes: changes, errors: errors, valid?: valid?} 406 end 407 408 def change(%{__struct__: struct} = data, changes) when is_map(changes) or is_list(changes) do 409 types = struct.__changeset__() 410 {changes, errors, valid?} = get_changed(data, types, %{}, changes, [], true) 411 %Changeset{valid?: valid?, data: data, changes: changes, 412 errors: errors, types: types} 413 end 414 415 defp get_changed(data, types, old_changes, new_changes, errors, valid?) do 416 Enum.reduce(new_changes, {old_changes, errors, valid?}, fn 417 {key, value}, {changes, errors, valid?} -> 418 put_change(data, changes, errors, valid?, key, value, Map.get(types, key)) 419 _, _ -> 420 raise ArgumentError, 421 "invalid changes being applied to changeset. " <> 422 "Expected a keyword list or a map, got: #{inspect(new_changes)}" 423 end) 424 end 425 426 @doc """ 427 Applies the given `params` as changes on the `data` according to 428 the set of `permitted` keys. Returns a changeset. 429 430 `data` may be either a changeset, a schema struct or a `{data, types}` 431 tuple. The second argument is a map of `params` that are cast according 432 to the type information from `data`. `params` is a map with string keys 433 or a map with atom keys, containing potentially invalid data. Mixed keys 434 are not allowed. 435 436 During casting, all `permitted` parameters whose values match the specified 437 type information will have their key name converted to an atom and stored 438 together with the value as a change in the `:changes` field of the changeset. 439 If the cast value matches the current value for the field, it will not be 440 included in `:changes` unless the `:force_changes: true` option is 441 provided. All parameters that are not explicitly permitted are ignored. 442 443 If casting of all fields is successful, the changeset is returned as valid. 444 445 Note that `cast/4` validates the types in the `params`, but not in the given 446 `data`. 447 448 ## Options 449 450 * `:empty_values` - a list of values to be considered as empty when casting. 451 Empty values are always replaced by the default value of the respective field. 452 If the field is an array type, any empty value inside of the array will be removed. 453 Defaults to `[""]` 454 * `:force_changes` - a boolean indicating whether to include values that don't alter the current data in `:changes`. Defaults to `false` 455 456 ## Examples 457 458 iex> changeset = cast(post, params, [:title]) 459 iex> if changeset.valid? do 460 ...> Repo.update!(changeset) 461 ...> end 462 463 iex> params = %{title: "", topics: [""]} 464 iex> changeset = cast(post, params, [:topics, :title], empty_values: ["", []]) 465 iex> changeset.changes 466 %{title: nil, topics: nil} 467 468 Passing a changeset as the first argument: 469 470 iex> changeset = cast(post, %{title: "Hello"}, [:title]) 471 iex> new_changeset = cast(changeset, %{title: "Foo", body: "World"}, [:body]) 472 iex> new_changeset.params 473 %{"title" => "Hello", "body" => "World"} 474 475 Or creating a changeset from a simple map with types: 476 477 iex> data = %{title: "hello"} 478 iex> types = %{title: :string} 479 iex> changeset = cast({data, types}, %{title: "world"}, [:title]) 480 iex> apply_changes(changeset) 481 %{title: "world"} 482 483 ## Composing casts 484 485 `cast/4` also accepts a changeset as its first argument. In such cases, all 486 the effects caused by the call to `cast/4` (additional errors and changes) 487 are simply added to the ones already present in the argument changeset. 488 Parameters are merged (**not deep-merged**) and the ones passed to `cast/4` 489 take precedence over the ones already in the changeset. 490 """ 491 @spec cast(Ecto.Schema.t | t | {data, types}, 492 %{binary => term} | %{atom => term} | :invalid, 493 [atom], 494 Keyword.t) :: t 495 def cast(data, params, permitted, opts \\ []) 496 497 def cast(_data, %{__struct__: _} = params, _permitted, _opts) do 498 raise Ecto.CastError, type: :map, value: params, 499 message: "expected params to be a :map, got: `#{inspect(params)}`" 500 end 501 502 def cast({data, types}, params, permitted, opts) when is_map(data) do 503 cast(data, types, %{}, params, permitted, opts) 504 end 505 506 def cast(%Changeset{types: nil}, _params, _permitted, _opts) do 507 raise ArgumentError, "changeset does not have types information" 508 end 509 510 def cast(%Changeset{changes: changes, data: data, types: types, empty_values: empty_values} = changeset, 511 params, permitted, opts) do 512 opts = Keyword.put_new(opts, :empty_values, empty_values) 513 new_changeset = cast(data, types, changes, params, permitted, opts) 514 cast_merge(changeset, new_changeset) 515 end 516 517 def cast(%{__struct__: module} = data, params, permitted, opts) do 518 cast(data, module.__changeset__(), %{}, params, permitted, opts) 519 end 520 521 defp cast(%{} = data, %{} = types, %{} = changes, :invalid, permitted, _opts) when is_list(permitted) do 522 _ = Enum.each(permitted, &cast_key/1) 523 %Changeset{params: nil, data: data, valid?: false, errors: [], 524 changes: changes, types: types} 525 end 526 527 defp cast(%{} = data, %{} = types, %{} = changes, %{} = params, permitted, opts) when is_list(permitted) do 528 empty_values = Keyword.get(opts, :empty_values, @empty_values) 529 force? = Keyword.get(opts, :force_changes, false) 530 params = convert_params(params) 531 532 defaults = case data do 533 %{__struct__: struct} -> struct.__struct__() 534 %{} -> %{} 535 end 536 537 {changes, errors, valid?} = 538 Enum.reduce(permitted, {changes, [], true}, 539 &process_param(&1, params, types, data, empty_values, defaults, force?, &2)) 540 541 %Changeset{params: params, data: data, valid?: valid?, 542 errors: Enum.reverse(errors), changes: changes, types: types} 543 end 544 545 defp cast(%{}, %{}, %{}, params, permitted, _opts) when is_list(permitted) do 546 raise Ecto.CastError, type: :map, value: params, 547 message: "expected params to be a :map, got: `#{inspect params}`" 548 end 549 550 defp process_param(key, params, types, data, empty_values, defaults, force?, {changes, errors, valid?}) do 551 {key, param_key} = cast_key(key) 552 type = cast_type!(types, key) 553 554 current = 555 case changes do 556 %{^key => value} -> value 557 _ -> Map.get(data, key) 558 end 559 560 case cast_field(key, param_key, type, params, current, empty_values, defaults, force?, valid?) do 561 {:ok, value, valid?} -> 562 {Map.put(changes, key, value), errors, valid?} 563 :missing -> 564 {changes, errors, valid?} 565 {:invalid, custom_errors} -> 566 {message, new_errors} = 567 custom_errors 568 |> Keyword.put_new(:validation, :cast) 569 |> Keyword.put(:type, type) 570 |> Keyword.pop(:message, "is invalid") 571 {changes, [{key, {message, new_errors}} | errors], false} 572 end 573 end 574 575 defp cast_type!(types, key) do 576 case types do 577 %{^key => {tag, _}} when tag in @relations -> 578 raise "casting #{tag}s with cast/4 for #{inspect key} field is not supported, use cast_#{tag}/3 instead" 579 %{^key => type} -> 580 type 581 _ -> 582 known_fields = types |> Map.keys() |> Enum.map_join(", ", &inspect/1) 583 raise ArgumentError, 584 "unknown field `#{inspect(key)}` given to cast. Either the field does not exist or it is a " <> 585 ":through association (which are read-only). The known fields are: #{known_fields}" 586 end 587 end 588 589 defp cast_key(key) when is_atom(key), 590 do: {key, Atom.to_string(key)} 591 592 defp cast_key(key) do 593 raise ArgumentError, "cast/3 expects a list of atom keys, got key: `#{inspect key}`" 594 end 595 596 defp cast_field(key, param_key, type, params, current, empty_values, defaults, force?, valid?) do 597 case params do 598 %{^param_key => value} -> 599 value = filter_empty_values(type, value, empty_values, defaults, key) 600 case Ecto.Type.cast(type, value) do 601 {:ok, value} -> 602 if not force? and Ecto.Type.equal?(type, current, value) do 603 :missing 604 else 605 {:ok, value, valid?} 606 end 607 608 :error -> 609 {:invalid, []} 610 611 {:error, custom_errors} when is_list(custom_errors) -> 612 {:invalid, custom_errors} 613 end 614 615 _ -> 616 :missing 617 end 618 end 619 620 defp filter_empty_values(type, value, empty_values, defaults, key) do 621 case Ecto.Type.filter_empty_values(type, value, empty_values) do 622 :empty -> Map.get(defaults, key) 623 {:ok, value} -> value 624 end 625 end 626 627 # We only look at the first element because traversing the whole map 628 # can be expensive and it was showing up during profiling. This means 629 # we won't always raise, but the check only exists for user convenience 630 # anyway, and it is not a guarantee. 631 defp convert_params(params) do 632 case :maps.next(:maps.iterator(params)) do 633 {key, _, _} when is_atom(key) -> 634 for {key, value} <- params, into: %{} do 635 if is_atom(key) do 636 {Atom.to_string(key), value} 637 else 638 raise Ecto.CastError, type: :map, value: params, 639 message: "expected params to be a map with atoms or string keys, " <> 640 "got a map with mixed keys: #{inspect params}" 641 end 642 end 643 644 _ -> 645 params 646 end 647 end 648 649 ## Casting related 650 651 @doc """ 652 Casts the given association with the changeset parameters. 653 654 This function should be used when working with the entire association at 655 once (and not a single element of a many-style association) and receiving 656 data external to the application. 657 658 `cast_assoc/3` works matching the records extracted from the database 659 and compares it with the parameters received from an external source. 660 Therefore, it is expected that the data in the changeset has explicitly 661 preloaded the association being cast and that all of the IDs exist and 662 are unique. 663 664 For example, imagine a user has many addresses relationship where 665 post data is sent as follows 666 667 %{"name" => "john doe", "addresses" => [ 668 %{"street" => "somewhere", "country" => "brazil", "id" => 1}, 669 %{"street" => "elsewhere", "country" => "poland"}, 670 ]} 671 672 and then 673 674 User 675 |> Repo.get!(id) 676 |> Repo.preload(:addresses) # Only required when updating data 677 |> Ecto.Changeset.cast(params, []) 678 |> Ecto.Changeset.cast_assoc(:addresses, with: &MyApp.Address.changeset/2) 679 680 The parameters for the given association will be retrieved 681 from `changeset.params`. Those parameters are expected to be 682 a map with attributes, similar to the ones passed to `cast/4`. 683 Once parameters are retrieved, `cast_assoc/3` will match those 684 parameters with the associations already in the changeset data. 685 686 Once `cast_assoc/3` is called, Ecto will compare each parameter 687 with the user's already preloaded addresses and act as follows: 688 689 * If the parameter does not contain an ID, the parameter data 690 will be passed to `MyApp.Address.changeset/2` with a new struct 691 and become an insert operation 692 * If the parameter contains an ID and there is no associated child 693 with such ID, the parameter data will be passed to 694 `MyApp.Address.changeset/2` with a new struct and become an insert 695 operation 696 * If the parameter contains an ID and there is an associated child 697 with such ID, the parameter data will be passed to 698 `MyApp.Address.changeset/2` with the existing struct and become an 699 update operation 700 * If there is an associated child with an ID and its ID is not given 701 as parameter, the `:on_replace` callback for that association will 702 be invoked (see the "On replace" section on the module documentation) 703 704 Every time the `MyApp.Address.changeset/2` function is invoked, it must 705 return a changeset. This changeset will always be included under `changes` 706 of the parent changeset, even if there are no changes. This is done for 707 reflection purposes, allowing developers to introspect validations and 708 other metadata from the association. Once the parent changeset is given 709 to an `Ecto.Repo` function, all entries will be inserted/updated/deleted 710 within the same transaction. 711 712 Note developers are allowed to explicitly set the `:action` field of a 713 changeset to instruct Ecto how to act in certain situations. Let's suppose 714 that, if one of the associations has only empty fields, you want to ignore 715 the entry altogether instead of showing an error. The changeset function could 716 be written like this: 717 718 def changeset(struct, params) do 719 struct 720 |> cast(params, [:title, :body]) 721 |> validate_required([:title, :body]) 722 |> case do 723 %{valid?: false, changes: changes} = changeset when changes == %{} -> 724 # If the changeset is invalid and has no changes, it is 725 # because all required fields are missing, so we ignore it. 726 %{changeset | action: :ignore} 727 changeset -> 728 changeset 729 end 730 end 731 732 ## Partial changes for many-style associations 733 734 By preloading an association using a custom query you can confine the behavior 735 of `cast_assoc/3`. This opens up the possibility to work on a subset of the data, 736 instead of all associations in the database. 737 738 Taking the initial example of users having addresses imagine those addresses 739 are set up to belong to a country. If you want to allow users to bulk edit all 740 addresses that belong to a single country, you can do so by changing the preload 741 query: 742 743 query = from MyApp.Address, where: [country: ^edit_country] 744 745 User 746 |> Repo.get!(id) 747 |> Repo.preload(addresses: query) 748 |> Ecto.Changeset.cast(params, []) 749 |> Ecto.Changeset.cast_assoc(:addresses) 750 751 This will allow you to cast and update only the association for the given country. 752 The important point for partial changes is that any addresses, which were not 753 preloaded won't be changed. 754 755 ## Options 756 757 * `:required` - if the association is a required field 758 * `:required_message` - the message on failure, defaults to "can't be blank" 759 * `:invalid_message` - the message on failure, defaults to "is invalid" 760 * `:force_update_on_change` - force the parent record to be updated in the 761 repository if there is a change, defaults to `true` 762 * `:with` - the function to build the changeset from params. Defaults to the 763 `changeset/2` function of the associated module. It can be changed by passing 764 an anonymous function or an MFA tuple. If using an MFA, the default changeset 765 and parameters arguments will be prepended to the given args. For example, 766 using `with: {Author, :special_changeset, ["hello"]}` will be invoked as 767 `Author.special_changeset(changeset, params, "hello")` 768 769 """ 770 def cast_assoc(changeset, name, opts \\ []) when is_atom(name) do 771 cast_relation(:assoc, changeset, name, opts) 772 end 773 774 @doc """ 775 Casts the given embed with the changeset parameters. 776 777 The parameters for the given embed will be retrieved 778 from `changeset.params`. Those parameters are expected to be 779 a map with attributes, similar to the ones passed to `cast/4`. 780 Once parameters are retrieved, `cast_embed/3` will match those 781 parameters with the embeds already in the changeset record. 782 See `cast_assoc/3` for an example of working with casts and 783 associations which would also apply for embeds. 784 785 The changeset must have been previously `cast` using 786 `cast/4` before this function is invoked. 787 788 ## Options 789 790 * `:required` - if the embed is a required field 791 * `:required_message` - the message on failure, defaults to "can't be blank" 792 * `:invalid_message` - the message on failure, defaults to "is invalid" 793 * `:force_update_on_change` - force the parent record to be updated in the 794 repository if there is a change, defaults to `true` 795 * `:with` - the function to build the changeset from params. Defaults to the 796 `changeset/2` function of the embedded module. It can be changed by passing 797 an anonymous function or an MFA tuple. If using an MFA, the default changeset 798 and parameters arguments will be prepended to the given args. For example, 799 using `with: {Author, :special_changeset, ["hello"]}` will be invoked as 800 `Author.special_changeset(changeset, params, "hello")` 801 """ 802 def cast_embed(changeset, name, opts \\ []) when is_atom(name) do 803 cast_relation(:embed, changeset, name, opts) 804 end 805 806 defp cast_relation(type, %Changeset{data: data, types: types}, _name, _opts) 807 when data == nil or types == nil do 808 raise ArgumentError, "cast_#{type}/3 expects the changeset to be cast. " <> 809 "Please call cast/4 before calling cast_#{type}/3" 810 end 811 812 defp cast_relation(type, %Changeset{} = changeset, key, opts) do 813 {key, param_key} = cast_key(key) 814 %{data: data, types: types, params: params, changes: changes} = changeset 815 %{related: related} = relation = relation!(:cast, type, key, Map.get(types, key)) 816 params = params || %{} 817 818 {changeset, required?} = 819 if opts[:required] do 820 {update_in(changeset.required, &[key|&1]), true} 821 else 822 {changeset, false} 823 end 824 825 on_cast = Keyword.get_lazy(opts, :with, fn -> on_cast_default(type, related) end) 826 original = Map.get(data, key) 827 828 changeset = 829 case Map.fetch(params, param_key) do 830 {:ok, value} -> 831 current = Relation.load!(data, original) 832 case Relation.cast(relation, data, value, current, on_cast) do 833 {:ok, change, relation_valid?} when change != original -> 834 valid? = changeset.valid? and relation_valid? 835 changes = Map.put(changes, key, change) 836 changeset = %{force_update(changeset, opts) | changes: changes, valid?: valid?} 837 missing_relation(changeset, key, current, required?, relation, opts) 838 839 {:error, {message, meta}} -> 840 meta = [validation: type] ++ meta 841 error = {key, {message(opts, :invalid_message, message), meta}} 842 %{changeset | errors: [error | changeset.errors], valid?: false} 843 844 # ignore or ok with change == original 845 _ -> 846 missing_relation(changeset, key, current, required?, relation, opts) 847 end 848 849 :error -> 850 missing_relation(changeset, key, original, required?, relation, opts) 851 end 852 853 update_in changeset.types[key], fn {type, relation} -> 854 {type, %{relation | on_cast: on_cast}} 855 end 856 end 857 858 defp on_cast_default(type, module) do 859 fn struct, params -> 860 try do 861 module.changeset(struct, params) 862 rescue 863 e in UndefinedFunctionError -> 864 case __STACKTRACE__ do 865 [{^module, :changeset, args_or_arity, _}] when args_or_arity == 2 866 when length(args_or_arity) == 2 -> 867 raise ArgumentError, """ 868 the module #{inspect module} does not define a changeset/2 function, 869 which is used by cast_#{type}/3. You need to either: 870 871 1. implement the #{type}.changeset/2 function 872 2. pass the :with option to cast_#{type}/3 with an anonymous 873 function that expects 2 args or an MFA tuple 874 875 When using an inline embed, the :with option must be given 876 """ 877 stacktrace -> 878 reraise e, stacktrace 879 end 880 end 881 end 882 end 883 884 defp missing_relation(%{changes: changes, errors: errors} = changeset, 885 name, current, required?, relation, opts) do 886 current_changes = Map.get(changes, name, current) 887 if required? and Relation.empty?(relation, current_changes) do 888 errors = [{name, {message(opts, :required_message, "can't be blank"), [validation: :required]}} | errors] 889 %{changeset | errors: errors, valid?: false} 890 else 891 changeset 892 end 893 end 894 895 defp relation!(_op, type, _name, {type, relation}), 896 do: relation 897 defp relation!(op, :assoc, name, nil), 898 do: raise(ArgumentError, "cannot #{op} assoc `#{name}`, assoc `#{name}` not found. Make sure it is spelled correctly and that the association type is not read-only") 899 defp relation!(op, type, name, nil), 900 do: raise(ArgumentError, "cannot #{op} #{type} `#{name}`, #{type} `#{name}` not found. Make sure that it exists and is spelled correctly") 901 defp relation!(op, type, name, {other, _}) when other in @relations, 902 do: raise(ArgumentError, "expected `#{name}` to be an #{type} in `#{op}_#{type}`, got: `#{other}`") 903 defp relation!(op, type, name, schema_type), 904 do: raise(ArgumentError, "expected `#{name}` to be an #{type} in `#{op}_#{type}`, got: `#{inspect schema_type}`") 905 906 defp force_update(changeset, opts) do 907 if Keyword.get(opts, :force_update_on_change, true) do 908 put_in(changeset.repo_opts[:force], true) 909 else 910 changeset 911 end 912 end 913 914 ## Working with changesets 915 916 @doc """ 917 Merges two changesets. 918 919 This function merges two changesets provided they have been applied to the 920 same data (their `:data` field is equal); if the data differs, an 921 `ArgumentError` exception is raised. If one of the changesets has a `:repo` 922 field which is not `nil`, then the value of that field is used as the `:repo` 923 field of the resulting changeset; if both changesets have a non-`nil` and 924 different `:repo` field, an `ArgumentError` exception is raised. 925 926 The other fields are merged with the following criteria: 927 928 * `params` - params are merged (not deep-merged) giving precedence to the 929 params of `changeset2` in case of a conflict. If both changesets have their 930 `:params` fields set to `nil`, the resulting changeset will have its params 931 set to `nil` too. 932 * `changes` - changes are merged giving precedence to the `changeset2` 933 changes. 934 * `errors` and `validations` - they are simply concatenated. 935 * `required` - required fields are merged; all the fields that appear 936 in the required list of both changesets are moved to the required 937 list of the resulting changeset. 938 939 ## Examples 940 941 iex> changeset1 = cast(%Post{}, %{title: "Title"}, [:title]) 942 iex> changeset2 = cast(%Post{}, %{title: "New title", body: "Body"}, [:title, :body]) 943 iex> changeset = merge(changeset1, changeset2) 944 iex> changeset.changes 945 %{body: "Body", title: "New title"} 946 947 iex> changeset1 = cast(%Post{body: "Body"}, %{title: "Title"}, [:title]) 948 iex> changeset2 = cast(%Post{}, %{title: "New title"}, [:title]) 949 iex> merge(changeset1, changeset2) 950 ** (ArgumentError) different :data when merging changesets 951 952 """ 953 @spec merge(t, t) :: t 954 def merge(changeset1, changeset2) 955 956 def merge(%Changeset{data: data} = cs1, %Changeset{data: data} = cs2) do 957 new_repo = merge_identical(cs1.repo, cs2.repo, "repos") 958 new_repo_opts = Keyword.merge(cs1.repo_opts, cs2.repo_opts) 959 new_action = merge_identical(cs1.action, cs2.action, "actions") 960 new_filters = Map.merge(cs1.filters, cs2.filters) 961 new_validations = cs1.validations ++ cs2.validations 962 new_constraints = cs1.constraints ++ cs2.constraints 963 964 cast_merge %{cs1 | repo: new_repo, repo_opts: new_repo_opts, filters: new_filters, 965 action: new_action, validations: new_validations, 966 constraints: new_constraints}, cs2 967 end 968 969 def merge(%Changeset{}, %Changeset{}) do 970 raise ArgumentError, message: "different :data when merging changesets" 971 end 972 973 defp cast_merge(cs1, cs2) do 974 new_params = (cs1.params || cs2.params) && Map.merge(cs1.params || %{}, cs2.params || %{}) 975 new_changes = Map.merge(cs1.changes, cs2.changes) 976 new_errors = Enum.uniq(cs1.errors ++ cs2.errors) 977 new_required = Enum.uniq(cs1.required ++ cs2.required) 978 new_types = cs1.types || cs2.types 979 new_valid? = cs1.valid? and cs2.valid? 980 981 %{cs1 | params: new_params, valid?: new_valid?, errors: new_errors, types: new_types, 982 changes: new_changes, required: new_required} 983 end 984 985 defp merge_identical(object, nil, _thing), do: object 986 defp merge_identical(nil, object, _thing), do: object 987 defp merge_identical(object, object, _thing), do: object 988 defp merge_identical(lhs, rhs, thing) do 989 raise ArgumentError, "different #{thing} (`#{inspect lhs}` and " <> 990 "`#{inspect rhs}`) when merging changesets" 991 end 992 993 @doc """ 994 Fetches the given field from changes or from the data. 995 996 While `fetch_change/2` only looks at the current `changes` 997 to retrieve a value, this function looks at the changes and 998 then falls back on the data, finally returning `:error` if 999 no value is available. 1000 1001 For relations, these functions will return the changeset 1002 original data with changes applied. To retrieve raw changesets, 1003 please use `fetch_change/2`. 1004 1005 ## Examples 1006 1007 iex> post = %Post{title: "Foo", body: "Bar baz bong"} 1008 iex> changeset = change(post, %{title: "New title"}) 1009 iex> fetch_field(changeset, :title) 1010 {:changes, "New title"} 1011 iex> fetch_field(changeset, :body) 1012 {:data, "Bar baz bong"} 1013 iex> fetch_field(changeset, :not_a_field) 1014 :error 1015 1016 """ 1017 @spec fetch_field(t, atom) :: {:changes, term} | {:data, term} | :error 1018 def fetch_field(%Changeset{changes: changes, data: data, types: types}, key) when is_atom(key) do 1019 case Map.fetch(changes, key) do 1020 {:ok, value} -> 1021 {:changes, change_as_field(types, key, value)} 1022 :error -> 1023 case Map.fetch(data, key) do 1024 {:ok, value} -> {:data, data_as_field(data, types, key, value)} 1025 :error -> :error 1026 end 1027 end 1028 end 1029 1030 @doc """ 1031 Same as `fetch_field/2` but returns the value or raises if the given key was not found. 1032 1033 ## Examples 1034 1035 iex> post = %Post{title: "Foo", body: "Bar baz bong"} 1036 iex> changeset = change(post, %{title: "New title"}) 1037 iex> fetch_field!(changeset, :title) 1038 "New title" 1039 iex> fetch_field!(changeset, :other) 1040 ** (KeyError) key :other not found in: %Post{...} 1041 """ 1042 @spec fetch_field!(t, atom) :: term 1043 def fetch_field!(changeset, key) do 1044 case fetch_field(changeset, key) do 1045 {_, value} -> 1046 value 1047 1048 :error -> 1049 raise KeyError, key: key, term: changeset.data 1050 end 1051 end 1052 1053 @doc """ 1054 Gets a field from changes or from the data. 1055 1056 While `get_change/3` only looks at the current `changes` 1057 to retrieve a value, this function looks at the changes and 1058 then falls back on the data, finally returning `default` if 1059 no value is available. 1060 1061 For relations, these functions will return the changeset data 1062 with changes applied. To retrieve raw changesets, please use `get_change/3`. 1063 1064 iex> post = %Post{title: "A title", body: "My body is a cage"} 1065 iex> changeset = change(post, %{title: "A new title"}) 1066 iex> get_field(changeset, :title) 1067 "A new title" 1068 iex> get_field(changeset, :not_a_field, "Told you, not a field!") 1069 "Told you, not a field!" 1070 1071 """ 1072 @spec get_field(t, atom, term) :: term 1073 def get_field(%Changeset{changes: changes, data: data, types: types}, key, default \\ nil) do 1074 case Map.fetch(changes, key) do 1075 {:ok, value} -> 1076 change_as_field(types, key, value) 1077 :error -> 1078 case Map.fetch(data, key) do 1079 {:ok, value} -> data_as_field(data, types, key, value) 1080 :error -> default 1081 end 1082 end 1083 end 1084 1085 defp change_as_field(types, key, value) do 1086 case Map.get(types, key) do 1087 {tag, relation} when tag in @relations -> 1088 Relation.apply_changes(relation, value) 1089 _other -> 1090 value 1091 end 1092 end 1093 1094 defp data_as_field(data, types, key, value) do 1095 case Map.get(types, key) do 1096 {tag, _relation} when tag in @relations -> 1097 Relation.load!(data, value) 1098 _other -> 1099 value 1100 end 1101 end 1102 1103 @doc """ 1104 Fetches a change from the given changeset. 1105 1106 This function only looks at the `:changes` field of the given `changeset` and 1107 returns `{:ok, value}` if the change is present or `:error` if it's not. 1108 1109 ## Examples 1110 1111 iex> changeset = change(%Post{body: "foo"}, %{title: "bar"}) 1112 iex> fetch_change(changeset, :title) 1113 {:ok, "bar"} 1114 iex> fetch_change(changeset, :body) 1115 :error 1116 1117 """ 1118 @spec fetch_change(t, atom) :: {:ok, term} | :error 1119 def fetch_change(%Changeset{changes: changes} = _changeset, key) when is_atom(key) do 1120 Map.fetch(changes, key) 1121 end 1122 1123 @doc """ 1124 Same as `fetch_change/2` but returns the value or raises if the given key was not found. 1125 1126 ## Examples 1127 1128 iex> changeset = change(%Post{body: "foo"}, %{title: "bar"}) 1129 iex> fetch_change!(changeset, :title) 1130 "bar" 1131 iex> fetch_change!(changeset, :body) 1132 ** (KeyError) key :body not found in: %{title: "bar"} 1133 """ 1134 @spec fetch_change!(t, atom) :: term 1135 def fetch_change!(changeset, key) do 1136 case fetch_change(changeset, key) do 1137 {:ok, value} -> 1138 value 1139 1140 :error -> 1141 raise KeyError, key: key, term: changeset.changes 1142 end 1143 end 1144 1145 @doc """ 1146 Gets a change or returns a default value. 1147 1148 ## Examples 1149 1150 iex> changeset = change(%Post{body: "foo"}, %{title: "bar"}) 1151 iex> get_change(changeset, :title) 1152 "bar" 1153 iex> get_change(changeset, :body) 1154 nil 1155 1156 """ 1157 @spec get_change(t, atom, term) :: term 1158 def get_change(%Changeset{changes: changes} = _changeset, key, default \\ nil) when is_atom(key) do 1159 Map.get(changes, key, default) 1160 end 1161 1162 @doc """ 1163 Updates a change. 1164 1165 The given `function` is invoked with the change value only if there 1166 is a change for `key`. Note that the value of the change 1167 can still be `nil` (unless the field was marked as required on `validate_required/3`). 1168 1169 ## Examples 1170 1171 iex> changeset = change(%Post{}, %{impressions: 1}) 1172 iex> changeset = update_change(changeset, :impressions, &(&1 + 1)) 1173 iex> changeset.changes.impressions 1174 2 1175 1176 """ 1177 @spec update_change(t, atom, (term -> term)) :: t 1178 def update_change(%Changeset{changes: changes} = changeset, key, function) when is_atom(key) do 1179 case Map.fetch(changes, key) do 1180 {:ok, value} -> 1181 put_change(changeset, key, function.(value)) 1182 :error -> 1183 changeset 1184 end 1185 end 1186 1187 @doc """ 1188 Puts a change on the given `key` with `value`. 1189 1190 `key` is an atom that represents any field, embed or 1191 association in the changeset. Note the `value` is directly 1192 stored in the changeset with no validation whatsoever. 1193 For this reason, this function is meant for working with 1194 data internal to the application. 1195 1196 If the change is already present, it is overridden with 1197 the new value. If the change has the same value as in the 1198 changeset data, it is not added to the list of changes. 1199 1200 When changing embeds and associations, see `put_assoc/4` 1201 for a complete reference on the accepted values. 1202 1203 ## Examples 1204 1205 iex> changeset = change(%Post{}, %{title: "foo"}) 1206 iex> changeset = put_change(changeset, :title, "bar") 1207 iex> changeset.changes 1208 %{title: "bar"} 1209 1210 iex> changeset = change(%Post{title: "foo"}) 1211 iex> changeset = put_change(changeset, :title, "foo") 1212 iex> changeset.changes 1213 %{} 1214 1215 """ 1216 @spec put_change(t, atom, term) :: t 1217 def put_change(%Changeset{types: nil}, _key, _value) do 1218 raise ArgumentError, "changeset does not have types information" 1219 end 1220 1221 def put_change(%Changeset{data: data, types: types} = changeset, key, value) do 1222 type = Map.get(types, key) 1223 {changes, errors, valid?} = 1224 put_change(data, changeset.changes, changeset.errors, changeset.valid?, key, value, type) 1225 %{changeset | changes: changes, errors: errors, valid?: valid?} 1226 end 1227 1228 defp put_change(data, changes, errors, valid?, key, value, {tag, relation}) 1229 when tag in @relations do 1230 original = Map.get(data, key) 1231 current = Relation.load!(data, original) 1232 1233 case Relation.change(relation, value, current) do 1234 {:ok, change, relation_valid?} when change != original -> 1235 {Map.put(changes, key, change), errors, valid? and relation_valid?} 1236 {:error, error} -> 1237 {changes, [{key, error} | errors], false} 1238 # ignore or ok with change == original 1239 _ -> 1240 {Map.delete(changes, key), errors, valid?} 1241 end 1242 end 1243 1244 defp put_change(data, _changes, _errors, _valid?, key, _value, nil) when is_atom(key) do 1245 raise ArgumentError, "unknown field `#{inspect(key)}` in #{inspect(data)}" 1246 end 1247 1248 defp put_change(_data, _changes, _errors, _valid?, key, _value, nil) when not is_atom(key) do 1249 raise ArgumentError, "field names given to change/put_change must be atoms, got: `#{inspect(key)}`" 1250 end 1251 1252 defp put_change(data, changes, errors, valid?, key, value, type) do 1253 if not Ecto.Type.equal?(type, Map.get(data, key), value) do 1254 {Map.put(changes, key, value), errors, valid?} 1255 else 1256 {Map.delete(changes, key), errors, valid?} 1257 end 1258 end 1259 1260 @doc """ 1261 Puts the given association entry or entries as a change in the changeset. 1262 1263 This function is used to work with associations as a whole. For example, 1264 if a Post has many Comments, it allows you to add, remove or change all 1265 comments at once. If your goal is to simply add a new comment to a post, 1266 then it is preferred to do so manually, as we will describe later in the 1267 "Example: Adding a comment to a post" section. 1268 1269 This function requires the associated data to have been preloaded, except 1270 when the parent changeset has been newly built and not yet persisted. 1271 Missing data will invoke the `:on_replace` behaviour defined on the 1272 association. 1273 1274 For associations with cardinality one, `nil` can be used to remove the existing 1275 entry. For associations with many entries, an empty list may be given instead. 1276 1277 If the association has no changes, it will be skipped. If the association is 1278 invalid, the changeset will be marked as invalid. If the given value is not any 1279 of values below, it will raise. 1280 1281 The associated data may be given in different formats: 1282 1283 * a map or a keyword list representing changes to be applied to the 1284 associated data. A map or keyword list can be given to update the 1285 associated data as long as they have matching primary keys. 1286 For example, `put_assoc(changeset, :comments, [%{id: 1, title: "changed"}])` 1287 will locate the comment with `:id` of 1 and update its title. 1288 If no comment with such id exists, one is created on the fly. 1289 Since only a single comment was given, any other associated comment 1290 will be replaced. On all cases, it is expected the keys to be atoms. 1291 Opposite to `cast_assoc` and `embed_assoc`, the given map (or struct) 1292 is not validated in any way and will be inserted as is. 1293 This API is mostly used in scripts and tests, to make it straight- 1294 forward to create schemas with associations at once, such as: 1295 1296 Ecto.Changeset.change( 1297 %Post{}, 1298 title: "foo", 1299 comments: [ 1300 %{body: "first"}, 1301 %{body: "second"} 1302 ] 1303 ) 1304 1305 * changesets - when changesets are given, they are treated as the canonical 1306 data and the associated data currently stored in the association is either 1307 updated or replaced. For example, if you call 1308 `put_assoc(post_changeset, :comments, [list_of_comments_changesets])`, 1309 all comments with matching IDs will be updated according to the changesets. 1310 New comments or comments not associated to any post will be correctly 1311 associated. Currently associated comments that do not have a matching ID 1312 in the list of changesets will act according to the `:on_replace` association 1313 configuration (you can chose to raise, ignore the operation, update or delete 1314 them). If there are changes in any of the changesets, they will be 1315 persisted too. 1316 1317 * structs - when structs are given, they are treated as the canonical data 1318 and the associated data currently stored in the association is replaced. 1319 For example, if you call 1320 `put_assoc(post_changeset, :comments, [list_of_comments_structs])`, 1321 all comments with matching IDs will be replaced by the new structs. 1322 New comments or comments not associated to any post will be correctly 1323 associated. Currently associated comments that do not have a matching ID 1324 in the list of changesets will act according to the `:on_replace` 1325 association configuration (you can chose to raise, ignore the operation, 1326 update or delete them). Different to passing changesets, structs are not 1327 change tracked in any fashion. In other words, if you change a comment 1328 struct and give it to `put_assoc/4`, the updates in the struct won't be 1329 persisted. You must use changesets instead. `put_assoc/4` with structs 1330 only takes care of guaranteeing that the comments and the parent data 1331 are associated. This is extremely useful when associating existing data, 1332 as we will see in the "Example: Adding tags to a post" section. 1333 1334 Once the parent changeset is given to an `Ecto.Repo` function, all entries 1335 will be inserted/updated/deleted within the same transaction. 1336 1337 ## Example: Adding a comment to a post 1338 1339 Imagine a relationship where Post has many comments and you want to add a 1340 new comment to an existing post. While it is possible to use `put_assoc/4` 1341 for this, it would be unnecessarily complex. Let's see an example. 1342 1343 First, let's fetch the post with all existing comments: 1344 1345 post = Post |> Repo.get!(1) |> Repo.preload(:comments) 1346 1347 The following approach is **wrong**: 1348 1349 post 1350 |> Ecto.Changeset.change() 1351 |> Ecto.Changeset.put_assoc(:comments, [%Comment{body: "bad example!"}]) 1352 |> Repo.update!() 1353 1354 The reason why the example above is wrong is because `put_assoc/4` always 1355 works with the **full data**. So the example above will effectively **erase 1356 all previous comments** and only keep the comment you are currently adding. 1357 Instead, you could try: 1358 1359 post 1360 |> Ecto.Changeset.change() 1361 |> Ecto.Changeset.put_assoc(:comments, [%Comment{body: "so-so example!"} | post.comments]) 1362 |> Repo.update!() 1363 1364 In this example, we prepend the new comment to the list of existing comments. 1365 Ecto will diff the list of comments currently in `post` with the list of comments 1366 given, and correctly insert the new comment to the database. Note, however, 1367 Ecto is doing a lot of work just to figure out something we knew since the 1368 beginning, which is that there is only one new comment. 1369 1370 In cases like above, when you want to work only on a single entry, it is 1371 much easier to simply work on the associated directly. For example, we 1372 could instead set the `post` association in the comment: 1373 1374 %Comment{body: "better example"} 1375 |> Ecto.Changeset.change() 1376 |> Ecto.Changeset.put_assoc(:post, post) 1377 |> Repo.insert!() 1378 1379 Alternatively, we can make sure that when we create a comment, it is already 1380 associated to the post: 1381 1382 Ecto.build_assoc(post, :comments) 1383 |> Ecto.Changeset.change(body: "great example!") 1384 |> Repo.insert!() 1385 1386 Or we can simply set the post_id in the comment itself: 1387 1388 %Comment{body: "better example", post_id: post.id} 1389 |> Repo.insert!() 1390 1391 In other words, when you find yourself wanting to work only with a subset 1392 of the data, then using `put_assoc/4` is most likely unnecessary. Instead, 1393 you want to work on the other side of the association. 1394 1395 Let's see an example where using `put_assoc/4` is a good fit. 1396 1397 ## Example: Adding tags to a post 1398 1399 Imagine you are receiving a set of tags you want to associate to a post. 1400 Let's imagine that those tags exist upfront and are all persisted to the 1401 database. Imagine we get the data in this format: 1402 1403 params = %{"title" => "new post", "tags" => ["learner"]} 1404 1405 Now, since the tags already exist, we will bring all of them from the 1406 database and put them directly in the post: 1407 1408 tags = Repo.all(from t in Tag, where: t.name in ^params["tags"]) 1409 1410 post 1411 |> Repo.preload(:tags) 1412 |> Ecto.Changeset.cast(params, [:title]) # No need to allow :tags as we put them directly 1413 |> Ecto.Changeset.put_assoc(:tags, tags) # Explicitly set the tags 1414 1415 Since in this case we always require the user to pass all tags 1416 directly, using `put_assoc/4` is a great fit. It will automatically 1417 remove any tag not given and properly associate all of the given 1418 tags with the post. 1419 1420 Furthermore, since the tag information is given as structs read directly 1421 from the database, Ecto will treat the data as correct and only do the 1422 minimum necessary to guarantee that posts and tags are associated, 1423 without trying to update or diff any of the fields in the tag struct. 1424 1425 Although it accepts an `opts` argument, there are no options currently 1426 supported by `put_assoc/4`. 1427 """ 1428 def put_assoc(%Changeset{} = changeset, name, value, opts \\ []) do 1429 put_relation(:assoc, changeset, name, value, opts) 1430 end 1431 1432 @doc """ 1433 Puts the given embed entry or entries as a change in the changeset. 1434 1435 This function is used to work with embeds as a whole. For embeds with 1436 cardinality one, `nil` can be used to remove the existing entry. For 1437 embeds with many entries, an empty list may be given instead. 1438 1439 If the embed has no changes, it will be skipped. If the embed is 1440 invalid, the changeset will be marked as invalid. 1441 1442 The list of supported values and their behaviour is described in 1443 `put_assoc/4`. If the given value is not any of values listed there, 1444 it will raise. 1445 1446 Although this function accepts an `opts` argument, there are no options 1447 currently supported by `put_embed/4`. 1448 """ 1449 def put_embed(%Changeset{} = changeset, name, value, opts \\ []) do 1450 put_relation(:embed, changeset, name, value, opts) 1451 end 1452 1453 defp put_relation(_tag, %{types: nil}, _name, _value, _opts) do 1454 raise ArgumentError, "changeset does not have types information" 1455 end 1456 1457 defp put_relation(tag, changeset, name, value, _opts) do 1458 %{data: data, types: types, changes: changes, errors: errors, valid?: valid?} = changeset 1459 relation = relation!(:put, tag, name, Map.get(types, name)) 1460 {changes, errors, valid?} = 1461 put_change(data, changes, errors, valid?, name, value, {tag, relation}) 1462 %{changeset | changes: changes, errors: errors, valid?: valid?} 1463 end 1464 1465 @doc """ 1466 Forces a change on the given `key` with `value`. 1467 1468 If the change is already present, it is overridden with 1469 the new value. 1470 1471 ## Examples 1472 1473 iex> changeset = change(%Post{author: "bar"}, %{title: "foo"}) 1474 iex> changeset = force_change(changeset, :title, "bar") 1475 iex> changeset.changes 1476 %{title: "bar"} 1477 1478 iex> changeset = force_change(changeset, :author, "bar") 1479 iex> changeset.changes 1480 %{title: "bar", author: "bar"} 1481 1482 """ 1483 @spec force_change(t, atom, term) :: t 1484 def force_change(%Changeset{types: nil}, _key, _value) do 1485 raise ArgumentError, "changeset does not have types information" 1486 end 1487 1488 def force_change(%Changeset{types: types} = changeset, key, value) do 1489 case Map.get(types, key) do 1490 {tag, _} when tag in @relations -> 1491 raise "changing #{tag}s with force_change/3 is not supported, " <> 1492 "please use put_#{tag}/4 instead" 1493 nil -> 1494 raise ArgumentError, "unknown field `#{inspect(key)}` in #{inspect(changeset.data)}" 1495 _ -> 1496 put_in changeset.changes[key], value 1497 end 1498 end 1499 1500 @doc """ 1501 Deletes a change with the given key. 1502 1503 ## Examples 1504 1505 iex> changeset = change(%Post{}, %{title: "foo"}) 1506 iex> changeset = delete_change(changeset, :title) 1507 iex> get_change(changeset, :title) 1508 nil 1509 1510 """ 1511 @spec delete_change(t, atom) :: t 1512 def delete_change(%Changeset{} = changeset, key) when is_atom(key) do 1513 update_in changeset.changes, &Map.delete(&1, key) 1514 end 1515 1516 1517 @doc """ 1518 Applies the changeset changes to the changeset data. 1519 1520 This operation will return the underlying data with changes 1521 regardless if the changeset is valid or not. See `apply_action/2` 1522 for a similar function that ensures the changeset is valid. 1523 1524 ## Examples 1525 1526 iex> changeset = change(%Post{author: "bar"}, %{title: "foo"}) 1527 iex> apply_changes(changeset) 1528 %Post{author: "bar", title: "foo"} 1529 1530 """ 1531 @spec apply_changes(t) :: Ecto.Schema.t | data 1532 def apply_changes(%Changeset{changes: changes, data: data}) when changes == %{} do 1533 data 1534 end 1535 1536 def apply_changes(%Changeset{changes: changes, data: data, types: types}) do 1537 Enum.reduce(changes, data, fn {key, value}, acc -> 1538 case Map.fetch(types, key) do 1539 {:ok, {tag, relation}} when tag in @relations -> 1540 apply_relation_changes(acc, key, relation, value) 1541 1542 {:ok, _} -> 1543 Map.put(acc, key, value) 1544 :error -> 1545 acc 1546 end 1547 end) 1548 end 1549 1550 @doc """ 1551 Applies the changeset action only if the changes are valid. 1552 1553 If the changes are valid, all changes are applied to the changeset data. 1554 If the changes are invalid, no changes are applied, and an error tuple 1555 is returned with the changeset containing the action that was attempted 1556 to be applied. 1557 1558 The action may be any atom. 1559 1560 ## Examples 1561 1562 iex> {:ok, data} = apply_action(changeset, :update) 1563 1564 iex> {:ok, data} = apply_action(changeset, :my_action) 1565 1566 iex> {:error, changeset} = apply_action(changeset, :update) 1567 %Ecto.Changeset{action: :update} 1568 """ 1569 @spec apply_action(t, atom) :: {:ok, Ecto.Schema.t() | data} | {:error, t} 1570 def apply_action(%Changeset{} = changeset, action) when is_atom(action) do 1571 if changeset.valid? do 1572 {:ok, apply_changes(changeset)} 1573 else 1574 {:error, %Changeset{changeset | action: action}} 1575 end 1576 end 1577 1578 def apply_action(%Changeset{}, action) do 1579 raise ArgumentError, "expected action to be an atom, got: #{inspect action}" 1580 end 1581 1582 @doc """ 1583 Applies the changeset action if the changes are valid or raises an error. 1584 1585 ## Examples 1586 1587 iex> changeset = change(%Post{author: "bar"}, %{title: "foo"}) 1588 iex> apply_action!(changeset, :update) 1589 %Post{author: "bar", title: "foo"} 1590 1591 iex> changeset = change(%Post{author: "bar"}, %{title: :bad}) 1592 iex> apply_action!(changeset, :update) 1593 ** (Ecto.InvalidChangesetError) could not perform update because changeset is invalid. 1594 1595 See `apply_action/2` for more information. 1596 """ 1597 @spec apply_action!(t, atom) :: Ecto.Schema.t() | data 1598 def apply_action!(%Changeset{} = changeset, action) do 1599 case apply_action(changeset, action) do 1600 {:ok, data} -> 1601 data 1602 1603 {:error, changeset} -> 1604 raise Ecto.InvalidChangesetError, action: action, changeset: changeset 1605 end 1606 end 1607 1608 ## Validations 1609 1610 @doc ~S""" 1611 Returns a keyword list of the validations for this changeset. 1612 1613 The keys in the list are the names of fields, and the values are a 1614 validation associated with the field. A field may occur multiple 1615 times in the list. 1616 1617 ## Example 1618 1619 %Post{} 1620 |> change() 1621 |> validate_format(:title, ~r/^\w+:\s/, message: "must start with a topic") 1622 |> validate_length(:title, max: 100) 1623 |> validations() 1624 #=> [ 1625 title: {:length, [ max: 100 ]}, 1626 title: {:format, ~r/^\w+:\s/} 1627 ] 1628 1629 The following validations may be included in the result. The list is 1630 not necessarily exhaustive. For example, custom validations written 1631 by the developer will also appear in our return value. 1632 1633 This first group contains validations that hold a keyword list of validators. 1634 This list may also include a `:message` key. 1635 1636 * `{:length, [option]}` 1637 1638 * `min: n` 1639 * `max: n` 1640 * `is: n` 1641 * `count: :graphemes | :codepoints` 1642 1643 * `{:number, [option]}` 1644 1645 * `equal_to: n` 1646 * `greater_than: n` 1647 * `greater_than_or_equal_to: n` 1648 * `less_than: n` 1649 * `less_than_or_equal_to: n` 1650 1651 The other validators simply take a value: 1652 1653 * `{:exclusion, Enum.t}` 1654 * `{:format, ~r/pattern/}` 1655 * `{:inclusion, Enum.t}` 1656 * `{:subset, Enum.t}` 1657 1658 Note that calling `validate_required/3` does not store the validation under the 1659 `changeset.validations` key (and so won't be included in the result of this 1660 function). The required fields are stored under the `changeset.required` key. 1661 """ 1662 @spec validations(t) :: [{atom, term}] 1663 def validations(%Changeset{validations: validations}) do 1664 validations 1665 end 1666 1667 @doc """ 1668 Adds an error to the changeset. 1669 1670 An additional keyword list `keys` can be passed to provide additional 1671 contextual information for the error. This is useful when using 1672 `traverse_errors/2` and when translating errors with `Gettext` 1673 1674 ## Examples 1675 1676 iex> changeset = change(%Post{}, %{title: ""}) 1677 iex> changeset = add_error(changeset, :title, "empty") 1678 iex> changeset.errors 1679 [title: {"empty", []}] 1680 iex> changeset.valid? 1681 false 1682 1683 iex> changeset = change(%Post{}, %{title: ""}) 1684 iex> changeset = add_error(changeset, :title, "empty", additional: "info") 1685 iex> changeset.errors 1686 [title: {"empty", [additional: "info"]}] 1687 iex> changeset.valid? 1688 false 1689 1690 iex> changeset = change(%Post{}, %{tags: ["ecto", "elixir", "x"]}) 1691 iex> changeset = add_error(changeset, :tags, "tag '%{val}' is too short", val: "x") 1692 iex> changeset.errors 1693 [tags: {"tag '%{val}' is too short", [val: "x"]}] 1694 iex> changeset.valid? 1695 false 1696 """ 1697 @spec add_error(t, atom, String.t, Keyword.t) :: t 1698 def add_error(%Changeset{errors: errors} = changeset, key, message, keys \\ []) when is_binary(message) do 1699 %{changeset | errors: [{key, {message, keys}}|errors], valid?: false} 1700 end 1701 1702 @doc """ 1703 Validates the given `field` change. 1704 1705 It invokes the `validator` function to perform the validation 1706 only if a change for the given `field` exists and the change 1707 value is not `nil`. The function must return a list of errors 1708 (with an empty list meaning no errors). 1709 1710 In case there's at least one error, the list of errors will be appended to the 1711 `:errors` field of the changeset and the `:valid?` flag will be set to 1712 `false`. 1713 1714 ## Examples 1715 1716 iex> changeset = change(%Post{}, %{title: "foo"}) 1717 iex> changeset = validate_change changeset, :title, fn :title, title -> 1718 ...> # Value must not be "foo"! 1719 ...> if title == "foo" do 1720 ...> [title: "cannot be foo"] 1721 ...> else 1722 ...> [] 1723 ...> end 1724 ...> end 1725 iex> changeset.errors 1726 [title: {"cannot be foo", []}] 1727 1728 """ 1729 @spec validate_change(t, atom, (atom, term -> [{atom, String.t} | {atom, {String.t, Keyword.t}}])) :: t 1730 def validate_change(%Changeset{} = changeset, field, validator) when is_atom(field) do 1731 %{changes: changes, types: types, errors: errors} = changeset 1732 ensure_field_exists!(changeset, types, field) 1733 1734 value = Map.get(changes, field) 1735 new = if is_nil(value), do: [], else: validator.(field, value) 1736 new = 1737 Enum.map(new, fn 1738 {key, val} when is_atom(key) and is_binary(val) -> 1739 {key, {val, []}} 1740 {key, {val, opts}} when is_atom(key) and is_binary(val) and is_list(opts) -> 1741 {key, {val, opts}} 1742 end) 1743 1744 case new do 1745 [] -> changeset 1746 [_|_] -> %{changeset | errors: new ++ errors, valid?: false} 1747 end 1748 end 1749 1750 @doc """ 1751 Stores the validation `metadata` and validates the given `field` change. 1752 1753 Similar to `validate_change/3` but stores the validation metadata 1754 into the changeset validators. The validator metadata is often used 1755 as a reflection mechanism, to automatically generate code based on 1756 the available validations. 1757 1758 ## Examples 1759 1760 iex> changeset = change(%Post{}, %{title: "foo"}) 1761 iex> changeset = validate_change changeset, :title, :useless_validator, fn 1762 ...> _, _ -> [] 1763 ...> end 1764 iex> changeset.validations 1765 [title: :useless_validator] 1766 1767 """ 1768 @spec validate_change(t, atom, term, (atom, term -> [{atom, String.t} | {atom, {String.t, Keyword.t}}])) :: t 1769 def validate_change(%Changeset{validations: validations} = changeset, 1770 field, metadata, validator) do 1771 changeset = %{changeset | validations: [{field, metadata}|validations]} 1772 validate_change(changeset, field, validator) 1773 end 1774 1775 @doc """ 1776 Validates that one or more fields are present in the changeset. 1777 1778 You can pass a single field name or a list of field names that 1779 are required. 1780 1781 If the value of a field is `nil` or a string made only of whitespace, 1782 the changeset is marked as invalid, the field is removed from the 1783 changeset's changes, and an error is added. An error won't be added if 1784 the field already has an error. 1785 1786 If a field is given to `validate_required/3` but it has not been passed 1787 as parameter during `cast/3` (i.e. it has not been changed), then 1788 `validate_required/3` will check for its current value in the data. 1789 If the data contains an non-empty value for the field, then no error is 1790 added. This allows developers to use `validate_required/3` to perform 1791 partial updates. For example, on `insert` all fields would be required, 1792 because their default values on the data are all `nil`, but on `update`, 1793 if you don't want to change a field that has been previously set, 1794 you are not required to pass it as a parameter, since `validate_required/3` 1795 won't add an error for missing changes as long as the value in the 1796 data given to the `changeset` is not empty. 1797 1798 Do not use this function to validate associations that are required, 1799 instead pass the `:required` option to `cast_assoc/3` or `cast_embed/3`. 1800 1801 Opposite to other validations, calling this function does not store 1802 the validation under the `changeset.validations` key. Instead, it 1803 stores all required fields under `changeset.required`. 1804 1805 ## Options 1806 1807 * `:message` - the message on failure, defaults to "can't be blank" 1808 * `:trim` - a boolean that sets whether whitespaces are removed before 1809 running the validation on binaries/strings, defaults to true 1810 1811 ## Examples 1812 1813 validate_required(changeset, :title) 1814 validate_required(changeset, [:title, :body]) 1815 1816 """ 1817 @spec validate_required(t, list | atom, Keyword.t) :: t 1818 def validate_required(%Changeset{} = changeset, fields, opts \\ []) when not is_nil(fields) do 1819 %{required: required, errors: errors, changes: changes, types: types} = changeset 1820 trim = Keyword.get(opts, :trim, true) 1821 fields = List.wrap(fields) 1822 1823 fields_with_errors = 1824 for field <- fields, 1825 ensure_field_not_many!(types, field), 1826 missing?(changeset, field, trim), 1827 ensure_field_exists!(changeset, types, field), 1828 is_nil(errors[field]), 1829 do: field 1830 1831 case fields_with_errors do 1832 [] -> 1833 %{changeset | required: fields ++ required} 1834 1835 _ -> 1836 message = message(opts, "can't be blank") 1837 new_errors = Enum.map(fields_with_errors, &{&1, {message, [validation: :required]}}) 1838 changes = Map.drop(changes, fields_with_errors) 1839 %{changeset | changes: changes, required: fields ++ required, errors: new_errors ++ errors, valid?: false} 1840 end 1841 end 1842 1843 @doc """ 1844 Validates that no existing record with a different primary key 1845 has the same values for these fields. 1846 1847 This function exists to provide quick feedback to users of your 1848 application. It should not be relied on for any data guarantee as it 1849 has race conditions and is inherently unsafe. For example, if this 1850 check happens twice in the same time interval (because the user 1851 submitted a form twice), both checks may pass and you may end-up with 1852 duplicate entries in the database. Therefore, a `unique_constraint/3` 1853 should also be used to ensure your data won't get corrupted. 1854 1855 However, because constraints are only checked if all validations 1856 succeed, this function can be used as an early check to provide 1857 early feedback to users, since most conflicting data will have been 1858 inserted prior to the current validation phase. 1859 1860 ## Options 1861 1862 * `:message` - the message in case the constraint check fails, 1863 defaults to "has already been taken". 1864 1865 * `:error_key` - the key to which changeset error will be added when 1866 check fails, defaults to the first field name of the given list of 1867 fields. 1868 1869 * `:prefix` - the prefix to run the query on (such as the schema path 1870 in Postgres or the database in MySQL). See `Ecto.Repo` documentation 1871 for more information. 1872 1873 * `:repo_opts` - the options to pass to the `Ecto.Repo` call. 1874 1875 * `:query` - the base query to use for the check. Defaults to the schema of 1876 the changeset. If the primary key is set, a clause will be added to exclude 1877 the changeset row itself from the check. 1878 1879 ## Examples 1880 1881 unsafe_validate_unique(changeset, :city_name, repo) 1882 unsafe_validate_unique(changeset, [:city_name, :state_name], repo) 1883 unsafe_validate_unique(changeset, [:city_name, :state_name], repo, message: "city must be unique within state") 1884 unsafe_validate_unique(changeset, [:city_name, :state_name], repo, prefix: "public") 1885 unsafe_validate_unique(changeset, [:city_name, :state_name], repo, query: from(c in City, where: is_nil(c.deleted_at))) 1886 1887 """ 1888 @spec unsafe_validate_unique(t, atom | [atom, ...], Ecto.Repo.t, Keyword.t) :: t 1889 def unsafe_validate_unique(%Changeset{} = changeset, fields, repo, opts \\ []) when is_list(opts) do 1890 {repo_opts, opts} = Keyword.pop(opts, :repo_opts, []) 1891 %{data: data, validations: validations} = changeset 1892 1893 unless is_struct(data) and function_exported?(data.__struct__, :__schema__, 1) do 1894 raise ArgumentError, 1895 "unsafe_validate_unique/4 does not work with schemaless changesets, got #{inspect(data)}" 1896 end 1897 1898 schema = 1899 case {changeset.data, opts[:query]} do 1900 # regular schema 1901 {%schema{__meta__: %Metadata{}}, _} -> 1902 schema 1903 1904 # embedded schema with base query 1905 {%schema{}, base_query} when base_query != nil -> 1906 schema 1907 1908 # embedded schema without base query 1909 {data, _} -> 1910 raise ArgumentError, 1911 "unsafe_validate_unique/4 does not work with embedded schemas unless " <> 1912 "the `:query` option is specified, got: #{inspect(data)}" 1913 end 1914 1915 fields = List.wrap(fields) 1916 changeset = %{changeset | validations: [{hd(fields), {:unsafe_unique, fields: fields}} | validations]} 1917 1918 where_clause = for field <- fields do 1919 {field, get_field(changeset, field)} 1920 end 1921 1922 # No need to query if there is a prior error for the fields 1923 any_prior_errors_for_fields? = Enum.any?(changeset.errors, &(elem(&1, 0) in fields)) 1924 1925 # No need to query if we haven't changed any of the fields in question 1926 unrelated_changes? = Enum.all?(fields, ¬ Map.has_key?(changeset.changes, &1)) 1927 1928 # If we don't have values for all fields, we can't query for uniqueness 1929 any_nil_values_for_fields? = Enum.any?(where_clause, &(&1 |> elem(1) |> is_nil())) 1930 1931 if unrelated_changes? or any_nil_values_for_fields? or any_prior_errors_for_fields? do 1932 changeset 1933 else 1934 query = 1935 Keyword.get(opts, :query, schema) 1936 |> maybe_exclude_itself(schema, changeset) 1937 |> Ecto.Query.where(^where_clause) 1938 1939 query = 1940 if prefix = opts[:prefix] do 1941 Ecto.Query.put_query_prefix(query, prefix) 1942 else 1943 query 1944 end 1945 1946 if repo.exists?(query, repo_opts) do 1947 error_key = Keyword.get(opts, :error_key, hd(fields)) 1948 1949 add_error(changeset, error_key, message(opts, "has already been taken"), 1950 validation: :unsafe_unique, fields: fields) 1951 else 1952 changeset 1953 end 1954 end 1955 end 1956 1957 defp maybe_exclude_itself(base_query, schema, changeset) do 1958 :primary_key 1959 |> schema.__schema__() 1960 |> Enum.map(&{&1, get_field(changeset, &1)}) 1961 |> case do 1962 [{_pk_field, nil} | _remaining_pks] -> 1963 base_query 1964 1965 [{pk_field, value} | remaining_pks] -> 1966 # generate a clean query (one that does not start with 'TRUE OR ...') 1967 first_expr = Ecto.Query.dynamic([q], field(q, ^pk_field) == ^value) 1968 1969 Enum.reduce_while(remaining_pks, first_expr, fn 1970 {_pk_field, nil}, _expr -> 1971 {:halt, nil} 1972 1973 {pk_field, value}, expr -> 1974 {:cont, Ecto.Query.dynamic([q], ^expr and field(q, ^pk_field) == ^value)} 1975 end) 1976 |> case do 1977 nil -> 1978 base_query 1979 1980 matches_pk -> 1981 Ecto.Query.where(base_query, ^Ecto.Query.dynamic(not (^matches_pk))) 1982 end 1983 1984 [] -> 1985 base_query 1986 end 1987 end 1988 1989 defp ensure_field_exists!(changeset = %Changeset{}, types, field) do 1990 unless Map.has_key?(types, field) do 1991 raise ArgumentError, "unknown field #{inspect(field)} in #{inspect(changeset.data)}" 1992 end 1993 true 1994 end 1995 1996 defp ensure_field_not_many!(types, field) do 1997 case types do 1998 %{^field => {:assoc, %Ecto.Association.Has{cardinality: :many}}} -> 1999 IO.warn("attempting to validate has_many association #{inspect(field)} " <> 2000 "with validate_required/3 which has no effect. You can pass the " <> 2001 ":required option to Ecto.Changeset.cast_assoc/3 to achieve this.") 2002 2003 %{^field => {:embed, %Ecto.Embedded{cardinality: :many}}} -> 2004 IO.warn("attempting to validate embed_many field #{inspect(field)} " <> 2005 "with validate_required/3 which has no effect. You can pass the " <> 2006 ":required option to Ecto.Changeset.cast_embed/3 to achieve this.") 2007 2008 _ -> 2009 true 2010 end 2011 end 2012 2013 defp missing?(changeset, field, trim) when is_atom(field) do 2014 case get_field(changeset, field) do 2015 %{__struct__: Ecto.Association.NotLoaded} -> 2016 raise ArgumentError, "attempting to validate association `#{field}` " <> 2017 "that was not loaded. Please preload your associations " <> 2018 "before calling validate_required/3 or pass the :required " <> 2019 "option to Ecto.Changeset.cast_assoc/3" 2020 value when is_binary(value) and trim -> String.trim_leading(value) == "" 2021 value when is_binary(value) -> value == "" 2022 nil -> true 2023 _ -> false 2024 end 2025 end 2026 2027 defp missing?(_changeset, field, _trim) do 2028 raise ArgumentError, "validate_required/3 expects field names to be atoms, got: `#{inspect field}`" 2029 end 2030 2031 @doc """ 2032 Validates a change has the given format. 2033 2034 The format has to be expressed as a regular expression. 2035 2036 ## Options 2037 2038 * `:message` - the message on failure, defaults to "has invalid format" 2039 2040 ## Examples 2041 2042 validate_format(changeset, :email, ~r/@/) 2043 2044 """ 2045 @spec validate_format(t, atom, Regex.t, Keyword.t) :: t 2046 def validate_format(changeset, field, format, opts \\ []) do 2047 validate_change changeset, field, {:format, format}, fn _, value -> 2048 if value =~ format, do: [], else: [{field, {message(opts, "has invalid format"), [validation: :format]}}] 2049 end 2050 end 2051 2052 @doc """ 2053 Validates a change is included in the given enumerable. 2054 2055 ## Options 2056 2057 * `:message` - the message on failure, defaults to "is invalid" 2058 2059 ## Examples 2060 2061 validate_inclusion(changeset, :cardinal_direction, ["north", "east", "south", "west"]) 2062 validate_inclusion(changeset, :age, 0..99) 2063 2064 """ 2065 @spec validate_inclusion(t, atom, Enum.t, Keyword.t) :: t 2066 def validate_inclusion(changeset, field, data, opts \\ []) do 2067 validate_change changeset, field, {:inclusion, data}, fn _, value -> 2068 type = Map.fetch!(changeset.types, field) 2069 2070 if Ecto.Type.include?(type, value, data), 2071 do: [], 2072 else: [{field, {message(opts, "is invalid"), [validation: :inclusion, enum: data]}}] 2073 end 2074 end 2075 2076 @doc ~S""" 2077 Validates a change, of type enum, is a subset of the given enumerable. 2078 2079 This validates if a list of values belongs to the given enumerable. 2080 If you need to validate if a single value is inside the given enumerable, 2081 you should use `validate_inclusion/4` instead. 2082 2083 Type of the field must be array. 2084 2085 ## Options 2086 2087 * `:message` - the message on failure, defaults to "has an invalid entry" 2088 2089 ## Examples 2090 2091 validate_subset(changeset, :pets, ["cat", "dog", "parrot"]) 2092 validate_subset(changeset, :lottery_numbers, 0..99) 2093 2094 """ 2095 @spec validate_subset(t, atom, Enum.t, Keyword.t) :: t 2096 def validate_subset(changeset, field, data, opts \\ []) do 2097 validate_change changeset, field, {:subset, data}, fn _, value -> 2098 element_type = 2099 case Map.fetch!(changeset.types, field) do 2100 {:array, element_type} -> 2101 element_type 2102 2103 type -> 2104 # backwards compatibility: custom types use underlying type 2105 {:array, element_type} = Ecto.Type.type(type) 2106 element_type 2107 end 2108 2109 case Enum.any?(value, fn element -> not Ecto.Type.include?(element_type, element, data) end) do 2110 true -> [{field, {message(opts, "has an invalid entry"), [validation: :subset, enum: data]}}] 2111 false -> [] 2112 end 2113 end 2114 end 2115 2116 @doc """ 2117 Validates a change is not included in the given enumerable. 2118 2119 ## Options 2120 2121 * `:message` - the message on failure, defaults to "is reserved" 2122 2123 ## Examples 2124 2125 validate_exclusion(changeset, :name, ~w(admin superadmin)) 2126 2127 """ 2128 @spec validate_exclusion(t, atom, Enum.t, Keyword.t) :: t 2129 def validate_exclusion(changeset, field, data, opts \\ []) do 2130 validate_change changeset, field, {:exclusion, data}, fn _, value -> 2131 type = Map.fetch!(changeset.types, field) 2132 2133 if Ecto.Type.include?(type, value, data), do: 2134 [{field, {message(opts, "is reserved"), [validation: :exclusion, enum: data]}}], else: [] 2135 end 2136 end 2137 2138 @doc """ 2139 Validates a change is a string or list of the given length. 2140 2141 Note that the length of a string is counted in graphemes by default. If using 2142 this validation to match a character limit of a database backend, 2143 it's likely that the limit ignores graphemes and limits the number 2144 of unicode characters. Then consider using the `:count` option to 2145 limit the number of codepoints (`:codepoints`), or limit the number of bytes (`:bytes`). 2146 2147 ## Options 2148 2149 * `:is` - the length must be exactly this value 2150 * `:min` - the length must be greater than or equal to this value 2151 * `:max` - the length must be less than or equal to this value 2152 * `:count` - what length to count for string, `:graphemes` (default), `:codepoints` or `:bytes` 2153 * `:message` - the message on failure, depending on the validation, is one of: 2154 * for strings: 2155 * "should be %{count} character(s)" 2156 * "should be at least %{count} character(s)" 2157 * "should be at most %{count} character(s)" 2158 * for binary: 2159 * "should be %{count} byte(s)" 2160 * "should be at least %{count} byte(s)" 2161 * "should be at most %{count} byte(s)" 2162 * for lists: 2163 * "should have %{count} item(s)" 2164 * "should have at least %{count} item(s)" 2165 * "should have at most %{count} item(s)" 2166 2167 ## Examples 2168 2169 validate_length(changeset, :title, min: 3) 2170 validate_length(changeset, :title, max: 100) 2171 validate_length(changeset, :title, min: 3, max: 100) 2172 validate_length(changeset, :code, is: 9) 2173 validate_length(changeset, :topics, is: 2) 2174 validate_length(changeset, :icon, count: :bytes, max: 1024 * 16) 2175 2176 """ 2177 @spec validate_length(t, atom, Keyword.t) :: t 2178 def validate_length(changeset, field, opts) when is_list(opts) do 2179 validate_change changeset, field, {:length, opts}, fn 2180 _, value -> 2181 count_type = opts[:count] || :graphemes 2182 {type, length} = case {value, count_type} do 2183 {value, :codepoints} when is_binary(value) -> 2184 {:string, codepoints_length(value, 0)} 2185 {value, :graphemes} when is_binary(value) -> 2186 {:string, String.length(value)} 2187 {value, :bytes} when is_binary(value) -> 2188 {:binary, byte_size(value)} 2189 {value, _} when is_list(value) -> 2190 {:list, list_length(changeset, field, value)} 2191 end 2192 2193 error = ((is = opts[:is]) && wrong_length(type, length, is, opts)) || 2194 ((min = opts[:min]) && too_short(type, length, min, opts)) || 2195 ((max = opts[:max]) && too_long(type, length, max, opts)) 2196 2197 if error, do: [{field, error}], else: [] 2198 end 2199 end 2200 2201 defp codepoints_length(<<_::utf8, rest::binary>>, acc), do: codepoints_length(rest, acc + 1) 2202 defp codepoints_length(<<_, rest::binary>>, acc), do: codepoints_length(rest, acc + 1) 2203 defp codepoints_length(<<>>, acc), do: acc 2204 2205 defp list_length(%{types: types}, field, value) do 2206 case Map.fetch(types, field) do 2207 {:ok, {tag, _association}} when tag in [:embed, :assoc] -> 2208 length(Relation.filter_empty(value)) 2209 _ -> 2210 length(value) 2211 end 2212 end 2213 2214 defp wrong_length(_type, value, value, _opts), do: nil 2215 defp wrong_length(:string, _length, value, opts), do: 2216 {message(opts, "should be %{count} character(s)"), count: value, validation: :length, kind: :is, type: :string} 2217 defp wrong_length(:binary, _length, value, opts), do: 2218 {message(opts, "should be %{count} byte(s)"), count: value, validation: :length, kind: :is, type: :binary} 2219 defp wrong_length(:list, _length, value, opts), do: 2220 {message(opts, "should have %{count} item(s)"), count: value, validation: :length, kind: :is, type: :list} 2221 2222 defp too_short(_type, length, value, _opts) when length >= value, do: nil 2223 defp too_short(:string, _length, value, opts), do: 2224 {message(opts, "should be at least %{count} character(s)"), count: value, validation: :length, kind: :min, type: :string} 2225 defp too_short(:binary, _length, value, opts), do: 2226 {message(opts, "should be at least %{count} byte(s)"), count: value, validation: :length, kind: :min, type: :binary} 2227 defp too_short(:list, _length, value, opts), do: 2228 {message(opts, "should have at least %{count} item(s)"), count: value, validation: :length, kind: :min, type: :list} 2229 2230 defp too_long(_type, length, value, _opts) when length <= value, do: nil 2231 defp too_long(:string, _length, value, opts), do: 2232 {message(opts, "should be at most %{count} character(s)"), count: value, validation: :length, kind: :max, type: :string} 2233 defp too_long(:binary, _length, value, opts), do: 2234 {message(opts, "should be at most %{count} byte(s)"), count: value, validation: :length, kind: :max, type: :binary} 2235 defp too_long(:list, _length, value, opts), do: 2236 {message(opts, "should have at most %{count} item(s)"), count: value, validation: :length, kind: :max, type: :list} 2237 2238 @doc """ 2239 Validates the properties of a number. 2240 2241 ## Options 2242 2243 * `:less_than` 2244 * `:greater_than` 2245 * `:less_than_or_equal_to` 2246 * `:greater_than_or_equal_to` 2247 * `:equal_to` 2248 * `:not_equal_to` 2249 * `:message` - the message on failure, defaults to one of: 2250 * "must be less than %{number}" 2251 * "must be greater than %{number}" 2252 * "must be less than or equal to %{number}" 2253 * "must be greater than or equal to %{number}" 2254 * "must be equal to %{number}" 2255 * "must be not equal to %{number}" 2256 2257 ## Examples 2258 2259 validate_number(changeset, :count, less_than: 3) 2260 validate_number(changeset, :pi, greater_than: 3, less_than: 4) 2261 validate_number(changeset, :the_answer_to_life_the_universe_and_everything, equal_to: 42) 2262 2263 """ 2264 @spec validate_number(t, atom, Keyword.t) :: t 2265 def validate_number(changeset, field, opts) do 2266 validate_change changeset, field, {:number, opts}, fn 2267 field, value -> 2268 {message, opts} = Keyword.pop(opts, :message) 2269 2270 unless valid_number?(value) do 2271 raise ArgumentError, "expected field `#{field}` to be a decimal, integer, or float, got: #{inspect(value)}" 2272 end 2273 2274 Enum.find_value opts, [], fn {spec_key, target_value} -> 2275 case Map.fetch(@number_validators, spec_key) do 2276 {:ok, {spec_function, default_message}} -> 2277 unless valid_number?(target_value) do 2278 raise ArgumentError, "expected option `#{spec_key}` to be a decimal, integer, or float, got: #{inspect(target_value)}" 2279 end 2280 2281 compare_numbers(field, value, message || default_message, 2282 spec_key, spec_function, target_value) 2283 2284 :error -> 2285 supported_options = @number_validators |> Map.keys() |> Enum.map_join("\n", &" * #{inspect(&1)}") 2286 2287 raise ArgumentError, """ 2288 unknown option #{inspect spec_key} given to validate_number/3 2289 2290 The supported options are: 2291 2292 #{supported_options} 2293 """ 2294 end 2295 end 2296 end 2297 end 2298 2299 defp valid_number?(%Decimal{}), do: true 2300 defp valid_number?(other), do: is_number(other) 2301 2302 defp compare_numbers(field, %Decimal{} = value, message, spec_key, _spec_function, %Decimal{} = target_value) do 2303 result = Decimal.compare(value, target_value) |> normalize_compare() 2304 case decimal_compare(result, spec_key) do 2305 true -> nil 2306 false -> [{field, {message, validation: :number, kind: spec_key, number: target_value}}] 2307 end 2308 end 2309 2310 defp compare_numbers(field, value, message, spec_key, spec_function, %Decimal{} = target_value) do 2311 compare_numbers(field, decimal_new(value), message, spec_key, spec_function, target_value) 2312 end 2313 2314 defp compare_numbers(field, %Decimal{} = value, message, spec_key, spec_function, target_value) do 2315 compare_numbers(field, value, message, spec_key, spec_function, decimal_new(target_value)) 2316 end 2317 2318 defp compare_numbers(field, value, message, spec_key, spec_function, target_value) do 2319 case apply(spec_function, [value, target_value]) do 2320 true -> nil 2321 false -> [{field, {message, validation: :number, kind: spec_key, number: target_value}}] 2322 end 2323 end 2324 2325 # TODO: Remove me once we support Decimal 2.0 only 2326 # Support mismatch between API for Decimal.compare/2 for versions 1.6 and 2.0 2327 defp normalize_compare(result) do 2328 case result do 2329 %Decimal{coef: 1, sign: -1} -> :lt 2330 %Decimal{coef: 0} -> :eq 2331 %Decimal{coef: 1, sign: 1} -> :gt 2332 _ -> result 2333 end 2334 end 2335 2336 defp decimal_new(term) when is_float(term), do: Decimal.from_float(term) 2337 defp decimal_new(term), do: Decimal.new(term) 2338 2339 defp decimal_compare(:lt, spec), do: spec in [:less_than, :less_than_or_equal_to, :not_equal_to] 2340 defp decimal_compare(:gt, spec), do: spec in [:greater_than, :greater_than_or_equal_to, :not_equal_to] 2341 defp decimal_compare(:eq, spec), do: spec in [:equal_to, :less_than_or_equal_to, :greater_than_or_equal_to] 2342 2343 @doc """ 2344 Validates that the given parameter matches its confirmation. 2345 2346 By calling `validate_confirmation(changeset, :email)`, this 2347 validation will check if both "email" and "email_confirmation" 2348 in the parameter map matches. Note this validation only looks 2349 at the parameters themselves, never the fields in the schema. 2350 As such as, the "email_confirmation" field does not need to be 2351 added as a virtual field in your schema. 2352 2353 Note that if the confirmation field is nil or missing, this does 2354 not add a validation error. You can specify that the confirmation 2355 parameter is required in the options (see below). 2356 2357 ## Options 2358 2359 * `:message` - the message on failure, defaults to "does not match confirmation" 2360 * `:required` - boolean, sets whether existence of confirmation parameter 2361 is required for addition of error. Defaults to false 2362 2363 ## Examples 2364 2365 validate_confirmation(changeset, :email) 2366 validate_confirmation(changeset, :password, message: "does not match password") 2367 2368 cast(data, params, [:password]) 2369 |> validate_confirmation(:password, message: "does not match password") 2370 2371 """ 2372 @spec validate_confirmation(t, atom, Keyword.t) :: t 2373 def validate_confirmation(changeset, field, opts \\ []) 2374 def validate_confirmation(%{params: params} = changeset, field, opts) when is_map(params) do 2375 param = Atom.to_string(field) 2376 error_param = "#{param}_confirmation" 2377 error_field = String.to_atom(error_param) 2378 value = Map.get(params, param) 2379 2380 errors = 2381 case Map.fetch(params, error_param) do 2382 {:ok, ^value} -> 2383 [] 2384 {:ok, _} -> 2385 [{error_field, 2386 {message(opts, "does not match confirmation"), [validation: :confirmation]}}] 2387 :error -> 2388 confirmation_missing(opts, error_field) 2389 end 2390 2391 %{changeset | validations: [{field, {:confirmation, opts}} | changeset.validations], 2392 errors: errors ++ changeset.errors, 2393 valid?: changeset.valid? and errors == []} 2394 end 2395 def validate_confirmation(%{params: nil} = changeset, _, _) do 2396 changeset 2397 end 2398 2399 defp confirmation_missing(opts, error_field) do 2400 required = Keyword.get(opts, :required, false) 2401 if required, do: [{error_field, {message(opts, "can't be blank"), [validation: :required]}}], else: [] 2402 end 2403 2404 defp message(opts, key \\ :message, default) do 2405 Keyword.get(opts, key, default) 2406 end 2407 2408 @doc """ 2409 Validates the given parameter is true. 2410 2411 Note this validation only checks the parameter itself is true, never 2412 the field in the schema. That's because acceptance parameters do not need 2413 to be persisted, as by definition they would always be stored as `true`. 2414 2415 ## Options 2416 2417 * `:message` - the message on failure, defaults to "must be accepted" 2418 2419 ## Examples 2420 2421 validate_acceptance(changeset, :terms_of_service) 2422 validate_acceptance(changeset, :rules, message: "please accept rules") 2423 2424 """ 2425 @spec validate_acceptance(t, atom, Keyword.t) :: t 2426 def validate_acceptance(changeset, field, opts \\ []) 2427 def validate_acceptance(%{params: params} = changeset, field, opts) do 2428 errors = validate_acceptance_errors(params, field, opts) 2429 2430 %{changeset | validations: [{field, {:acceptance, opts}} | changeset.validations], 2431 errors: errors ++ changeset.errors, 2432 valid?: changeset.valid? and errors == []} 2433 end 2434 2435 defp validate_acceptance_errors(nil, _field, _opts), do: [] 2436 2437 defp validate_acceptance_errors(params, field, opts) do 2438 param = Atom.to_string(field) 2439 value = Map.get(params, param) 2440 2441 case Ecto.Type.cast(:boolean, value) do 2442 {:ok, true} -> [] 2443 _ -> [{field, {message(opts, "must be accepted"), validation: :acceptance}}] 2444 end 2445 end 2446 2447 ## Optimistic lock 2448 2449 @doc ~S""" 2450 Applies optimistic locking to the changeset. 2451 2452 [Optimistic 2453 locking](https://en.wikipedia.org/wiki/Optimistic_concurrency_control) (or 2454 *optimistic concurrency control*) is a technique that allows concurrent edits 2455 on a single record. While pessimistic locking works by locking a resource for 2456 an entire transaction, optimistic locking only checks if the resource changed 2457 before updating it. 2458 2459 This is done by regularly fetching the record from the database, then checking 2460 whether another user has made changes to the record *only when updating the 2461 record*. This behaviour is ideal in situations where the chances of concurrent 2462 updates to the same record are low; if they're not, pessimistic locking or 2463 other concurrency patterns may be more suited. 2464 2465 ## Usage 2466 2467 Optimistic locking works by keeping a "version" counter for each record; this 2468 counter gets incremented each time a modification is made to a record. Hence, 2469 in order to use optimistic locking, a field must exist in your schema for 2470 versioning purpose. Such field is usually an integer but other types are 2471 supported. 2472 2473 ## Examples 2474 2475 Assuming we have a `Post` schema (stored in the `posts` table), the first step 2476 is to add a version column to the `posts` table: 2477 2478 alter table(:posts) do 2479 add :lock_version, :integer, default: 1 2480 end 2481 2482 The column name is arbitrary and doesn't need to be `:lock_version`. Now add 2483 a field to the schema too: 2484 2485 defmodule Post do 2486 use Ecto.Schema 2487 2488 schema "posts" do 2489 field :title, :string 2490 field :lock_version, :integer, default: 1 2491 end 2492 2493 def changeset(:update, struct, params \\ %{}) do 2494 struct 2495 |> Ecto.Changeset.cast(params, [:title]) 2496 |> Ecto.Changeset.optimistic_lock(:lock_version) 2497 end 2498 end 2499 2500 Now let's take optimistic locking for a spin: 2501 2502 iex> post = Repo.insert!(%Post{title: "foo"}) 2503 %Post{id: 1, title: "foo", lock_version: 1} 2504 iex> valid_change = Post.changeset(:update, post, %{title: "bar"}) 2505 iex> stale_change = Post.changeset(:update, post, %{title: "baz"}) 2506 iex> Repo.update!(valid_change) 2507 %Post{id: 1, title: "bar", lock_version: 2} 2508 iex> Repo.update!(stale_change) 2509 ** (Ecto.StaleEntryError) attempted to update a stale entry: 2510 2511 %Post{id: 1, title: "baz", lock_version: 1} 2512 2513 When a conflict happens (a record which has been previously fetched is 2514 being updated, but that same record has been modified since it was 2515 fetched), an `Ecto.StaleEntryError` exception is raised. 2516 2517 Optimistic locking also works with delete operations. Just call the 2518 `optimistic_lock/3` function with the data before delete: 2519 2520 iex> changeset = Ecto.Changeset.optimistic_lock(post, :lock_version) 2521 iex> Repo.delete(changeset) 2522 2523 `optimistic_lock/3` by default assumes the field 2524 being used as a lock is an integer. If you want to use another type, 2525 you need to pass the third argument customizing how the next value 2526 is generated: 2527 2528 iex> Ecto.Changeset.optimistic_lock(post, :lock_uuid, fn _ -> Ecto.UUID.generate end) 2529 2530 """ 2531 @spec optimistic_lock(Ecto.Schema.t | t, atom, (term -> term)) :: t 2532 def optimistic_lock(data_or_changeset, field, incrementer \\ &increment_with_rollover/1) do 2533 changeset = change(data_or_changeset, %{}) 2534 current = get_field(changeset, field) 2535 2536 # Apply these changes only inside the repo because we 2537 # don't want to permanently track the lock change. 2538 changeset = prepare_changes(changeset, fn changeset -> 2539 put_in(changeset.changes[field], incrementer.(current)) 2540 end) 2541 2542 if is_nil(current) do 2543 Logger.warn """ 2544 the current value of `#{field}` is `nil` and will not be used as a filter for optimistic 2545 locking. To ensure `#{field}` is never `nil`, consider setting a default value. 2546 """ 2547 changeset 2548 else 2549 put_in(changeset.filters[field], current) 2550 end 2551 end 2552 2553 # increment_with_rollover expect to be used with lock_version set as :integer in db schema 2554 # 2_147_483_647 is upper limit for signed integer for both PostgreSQL and MySQL 2555 defp increment_with_rollover(val) when val >= 2_147_483_647 do 2556 1 2557 end 2558 2559 defp increment_with_rollover(val) when is_integer(val) do 2560 val + 1 2561 end 2562 2563 @doc """ 2564 Provides a function executed by the repository on insert/update/delete. 2565 2566 If the changeset given to the repository is valid, the function given to 2567 `prepare_changes/2` will be called with the changeset and must return a 2568 changeset, allowing developers to do final adjustments to the changeset or 2569 to issue data consistency commands. The repository itself can be accessed 2570 inside the function under the `repo` field in the changeset. If the 2571 changeset given to the repository is invalid, the function will not be 2572 invoked. 2573 2574 The given function is guaranteed to run inside the same transaction 2575 as the changeset operation for databases that do support transactions. 2576 2577 ## Example 2578 2579 A common use case is updating a counter cache, in this case updating a post's 2580 comment count when a comment is created: 2581 2582 def create_comment(comment, params) do 2583 comment 2584 |> cast(params, [:body, :post_id]) 2585 |> prepare_changes(fn changeset -> 2586 if post_id = get_change(changeset, :post_id) do 2587 query = from Post, where: [id: ^post_id] 2588 changeset.repo.update_all(query, inc: [comment_count: 1]) 2589 end 2590 changeset 2591 end) 2592 end 2593 2594 We retrieve the repo from the comment changeset itself and use 2595 update_all to update the counter cache in one query. Finally, the original 2596 changeset must be returned. 2597 """ 2598 @spec prepare_changes(t, (t -> t)) :: t 2599 def prepare_changes(%Changeset{prepare: prepare} = changeset, function) when is_function(function, 1) do 2600 %{changeset | prepare: [function | prepare]} 2601 end 2602 2603 ## Constraints 2604 2605 @doc """ 2606 Returns all constraints in a changeset. 2607 2608 A constraint is a map with the following fields: 2609 2610 * `:type` - the type of the constraint that will be checked in the database, 2611 such as `:check`, `:unique`, etc 2612 * `:constraint` - the database constraint name as a string 2613 * `:match` - the type of match Ecto will perform on a violated constraint 2614 against the `:constraint` value. It is `:exact`, `:suffix` or `:prefix` 2615 * `:field` - the field a violated constraint will apply the error to 2616 * `:error_message` - the error message in case of violated constraints 2617 * `:error_type` - the type of error that identifies the error message 2618 2619 """ 2620 @spec constraints(t) :: [constraint] 2621 def constraints(%Changeset{constraints: constraints}) do 2622 constraints 2623 end 2624 2625 @doc """ 2626 Checks for a check constraint in the given field. 2627 2628 The check constraint works by relying on the database to check 2629 if the check constraint has been violated or not and, if so, 2630 Ecto converts it into a changeset error. 2631 2632 In order to use the check constraint, the first step is 2633 to define the check constraint in a migration: 2634 2635 create constraint("users", :age_must_be_positive, check: "age > 0") 2636 2637 Now that a constraint exists, when modifying users, we could 2638 annotate the changeset with a check constraint so Ecto knows 2639 how to convert it into an error message: 2640 2641 cast(user, params, [:age]) 2642 |> check_constraint(:age, name: :age_must_be_positive) 2643 2644 Now, when invoking `c:Ecto.Repo.insert/2` or `c:Ecto.Repo.update/2`, 2645 if the age is not positive, the underlying operation will fail 2646 but Ecto will convert the database exception into a changeset error 2647 and return an `{:error, changeset}` tuple. Note that the error will 2648 occur only after hitting the database, so it will not be visible 2649 until all other validations pass. If the constraint fails inside a 2650 transaction, the transaction will be marked as aborted. 2651 2652 ## Options 2653 2654 * `:message` - the message in case the constraint check fails. 2655 Defaults to "is invalid" 2656 * `:name` - the name of the constraint. Required. 2657 * `:match` - how the changeset constraint name is matched against the 2658 repo constraint, may be `:exact`, `:suffix` or `:prefix`. Defaults to 2659 `:exact`. `:suffix` matches any repo constraint which `ends_with?` `:name` 2660 to this changeset constraint. `:prefix` matches any repo constraint which 2661 `starts_with?` `:name` to this changeset constraint. 2662 2663 """ 2664 def check_constraint(changeset, field, opts \\ []) do 2665 constraint = opts[:name] || raise ArgumentError, "must supply the name of the constraint" 2666 message = message(opts, "is invalid") 2667 match_type = Keyword.get(opts, :match, :exact) 2668 add_constraint(changeset, :check, to_string(constraint), match_type, field, message) 2669 end 2670 2671 @doc """ 2672 Checks for a unique constraint in the given field or list of fields. 2673 2674 The unique constraint works by relying on the database to check 2675 if the unique constraint has been violated or not and, if so, 2676 Ecto converts it into a changeset error. 2677 2678 In order to use the uniqueness constraint, the first step is 2679 to define the unique index in a migration: 2680 2681 create unique_index(:users, [:email]) 2682 2683 Now that a constraint exists, when modifying users, we could 2684 annotate the changeset with a unique constraint so Ecto knows 2685 how to convert it into an error message: 2686 2687 cast(user, params, [:email]) 2688 |> unique_constraint(:email) 2689 2690 Now, when invoking `c:Ecto.Repo.insert/2` or `c:Ecto.Repo.update/2`, 2691 if the email already exists, the underlying operation will fail but 2692 Ecto will convert the database exception into a changeset error and 2693 return an `{:error, changeset}` tuple. Note that the error will occur 2694 only after hitting the database, so it will not be visible until all 2695 other validations pass. If the constraint fails inside a transaction, 2696 the transaction will be marked as aborted. 2697 2698 ## Options 2699 2700 * `:message` - the message in case the constraint check fails, 2701 defaults to "has already been taken" 2702 2703 * `:name` - the constraint name. By default, the constraint 2704 name is inferred from the table + field(s). May be required 2705 explicitly for complex cases 2706 2707 * `:match` - how the changeset constraint name is matched against the 2708 repo constraint, may be `:exact`, `:suffix` or `:prefix`. Defaults to 2709 `:exact`. `:suffix` matches any repo constraint which `ends_with?` `:name` 2710 to this changeset constraint. `:prefix` matches any repo constraint which 2711 `starts_with?` `:name` to this changeset constraint. 2712 2713 * `:error_key` - the key to which changeset error will be added when 2714 check fails, defaults to the first field name of the given list of 2715 fields. 2716 2717 ## Complex constraints 2718 2719 Because the constraint logic is in the database, we can leverage 2720 all the database functionality when defining them. For example, 2721 let's suppose the e-mails are scoped by company id: 2722 2723 # In migration 2724 create unique_index(:users, [:email, :company_id]) 2725 2726 # In the changeset function 2727 cast(user, params, [:email]) 2728 |> unique_constraint([:email, :company_id]) 2729 2730 The first field name, `:email` in this case, will be used as the error 2731 key to the changeset errors keyword list. For example, the above 2732 `unique_constraint/3` would generate something like: 2733 2734 Repo.insert!(%User{email: "john@elixir.org", company_id: 1}) 2735 changeset = User.changeset(%User{}, %{email: "john@elixir.org", company_id: 1}) 2736 {:error, changeset} = Repo.insert(changeset) 2737 changeset.errors #=> [email: {"has already been taken", []}] 2738 2739 In complex cases, instead of relying on name inference, it may be best 2740 to set the constraint name explicitly: 2741 2742 # In the migration 2743 create unique_index(:users, [:email, :company_id], name: :users_email_company_id_index) 2744 2745 # In the changeset function 2746 cast(user, params, [:email]) 2747 |> unique_constraint(:email, name: :users_email_company_id_index) 2748 2749 ### Partitioning 2750 2751 If your table is partitioned, then your unique index might look different 2752 per partition, e.g. Postgres adds p<number> to the middle of your key, like: 2753 2754 users_p0_email_key 2755 users_p1_email_key 2756 ... 2757 users_p99_email_key 2758 2759 In this case you can use the name and suffix options together to match on 2760 these dynamic indexes, like: 2761 2762 cast(user, params, [:email]) 2763 |> unique_constraint(:email, name: :email_key, match: :suffix) 2764 2765 ## Case sensitivity 2766 2767 Unfortunately, different databases provide different guarantees 2768 when it comes to case-sensitiveness. For example, in MySQL, comparisons 2769 are case-insensitive by default. In Postgres, users can define case 2770 insensitive column by using the `:citext` type/extension. In your migration: 2771 2772 execute "CREATE EXTENSION IF NOT EXISTS citext" 2773 create table(:users) do 2774 ... 2775 add :email, :citext 2776 ... 2777 end 2778 2779 If for some reason your database does not support case insensitive columns, 2780 you can explicitly downcase values before inserting/updating them: 2781 2782 cast(data, params, [:email]) 2783 |> update_change(:email, &String.downcase/1) 2784 |> unique_constraint(:email) 2785 2786 """ 2787 @spec unique_constraint(t, atom | [atom, ...], Keyword.t) :: t 2788 def unique_constraint(changeset, field_or_fields, opts \\ []) 2789 2790 def unique_constraint(changeset, field, opts) when is_atom(field) do 2791 unique_constraint(changeset, [field], opts) 2792 end 2793 2794 def unique_constraint(changeset, [first_field | _] = fields, opts) do 2795 constraint = opts[:name] || unique_index_name(changeset, fields) 2796 message = message(opts, "has already been taken") 2797 match_type = Keyword.get(opts, :match, :exact) 2798 error_key = Keyword.get(opts, :error_key, first_field) 2799 add_constraint(changeset, :unique, to_string(constraint), match_type, error_key, message) 2800 end 2801 2802 defp unique_index_name(changeset, fields) do 2803 field_names = Enum.map(fields, &get_field_source(changeset, &1)) 2804 Enum.join([get_source(changeset)] ++ field_names ++ ["index"], "_") 2805 end 2806 2807 @doc """ 2808 Checks for foreign key constraint in the given field. 2809 2810 The foreign key constraint works by relying on the database to 2811 check if the associated data exists or not. This is useful to 2812 guarantee that a child will only be created if the parent exists 2813 in the database too. 2814 2815 In order to use the foreign key constraint the first step is 2816 to define the foreign key in a migration. This is often done 2817 with references. For example, imagine you are creating a 2818 comments table that belongs to posts. One would have: 2819 2820 create table(:comments) do 2821 add :post_id, references(:posts) 2822 end 2823 2824 By default, Ecto will generate a foreign key constraint with 2825 name "comments_post_id_fkey" (the name is configurable). 2826 2827 Now that a constraint exists, when creating comments, we could 2828 annotate the changeset with foreign key constraint so Ecto knows 2829 how to convert it into an error message: 2830 2831 cast(comment, params, [:post_id]) 2832 |> foreign_key_constraint(:post_id) 2833 2834 Now, when invoking `c:Ecto.Repo.insert/2` or `c:Ecto.Repo.update/2`, 2835 if the associated post does not exist, the underlying operation will 2836 fail but Ecto will convert the database exception into a changeset 2837 error and return an `{:error, changeset}` tuple. Note that the error 2838 will occur only after hitting the database, so it will not be visible 2839 until all other validations pass. If the constraint fails inside a 2840 transaction, the transaction will be marked as aborted. 2841 2842 ## Options 2843 2844 * `:message` - the message in case the constraint check fails, 2845 defaults to "does not exist" 2846 * `:name` - the constraint name. By default, the constraint 2847 name is inferred from the table + field. May be required 2848 explicitly for complex cases 2849 * `:match` - how the changeset constraint name is matched against the 2850 repo constraint, may be `:exact`, `:suffix` or `:prefix`. Defaults to 2851 `:exact`. `:suffix` matches any repo constraint which `ends_with?` `:name` 2852 to this changeset constraint. `:prefix` matches any repo constraint which 2853 `starts_with?` `:name` to this changeset constraint. 2854 2855 """ 2856 @spec foreign_key_constraint(t, atom, Keyword.t) :: t 2857 def foreign_key_constraint(changeset, field, opts \\ []) do 2858 constraint = opts[:name] || "#{get_source(changeset)}_#{get_field_source(changeset, field)}_fkey" 2859 match_type = Keyword.get(opts, :match, :exact) 2860 message = message(opts, "does not exist") 2861 add_constraint(changeset, :foreign_key, to_string(constraint), match_type, field, message, :foreign) 2862 end 2863 2864 @doc """ 2865 Checks the associated field exists. 2866 2867 This is similar to `foreign_key_constraint/3` except that the 2868 field is inferred from the association definition. This is useful 2869 to guarantee that a child will only be created if the parent exists 2870 in the database too. Therefore, it only applies to `belongs_to` 2871 associations. 2872 2873 As the name says, a constraint is required in the database for 2874 this function to work. Such constraint is often added as a 2875 reference to the child table: 2876 2877 create table(:comments) do 2878 add :post_id, references(:posts) 2879 end 2880 2881 Now, when inserting a comment, it is possible to forbid any 2882 comment to be added if the associated post does not exist: 2883 2884 comment 2885 |> Ecto.Changeset.cast(params, [:post_id]) 2886 |> Ecto.Changeset.assoc_constraint(:post) 2887 |> Repo.insert 2888 2889 ## Options 2890 2891 * `:message` - the message in case the constraint check fails, 2892 defaults to "does not exist" 2893 * `:name` - the constraint name. By default, the constraint 2894 name is inferred from the table + association field. 2895 May be required explicitly for complex cases 2896 * `:match` - how the changeset constraint name is matched against the 2897 repo constraint, may be `:exact`, `:suffix` or `:prefix`. Defaults to 2898 `:exact`. `:suffix` matches any repo constraint which `ends_with?` `:name` 2899 to this changeset constraint. `:prefix` matches any repo constraint which 2900 `starts_with?` `:name` to this changeset constraint. 2901 """ 2902 @spec assoc_constraint(t, atom, Keyword.t) :: t 2903 def assoc_constraint(changeset, assoc, opts \\ []) do 2904 constraint = opts[:name] || 2905 case get_assoc(changeset, assoc) do 2906 %Ecto.Association.BelongsTo{owner_key: owner_key} -> 2907 "#{get_source(changeset)}_#{owner_key}_fkey" 2908 other -> 2909 raise ArgumentError, 2910 "assoc_constraint can only be added to belongs to associations, got: #{inspect other}" 2911 end 2912 2913 match_type = Keyword.get(opts, :match, :exact) 2914 message = message(opts, "does not exist") 2915 add_constraint(changeset, :foreign_key, to_string(constraint), match_type, assoc, message, :assoc) 2916 end 2917 2918 @doc """ 2919 Checks the associated field does not exist. 2920 2921 This is similar to `foreign_key_constraint/3` except that the 2922 field is inferred from the association definition. This is useful 2923 to guarantee that parent can only be deleted (or have its primary 2924 key changed) if no child exists in the database. Therefore, it only 2925 applies to `has_*` associations. 2926 2927 As the name says, a constraint is required in the database for 2928 this function to work. Such constraint is often added as a 2929 reference to the child table: 2930 2931 create table(:comments) do 2932 add :post_id, references(:posts) 2933 end 2934 2935 Now, when deleting the post, it is possible to forbid any post to 2936 be deleted if they still have comments attached to it: 2937 2938 post 2939 |> Ecto.Changeset.change 2940 |> Ecto.Changeset.no_assoc_constraint(:comments) 2941 |> Repo.delete 2942 2943 ## Options 2944 2945 * `:message` - the message in case the constraint check fails, 2946 defaults to "is still associated with this entry" (for `has_one`) 2947 and "are still associated with this entry" (for `has_many`) 2948 * `:name` - the constraint name. By default, the constraint 2949 name is inferred from the association table + association 2950 field. May be required explicitly for complex cases 2951 * `:match` - how the changeset constraint name is matched against the 2952 repo constraint, may be `:exact`, `:suffix` or `:prefix`. Defaults to 2953 `:exact`. `:suffix` matches any repo constraint which `ends_with?` `:name` 2954 to this changeset constraint. `:prefix` matches any repo constraint which 2955 `starts_with?` `:name` to this changeset constraint. 2956 2957 """ 2958 @spec no_assoc_constraint(t, atom, Keyword.t) :: t 2959 def no_assoc_constraint(changeset, assoc, opts \\ []) do 2960 {constraint, message} = 2961 case get_assoc(changeset, assoc) do 2962 %Ecto.Association.Has{cardinality: cardinality, 2963 related_key: related_key, related: related} -> 2964 {opts[:name] || "#{related.__schema__(:source)}_#{related_key}_fkey", 2965 message(opts, no_assoc_message(cardinality))} 2966 other -> 2967 raise ArgumentError, 2968 "no_assoc_constraint can only be added to has one/many associations, got: #{inspect other}" 2969 end 2970 2971 match_type = Keyword.get(opts, :match, :exact) 2972 add_constraint(changeset, :foreign_key, to_string(constraint), match_type, assoc, message, :no_assoc) 2973 end 2974 2975 @doc """ 2976 Checks for an exclusion constraint in the given field. 2977 2978 The exclusion constraint works by relying on the database to check 2979 if the exclusion constraint has been violated or not and, if so, 2980 Ecto converts it into a changeset error. 2981 2982 ## Options 2983 2984 * `:message` - the message in case the constraint check fails, 2985 defaults to "violates an exclusion constraint" 2986 * `:name` - the constraint name. By default, the constraint 2987 name is inferred from the table + field. May be required 2988 explicitly for complex cases 2989 * `:match` - how the changeset constraint name is matched against the 2990 repo constraint, may be `:exact`, `:suffix` or `:prefix`. Defaults to 2991 `:exact`. `:suffix` matches any repo constraint which `ends_with?` `:name` 2992 to this changeset constraint. `:prefix` matches any repo constraint which 2993 `starts_with?` `:name` to this changeset constraint. 2994 2995 """ 2996 def exclusion_constraint(changeset, field, opts \\ []) do 2997 constraint = opts[:name] || "#{get_source(changeset)}_#{get_field_source(changeset, field)}_exclusion" 2998 message = message(opts, "violates an exclusion constraint") 2999 match_type = Keyword.get(opts, :match, :exact) 3000 add_constraint(changeset, :exclusion, to_string(constraint), match_type, field, message, :exclusion) 3001 end 3002 3003 defp no_assoc_message(:one), do: "is still associated with this entry" 3004 defp no_assoc_message(:many), do: "are still associated with this entry" 3005 3006 defp add_constraint(changeset, type, constraint, match, field, message) do 3007 add_constraint(changeset, type, constraint, match, field, message, type) 3008 end 3009 3010 defp add_constraint(%Changeset{constraints: constraints} = changeset, 3011 type, constraint, match, field, error_message, error_type) 3012 when is_binary(constraint) and is_atom(field) and is_binary(error_message) do 3013 unless match in @match_types do 3014 raise ArgumentError, "invalid match type: #{inspect match}. Allowed match types: #{inspect @match_types}" 3015 end 3016 3017 constraint = %{ 3018 constraint: constraint, 3019 error_message: error_message, 3020 error_type: error_type, 3021 field: field, 3022 match: match, 3023 type: type 3024 } 3025 3026 %{changeset | constraints: [constraint | constraints]} 3027 end 3028 3029 defp get_source(%{data: %{__meta__: %{source: source}}}) when is_binary(source), 3030 do: source 3031 3032 defp get_source(%{data: data}) do 3033 raise ArgumentError, "cannot add constraint to changeset because it does not have a source, got: #{inspect data}" 3034 end 3035 3036 defp get_source(item) do 3037 raise ArgumentError, "cannot add constraint because a changeset was not supplied, got: #{inspect item}" 3038 end 3039 3040 defp get_assoc(%{types: types}, assoc) do 3041 case Map.fetch(types, assoc) do 3042 {:ok, {:assoc, association}} -> 3043 association 3044 _ -> 3045 raise_invalid_assoc(types, assoc) 3046 end 3047 end 3048 3049 defp raise_invalid_assoc(types, assoc) do 3050 associations = for {_key, {:assoc, %{field: field}}} <- types, do: field 3051 one_of = if match?([_], associations), do: "", else: "one of " 3052 3053 raise ArgumentError, 3054 "cannot add constraint to changeset because association `#{assoc}` does not exist. " <> 3055 "Did you mean #{one_of}`#{Enum.join(associations, "`, `")}`?" 3056 end 3057 3058 defp get_field_source(%{data: %{__struct__: schema}}, field) when is_atom(schema), 3059 do: schema.__schema__(:field_source, field) || field 3060 defp get_field_source(%{}, field), 3061 do: field 3062 3063 @doc ~S""" 3064 Traverses changeset errors and applies the given function to error messages. 3065 3066 This function is particularly useful when associations and embeds 3067 are cast in the changeset as it will traverse all associations and 3068 embeds and place all errors in a series of nested maps. 3069 3070 A changeset is supplied along with a function to apply to each 3071 error message as the changeset is traversed. The error message 3072 function receives an error tuple `{msg, opts}`, for example: 3073 3074 {"should be at least %{count} characters", [count: 3, validation: :length, min: 3]} 3075 3076 ## Examples 3077 3078 iex> traverse_errors(changeset, fn {msg, opts} -> 3079 ...> Regex.replace(~r"%{(\w+)}", msg, fn _, key -> 3080 ...> opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string() 3081 ...> end) 3082 ...> end) 3083 %{title: ["should be at least 3 characters"]} 3084 3085 Optionally function can accept three arguments: `changeset`, `field` 3086 and error tuple `{msg, opts}`. It is useful whenever you want to extract 3087 validations rules from `changeset.validations` to build detailed error 3088 description. 3089 """ 3090 @spec traverse_errors(t, (error -> String.t) | (Changeset.t, atom, error -> String.t)) :: %{atom => [term]} 3091 def traverse_errors(%Changeset{errors: errors, changes: changes, types: types} = changeset, msg_func) 3092 when is_function(msg_func, 1) or is_function(msg_func, 3) do 3093 errors 3094 |> Enum.reverse() 3095 |> merge_keyword_keys(msg_func, changeset) 3096 |> merge_related_keys(changes, types, msg_func, &traverse_errors/2) 3097 end 3098 3099 defp merge_keyword_keys(keyword_list, msg_func, _) when is_function(msg_func, 1) do 3100 Enum.reduce(keyword_list, %{}, fn({key, val}, acc) -> 3101 val = msg_func.(val) 3102 Map.update(acc, key, [val], &[val|&1]) 3103 end) 3104 end 3105 3106 defp merge_keyword_keys(keyword_list, msg_func, changeset) when is_function(msg_func, 3) do 3107 Enum.reduce(keyword_list, %{}, fn({key, val}, acc) -> 3108 val = msg_func.(changeset, key, val) 3109 Map.update(acc, key, [val], &[val|&1]) 3110 end) 3111 end 3112 3113 defp merge_related_keys(_, _, nil, _, _) do 3114 raise ArgumentError, "changeset does not have types information" 3115 end 3116 3117 defp merge_related_keys(map, changes, types, msg_func, traverse_function) do 3118 Enum.reduce types, map, fn 3119 {field, {tag, %{cardinality: :many}}}, acc when tag in @relations -> 3120 if changesets = Map.get(changes, field) do 3121 {child, all_empty?} = 3122 Enum.map_reduce(changesets, true, fn changeset, all_empty? -> 3123 child = traverse_function.(changeset, msg_func) 3124 {child, all_empty? and child == %{}} 3125 end) 3126 3127 case all_empty? do 3128 true -> acc 3129 false -> Map.put(acc, field, child) 3130 end 3131 else 3132 acc 3133 end 3134 {field, {tag, %{cardinality: :one}}}, acc when tag in @relations -> 3135 if changeset = Map.get(changes, field) do 3136 case traverse_function.(changeset, msg_func) do 3137 child when child == %{} -> acc 3138 child -> Map.put(acc, field, child) 3139 end 3140 else 3141 acc 3142 end 3143 {_, _}, acc -> 3144 acc 3145 end 3146 end 3147 3148 defp apply_relation_changes(acc, key, relation, value) do 3149 relation_changed = Relation.apply_changes(relation, value) 3150 3151 acc = Map.put(acc, key, relation_changed) 3152 3153 with %Ecto.Association.BelongsTo{related_key: related_key} <- relation, 3154 %{^related_key => id} <- relation_changed do 3155 Map.put(acc, relation.owner_key, id) 3156 else 3157 _ -> acc 3158 end 3159 end 3160 3161 @doc ~S""" 3162 Traverses changeset validations and applies the given function to validations. 3163 3164 This behaves the same as `traverse_errors/2`, but operates on changeset 3165 validations instead of errors. 3166 3167 ## Examples 3168 3169 iex> traverse_validations(changeset, &(&1)) 3170 %{title: [format: ~r/pattern/, length: [min: 1, max: 20]]} 3171 3172 iex> traverse_validations(changeset, fn 3173 ...> {:length, opts} -> {:length, "#{Keyword.get(opts, :min, 0)}-#{Keyword.get(opts, :max, 32)}"} 3174 ...> {:format, %Regex{source: source}} -> {:format, "/#{source}/"} 3175 ...> {other, opts} -> {other, inspect(opts)} 3176 ...> end) 3177 %{title: [format: "/pattern/", length: "1-20"]} 3178 """ 3179 @spec traverse_validations(t, (error -> String.t) | (Changeset.t, atom, error -> String.t)) :: %{atom => [term]} 3180 def traverse_validations(%Changeset{validations: validations, changes: changes, types: types} = changeset, msg_func) 3181 when is_function(msg_func, 1) or is_function(msg_func, 3) do 3182 validations 3183 |> Enum.reverse() 3184 |> merge_keyword_keys(msg_func, changeset) 3185 |> merge_related_keys(changes, types, msg_func, &traverse_validations/2) 3186 end 3187 end 3188 3189 defimpl Inspect, for: Ecto.Changeset do 3190 import Inspect.Algebra 3191 3192 def inspect(%Ecto.Changeset{data: data} = changeset, opts) do 3193 list = for attr <- [:action, :changes, :errors, :data, :valid?] do 3194 {attr, Map.get(changeset, attr)} 3195 end 3196 3197 redacted_fields = case data do 3198 %type{} -> 3199 if function_exported?(type, :__schema__, 1) do 3200 type.__schema__(:redact_fields) 3201 else 3202 [] 3203 end 3204 _ -> [] 3205 end 3206 3207 container_doc("#Ecto.Changeset<", list, ">", opts, fn 3208 {:action, action}, opts -> concat("action: ", to_doc(action, opts)) 3209 {:changes, changes}, opts -> concat("changes: ", changes |> filter(redacted_fields) |> to_doc(opts)) 3210 {:data, data}, _opts -> concat("data: ", to_struct(data, opts)) 3211 {:errors, errors}, opts -> concat("errors: ", to_doc(errors, opts)) 3212 {:valid?, valid?}, opts -> concat("valid?: ", to_doc(valid?, opts)) 3213 end) 3214 end 3215 3216 defp to_struct(%{__struct__: struct}, _opts), do: "#" <> Kernel.inspect(struct) <> "<>" 3217 defp to_struct(other, opts), do: to_doc(other, opts) 3218 3219 defp filter(changes, redacted_fields) do 3220 Enum.reduce(redacted_fields, changes, fn redacted_field, changes -> 3221 if Map.has_key?(changes, redacted_field) do 3222 Map.put(changes, redacted_field, "**redacted**") 3223 else 3224 changes 3225 end 3226 end) 3227 end 3228 end