Might be that I'm missing something, but it feels that this should have been an invalid record:
pry(1)> project_params
%{"name" => ""}
pry(2)> changeset
%Ecto.Changeset{action: nil, changes: %{name: ""}, constraints: [], errors: [],
filters: %{},
model: %Elix.Project{__meta__: #Ecto.Schema.Metadata<:built>, id: nil,
inserted_at: nil, name: nil, updated_at: nil,
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 2}, optional: [], opts: [], params: %{"name" => ""}, prepare: [],
repo: nil, required: [:name, :user_id],
types: %{id: :id, inserted_at: Ecto.DateTime, name: :string,
updated_at: Ecto.DateTime, user_id: :id}, valid?: true, validations: []}
pry(3)> changeset.valid?
true
Here's how I have the models defined:
Project
defmodule Elix.Project do
use Elix.Web, :model
schema "projects" do
field :name, :string
belongs_to :user, Elix.User
timestamps
end
@required_fields ~w(name user_id)
@optional_fields ~w()
@doc """
Creates a changeset based on the `model` and `params`.
If no params are provided, an invalid changeset is returned
with no validation performed.
"""
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
end
end
User
defmodule Elix.User do
use Elix.Web, :model
schema "users" do
field :email, :string
field :password, :string, virtual: true
field :crypted_password, :string
has_many :projects, Elix.Project
timestamps
end
@required_fields ~w(email password)
@optional_fields ~w()
@doc """
Creates a changeset based on the `model` and `params`.
If no params are provided, an invalid changeset is returned
with no validation performed.
"""
def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
|> unique_constraint(:email)
|> validate_format(:email, ~r/@/)
|> validate_length(:password, min: 5)
end
end
and this is the code for the controller create action:
def create(conn, %{"project" => project_params}) do
changeset =
Ecto.build_assoc(conn.assigns.current_user, :projects) |>
Project.changeset(project_params)
IEx.pry
case Repo.insert(changeset) do
{:ok, project} ->
conn
|> put_flash(:info, "Project #{project.name} created succesfully")
|> redirect(to: project_path(conn, :index))
{:error, changeset} ->
render(conn, "new.html", project: changeset)
end
end
I am submitting the form without entering anything on purpose, so that I might test form display errors. What am I missing here?
The changeset.valid?
will only be false
if the value of name
is nil
. Since the value of name
in your case is an empty string, the changeset will be valid.
The best place to deal with forms passing empty strings as values is in the controller by adding the scrub_params
plug in like so:
plug :scrub_params, "project" when action in [:create, :update]
Check the documentation for more info on scrub_params
.