How can I get info about my schema and changesets?

This is the use case (to understand why I need the info) I’m working on how to validate forms in “dead views” (sometimes Liveview is overkill if 99% of stuff can be cached in a CDN). It is a combination of using HTMX to call Phoenix to do a validation when something is changed (nothing strange here) and I just replace the content that comes back from Phoenix. But it would be nice to not have to do a trip to the server just to check wether a field is not empty when it is required or if min length is 5. So I build a small javascript library to do some basic client side validations (like checking if a field is required, if it matches a regex etc). And then I only need to fall back to the server for validation if a field contains a validation that is impossible client-side (like checking if an email is not already in the database). The javascript library checks for any attribute that starts with “validation-” so all I need to do is add an HTML-attribute in the phoenix code. Right now I do this manually but I want a way to automatically do a lookup on which validations are done in a changeset for a field so it can be added automatically (so I don’t need to remember to change both in changeset and in any forms everything a validation is added, changed or removed).

Enough background, can I, using elixir code or macros get a list of the schema types and which functions are run in validation. So with a schema and changeset like this:

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  schema "listings" do
    field :title, :string
    field :price, :integer
    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(listing, attrs) do
    listing
    |> cast(attrs, [:title, :price])
    |> validate_required([:title, :price])
    |> validate_length(:title, min: 15)
  end

I wanna be able to call Marketplace.Listings.Listing.get_validations() and get something like this:

%{name: :validate_required, fields: [:title, :price], params: %{}}, 
%{name: :validate_length, fields: [:title], params: %{min: 5}}
]

And Marketplace.Listings.Listing.get_types()

[
    {:title, :string},
   {:price, :integer}
]

I feel like it should be possible with macros (but way over my head) since it is all known at compile time.

Any idea on how I can do this?

1 Like

All the information you want is provided by ecto already.

Information around validations are part of changesets:

import Ecto.Changeset

attrs = %{}

changeset =
  {%{title: nil, price: nil}, %{title: :string, price: :integer}}
  |> cast(attrs, [:title, :price])
  |> validate_required([:title, :price])
  |> validate_length(:title, min: 15)

changeset.required
# [:title, :price]
changeset.validations
# [title: {:length, [min: 15]}]

Type information you get with schema reflection:

https://hexdocs.pm/ecto/3.11.2/Ecto.Schema.html#module-reflection

3 Likes

Aha! It was easier than expected! Thanks a lot!