5 ms·
My perhaps slightly biased input on Python vs. Elixir (I worked with Python for different projects for a couple years, and have been using Elixir full-time for
by ralmidani 4y ago
My perhaps slightly biased input on Python vs. Elixir (I worked with Python for different projects for a couple years, and have been using Elixir full-time for ~1.5 years).
Elixir as a core technology for application development (as opposed to data science/ML/AI - it’s still early days for Nx/Axon/etc.) is better than Python in just about every way that matters; e.g. immutability by default eliminating whole classes (no pun intended) of bugs, having the full power of OTP available if you need a distributed system, more convergence around libraries and frameworks (to the point that José Valim also works on Phoenix, LiveView, Nx, etc., not to mention having Mix, EEx, and ExUnit built in).
I do wish Elixir had significant whitespace rather than do/end, but that’s not a hill I’m willing to die on…
My one major gripe with Elixir’s ecosystem compared to Python’s is that, IMO, Django would have been a better source of inspiration for a dominant Web framework compared to Rails; Django’s model layer is top-notch, with all your basic model information for an app contained in one models.py file. In Rails and Phoenix, you don’t get auto-migrations out of the box for simple use cases, and your model layer ends up being distributed across schema/ActiveRecord files, a “structure.sql” or “schema.rb” file, and the migrations. In my real-world use, this has been… “sub-optimal” compared to Django, to the point that I sometimes dream about building my own Django-ish framework for Elixir. Django+Django REST Framework are that good - if you stick with Python and are not using them already, do yourself a favor and check them out.
Also, Django’s admin is old, crusty tech, but it does what it set out to do really well.
I will admit Django’s implicit queries can cause DB performance issues, I wish there was an explicit analogue to Ecto’s Repo.* functions to force devs to think about when to make the DB call.
- josevalim 4y agoIf you don’t mind, I have some questions as I would like to learn more. Are you aware of a large models.py for reference and learning purposes? Is it expected to define the schema of all my models in a single place but none of the logic? Also, don’t you run into scenarios in Django where the automatic migration is not enough and you need to provide custom commands? In such cases, how do you provide them? And can you have scenarios where you have two models pointing to the same table, but perhaps to a subset of fields (this is specially useful in read-only cases)? How is that handled? Thank you :)
- theptip 4y ago> Also, don’t you run into scenarios in Django where the automatic migration is not enough and you need to provide custom commands? In such cases, how do you provide them? Data migrations are the obvious case where you need to write migration code manually. It comes up in schema migrations too on big projects. It’s easy to modify the auto-generated migration; it’s just Python that gets run by the migration tool: https://docs.djangoproject.com/en/4.1/topics/migrations/#data-migrations https://docs.djangoproject.com/en/4.1/topics/migrations/#dat...
- traverseda 4y ago>Are you aware of a large models.py for reference and learning purposes? Is it expected to define the schema of all my models in a single place but none of the logic? Django models are normal python classes. Depends on exactly the logic you're dealing with, but generally you can make logic be a method on that class. Try to avoid logic that spans multiple tables in general, and if you have logic that does span multiple tables you probably want it to be a function and not a model method. There are also signals that get sent on different things like model delete/create/etc, but they're to be used even more sparingly. Logic for querying data should probably go wherever you're going to use it and you should just pass the model objects directly to your views. To start don't worry about optimizing queries or the n+1 problem, but as you get more experience you can use `prefetch_related` in order to avoid the n+1 problem. >Also, don’t you run into scenarios in Django where the automatic migration is not enough and you need to provide custom commands? In such cases, how do you provide them? You shouldn't generally need to. That said django's migration system is very powerful and you can hand-write a migration if you need to. >And can you have scenarios where you have two models pointing to the same table, but perhaps to a subset of fields (this is specially useful in read-only cases)? How is that handled? I mean you can using proxy models but that's not really a thing with django. Models are for developers and generally developers have all the permissions any way, so the solution to making a model read only is to just not write to it. If you want to present a read-only model to an end user you can reference it in a view of make a read-only serializer with django-rest-framework. It's python, not java, there's no such thing as private/protected members just convention to put an "_" in front of things other developers probably shouldn't be messing around with.
- dudul 4y agoAren't you confusing Phoenix and Ecto here? I'm familiar with neither Rails nor Django so I dont fully follow what you're describing. Are you just talking about the difficulty of maintaining the schema/DB mapping with the migrations ran against the DB?
- ralmidani 4y agoI’ve never used Ecto outside of a Phoenix app, so yes, in my mind the line between them was blurry. Thanks for pointing out my mistake! A very recent real-world challenge I ran into was having to coordinate updates and diffs to all the files involved and keeping them in sync while doing local development (before deployment but also while working on fixing a very subtle bug). Every time I changed the migration file (even to add an index), I had to remember to checkout the previous version of “structure.sql” or the migration wouldn’t be run, even during a complete DB reset (structure.sql is used to know which migrations have been run and synced with the SQL dump). Also, the schema in Ecto does not have a complete validation API, so you’ll need to do those in “changeset” functions. Overall I like Ecto, and part of me regrets letting myself get spoiled by Django’s model layer and DB tools.
- josevalim 4y agoIt is totally fine to get spoiled if you consider it is better :D Your case about structure.sql is interesting. It would be nice if we could automate it somehow but, if we simply tried to re-run a changed migration, then the migration would likely fail because the other operations in it (such as adding fields), you already exist, no? Do you have any suggestions/ideas on how to tackle this? We could have a "mix ecto.migration.fix" that reverts the last migration, wait until you close/save the file, and migrate again, but I am not sure how useful it would be. How would Django approach this? If you auto-migrate and then do further changes, does it change the existing migration or does it generate new ones? PS: the changeset functions are pretty much on purpose though. It is important to decouple the validation from the schema because a single schema can have several validations rules (and they can diverge overtime!).
- 4y ago