The new Phoenix 1.3 generator creates changeset functions for schemas in the context module. This brings in two problems.
cast_assoc depends on changeset functions defined in the schema. So developers have to rewrite changeset functions inside schema and cannot just modify the changeset function already generated. Alternate option is to use with option in cast_assoc but to make use of with option for every association is too much explicit and a burden for developers imho.
In a production app that I am rewriting in Phoenix 1.3, I am already facing at least two or more changesets per schema. Given that I am having about 3-4 schemas in a given context, it's about 10 changeset functions in my context file which again I feel is producing more noise in my context file. It would be much cleaner and easy to browse changeset by schemas rather than scrolling a lengthy context file and training the eye to catch the required changeset.
Original discussion started in https://elixirforum.com/t/phoenix-v1-3-0-rc-0-released/3947/88?u=shankardevy and I am opening this issue as per @josevalim request.
as @michalmuskala has said on the forum discussion, I'm in agreement that I believe using the with: option explicitly is the way to go. I'm not sure how discoverable it is for cast_assoc given that it has worked out for a lot of people to use the defaults before. Any ideas @josevalim ?
Do we get a good error message? If we get a good error message that points people to with:, then I am happy with any decision.
I think the message is good enough https://github.com/elixir-ecto/ecto/blob/master/lib/ecto/changeset.ex#L711-L719
@josevalim @chrismccord @michalmuskala I'm migrating a production from 1.2 to 1.3.
I just only generated 3 schemas, and the context file already has 300+ lines of code, it might become a big fat ugly file very soon.
I also noticed that all the changesets are in context file now, with all the validations. I would think those checks for data integrity should be in the schema module. It would be nice to know the reasons the team decided to put them in context.
Thanks.
@ducduong questions like this are best kept to the forum, but for brevity:
We generate changesets in the context for a couple reasons. First, we want to place the changeset building closer to the user input, so folks start thinking about decoupling their db schemas from the various shapes of user input that they need to cast and validate. Another reason is the schemas we generate are sharable across boundaries, so we don't want folks to see the changeset functions there and start violating boundaries b/w parts of their system. This does not mean you should never build a changeset in your schema or elsewhere, but we feel the current approach pushes newcomers in the best direction. Generators are learning tools and a starting point 鈥撀爊ot the end of your application deign. Ultimately you need to make design decisions and structure your code beyond running code generators.
I just only generated 3 schemas, and the context file already has 300+ lines of code, it might become a big fat ugly file very soon.
As with any code, if you feel your code is becoming large, coupled, and ugly, then it's time to refactor and think about how you can cleanly isolate and clean up the moving parts :)
@chrismccord
As with any code, if you feel your code is becoming large, coupled, and ugly, then it's time to refactor and think about how you can cleanly isolate and clean up the moving parts :)
But how?
I have two schemas in my context and it's over 380 lines with one more schema to come.
@abitdodgy a context is just a module. If you need to break it apart, you break into smaller modules. Such as MyApp.Context.Foo and MyApp.Context.Bar.
For everyone following this thread, changesets are now in schema files as per the latest master code, even though the discussion here doesn't convey it. Maybe the core team changed their choice later.
Well said @shankardevy. We changed because we thought it would be a better example of how to break your code apart. Mostly bullet 2 above.
I just got the latest master and generated an json endpoint and it generates the change set in the context file:
# mix phx.gen.json Accounts User users name:string email:string password:string
and then, in the accounts.ex file:
defp user_changeset(%User{} = user, attrs) do
user
|> cast(attrs, [:name, :email, :password])
|> validate_required([:name, :email, :password])
end
Am I losing something here?
Please make sure you are in master since there is no changeset here: https://github.com/phoenixframework/phoenix/blob/master/priv/templates/phx.gen.context/schema_access.ex but in the schema: https://github.com/phoenixframework/phoenix/blob/master/priv/templates/phx.gen.schema/schema.ex
thanks @josevalim I was using the installer with the instructions on installer/README.md, just changed to install it on installer folder and it get the latest version.
Most helpful comment
@ducduong questions like this are best kept to the forum, but for brevity:
We generate changesets in the context for a couple reasons. First, we want to place the changeset building closer to the user input, so folks start thinking about decoupling their db schemas from the various shapes of user input that they need to cast and validate. Another reason is the schemas we generate are sharable across boundaries, so we don't want folks to see the changeset functions there and start violating boundaries b/w parts of their system. This does not mean you should never build a changeset in your schema or elsewhere, but we feel the current approach pushes newcomers in the best direction. Generators are learning tools and a starting point 鈥撀爊ot the end of your application deign. Ultimately you need to make design decisions and structure your code beyond running code generators.
As with any code, if you feel your code is becoming large, coupled, and ugly, then it's time to refactor and think about how you can cleanly isolate and clean up the moving parts :)