Generic default has_many association design

I’m working on the form builder for the refactor of ExAdmin. I’m prototyping a generic for control builder that figures out, based on reflecting the Ecto schema, how to render the correct form control to use (text_intput, radio, checkbox, select, etc.).

For a has_many association, I want to create a select and load it will all records of the association, unless a collection is provided as an override. The idea here being that it works out of the box with defaults, unless override by the programmer.

Question: What would be the best way to handle default has_many associations.

Here is a slim template showing how this would be used:

= form_for @changeset, @action, [class: "form-horizontal"], fn f ->
  .box-body
      .form-group
        = label f, :name, class: "col-sm-2 control-label"
        .col-sm-10
          / generates a text_input for :string
          = ExAdmin.Form.input_builder f, :name, class: "form-control", place_holder: "name"
          = error_tag f, :name
      / <snip> ...
      .form-group
        = label f, :active, class: "col-sm-2 control-label"
        .col-sm-10
          / generates a toggle input for boolean
          = ExAdmin.Form.input_builder f, :active, class: "form-control"
          = error_tag f, :active
      .form-group
        = label f, :country_id, class: "col-sm-2 control-label"
        .col-sm-10
          / generates a select with options for all countries by default
          = ExAdmin.Form.input_builder f, :country_id, class: "form-control", place_holder: "country_id"
          = error_tag f, :country_id
    .box-footer
      = submit "Submit", class: "btn btn-primary"

For the has_many :countries association, the approach above would need the Repo to fetch all countries. Here are a few options that I’ve come up with:

  • find it from the @changeset for the Phoenix.HTML.Form data (is this even possible?). I know there is a repo field in the changeset, but its not set at this point.
  • generate the default collection in the controller and explicitly pass the @countries binding to the api. This makes the generator more complicated
  • create an “admin” data struct and pass it along with the FormData? This is the approach I stared with, but it would be cleaner if I could use the f FormData only.
  • any other ideas?

In the past, I just used a ex_admin scoped :repo config entry. However, I would like to support multiple repos, endpoints, and multi instances in umbrella projects. So, I’m trying to stay away from global config.

Any thoughts, or opinions?