fireproofsocks
While working with some colleagues who are familiar with other languages and frameworks, they wondered why Ecto migrations don’t use raw SQL and instead typically rely on the Ecto Schemas to insert data. I have never seen any examples of raw SQL in Ecto migrations, but it would make sense to isolate the database “bootstrapping” away from any specific code implementation that might show up in your Schema files.
Any thoughts on this? Thanks!
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Documentation
While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself.
My main conc...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 9- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
shanesveller
I would actually assert that using Ecto.Schemas, changeset functions, and/or context modules directly in migrations is dangerous, independent of whether doing pure data insertions in a migration is a good or bad practice - which was part of my position in the forum thread about Seeds as migrations.
My main concern about this practice is because I believe your migrations should remain valid to execute from start-to-finish throughout the life of your project - no shortcuts with
mix ecto.dump/mix ecto.load, etc. I also believe that once a migration has been applied anywhere outside of the author’s workstation, you should treat that migration file as largely read-only and not go mucking about with its meaningful content later on. Aesthetic changes to syntax are still okay if the behavior remains the same. This is obviously a subjective position to take, and a fairly militant one.Now, with that context in mind, here’s how those concerns pertain to this question: If your schemas evolve sufficiently over time, and you’re intentionally using schema structs and/or changeset functions in migration files, you’ll likely eventually experience the problem that the content in your earlier migrations, which were valid at the time, are no longer passing validations or are otherwise failing to execute with the current versions of those schemas/changesets. How do you solve this? Adhering to my constraints above, you nearly have to resort to schemaless Ecto queries or raw SQL.
It’s not often that I get to hold up Ruby/Rails as a positive example for Elixir devs, but they’ve also previously included some notes in their official documentation about the pitfalls of this approach, and techniques to avoid this tight temporal coupling between migrations and ActiveRecord models. It looks like it’s been removed from the latest version of the docs for Rails 5.2, though, but some of the ideas in the historical link are still pertinent.
fireproofsocks
Yes! That was the exactly the concern (and from a Rails developer).
So why don’t the official docs push raw SQL migrations in their examples? I’m not even sure I know HOW to issue raw SQL statements using Ecto…
Currently I use repo operations (e.g.
Repo.insert_all()), but I noticed that it can get weird when you use an atom vs. a string for the 1st argument, but regardless, it’s all in an effort to avoid using any Schema modules in our migrations.Ninigi
I would consider using schemas in your migration as antipattern, for the reasons @shanesveller listed.
Just because I am curious, where did you find an example of this?
fireproofsocks
That’s the only example I’ve ever seen in my experience with Elixir to date. In fact, I’m not sure I have ever seen an example of how to run raw SQL.
Examples of using schemas to create seeds:
seeds.exsdoes not use raw SQL, it references a schema:https://phoenixframework.org/blog/seeding-data
Seeding Data in Phoenix - ElixirCasts seems to demonstrate using a context, which I think might be more brittle than using the schemas.
The hex docs at Overview — Phoenix v1.8.8 don’t seem to make mention of seeds, but in any case, I think the predominant example most users will encounter will demonstrate seeding your database with schema structs and not raw SQL.
benwilson512
Seeding data in this case doesn’t seem related to all of this. I view seeds as populating the database for development in which case structs are fine, that’s the present value. It runs after all the migrations run anyway. Otherwise what’s really even the point of having the file? If it’s just the production seeds you did 3 years ago then it’s just a relic with no present value.
I agree about the overall point of not using schemas inside migrations though.
As for why migration files use a DSL and not raw SQL: convenience functions. It lets you do
field :foo_id, references(:foos)which helps set up constraints, makes sure the column types are correct, etc.Ninigi
Ah, interesting! Following is my take on that, so grain of salt etc…
IMO a seed should be exactly that: The very minimum of data you need when firing up the project, for example if you have a product database, and you have a set of vendors, you’d want new developers to start out with a most basic, but realistic data set.
It should also be a viable seed on newly created “clusters” that do not communicate with other clusters at all, so in the given example every newly created shop could have the same vendors, but not necessarily the same products.
This also means that you should probably have functions that handle creating new vendors (gonna stick to this example), even if it is just for internal use, without ever providing a public UI/API. IMO, your seeds should only invoke functions you could also use in production, to create new vendors, and more or less emulate what a developer would probably do when setting up a fresh app in whatever environment.
Using structs to create database entries should be limited to test factories (again imo, others might disagree) to circumvent potential side effects in the actual create functions.
Would you mind giving some more information on how you were using it that resulted in your post? Maybe we are not talking about the same thing
Edit: You linked the seeds tutorial, and I agree. I would not use that pattern, I would prefer using functions that are tested and production ready. So instead of inserting a vendor, I would write a function register_vendor to handle it. So, if the seeds fail, the function should be rewritten.
fireproofsocks
Sorry, I should clarify: this discussion applies to both migrations and to seeds… the bigger picture here came out of dealing with enums in the database. From my experience with MySQL, I know that those do not migrate well… e.g. if you have to add a new value to the list of allowed types, it was a problem. I don’t know if that’s true with PostGres.
Nonetheless, instead of an enum column, I used a related table and a foreign-key relationship. That’s preferable since it allows us to add a few bits of extra data to the values if needed.
But that got me to the point of wanting to include seed data (and therefore schema-structs per the available examples) for those tables as migrations, because in my mind, they are part of the database definition, and had we defined the columns as enums, their definitions WOULD be in the migrations. So I would up having some seeded data in my migrations – that lead to a separate post, and there are some strong opinions about whether this is an appropriate practice, but in this case, it seems to make a lot of sense and solve the problems of trying to seed data into servers where we may or may not have access or a working command.
anthonator
Here are the docs for executing raw SQL from a migration.
benwilson512
Again I think you’re using seeds here in a different sense than the phoenix generator is. If you’re talking about production server bootstrapping then yeah, perhaps those seeds should sit in some kind of migration. That isn’t what seeds.ex is most commonly for. It’s for bootstrapping your development environment with some basic data so you can use the current version of the application. Elixir code makes perfect sense there. Arguably, it should promote using context functions instead of direct inserts however.