News for hackers. Stuff that matters.
Re:Elixir for Humans Who Know Python by ralmidani on Saturday February 25, 2023 @07:04AM (#34936023)
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.

Re:Elixir for Humans Who Know Python by josevalim on Saturday February 25, 2023 @08:29AM (#34936510)
If you don’t mind, I have some questions as I would like to learn more. Could you share such a models.py for reference and learning purposes?

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 :)

Re:Elixir for Humans Who Know Python by traverseda on Saturday February 25, 2023 @09:26AM (#34936893)
>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.

Re:Elixir for Humans Who Know Python by josevalim on Saturday February 25, 2023 @10:03AM (#34937160)
Thanks for the insights, I appreciate it! I will take some time to go through the docs and learn more. One last question for now (I hope): after the migrations are auto-generated, do you check them into version control?

> Models are for developers and generally developers have all the permissions any way

To clarify, the reason why you would want to have read-only models is rather in complex data model cases or for performance reasons. For example, if you have a large model with 50 fields, defining a subset with 10 fields can be beneficial to performance in several queries. Other than that, I agree with you.

Re:Elixir for Humans Who Know Python by Alir3z4 on Saturday February 25, 2023 @10:39AM (#34937441)
Yes. Migrations are checked into the source control. They're still the source of truth of the database changes.

Regarding read only models pointing to a tabel. It'd possible to use proxy models or even non managed models where with some mix of Managers it can provide what you're looking for.

For getting a subset of fields from a database tabel, Django provides ".only()" on the QuerySet which you can use to list all the fields explicitly and only them will be retrieved. It can even span to foreign field relations as well.

Diango ORM is indeed powerful and vrry flexible once you learn it. A novice developer can use it quickly and won't get in the way. An advanced developer can do very complex sfuff with it without touching any raw sql. Still, it's possible to write custom sql commands directly.

Re:Elixir for Humans Who Know Python by traverseda on Saturday February 25, 2023 @10:32AM (#34937374)
Yep, you do check the migrations into version control.

>For example, if you have a large model with 50 fields, defining a subset with 10 fields can be beneficial to performance in several queries. Other than that, I agree with you.

Nope, the django ORM has several helpers here. What you do in the most basic case is do something like

    MyTable.objects.filter(key__gte=12).only("field1","field2","field3")
Which will (during the initial request) only fetch the specified fields from the DB. There's an equivalent (defer) for telling it not to fetch those fields during the first query.

Of course is someone than tries to access "field4" in a template/view django will make an additional query to get that extra data.

It's a bit more complicated to make a nice chainable filter so you can call `MyTable.objects.only_foo_rows()`, you'd need to define a method on a custom object "manager", but that still wouldn't generally be handled at the model level.

There's also things like select_related, prefetch_related, and other tools to make the ORM layer more performant. Of course for really exotic things like tree-based data structures you can also perform raw SQL queries on an ORM object with `MyTable.object.raw("SQL goes here")` and have it still return ORM objects. Please don't do that though.

For a long time I didn't realize why people were so down on ORMs until I tried using a non-django ORM, it really does set the bar.

Re:Elixir for Humans Who Know Python by Alir3z4 on Saturday February 25, 2023 @10:44AM (#34937483)
> For a long time I didn't realize why people were so down on ORMs until I tried using a non-django ORM, it really does set the bar.

This. Very much this.

I have given up on one finding such. The closest and nicest one that comes to mind is SQLAlchemy. The rest of languages or frameworks have indeed nothing compared to Django ORM and SQLAlchemy. It's not even fair to compare them.

Re:Elixir for Humans Who Know Python by traverseda on Saturday February 25, 2023 @12:26PM (#34938413)
Peewee ORM can also be nice if you need support for more esoteric DB features (sqlite user-defined table functions?). No migrations though.
Re:Elixir for Humans Who Know Python by spapas82 on Saturday February 25, 2023 @02:20PM (#34939571)
My experience is also exactly like this. I have a project in java/spring and it uses hibernate as an ORM. I can't think of how much better is the Django ORM than hibernate. When people express hate for ORMs I understand where this hates comes from.

If I ever need to do another Java project I prefer to use raw jdbc sql than using hibernate.

Re:Elixir for Humans Who Know Python by josevalim on Saturday February 25, 2023 @10:51AM (#34937533)
Ack on migrations, tks!

---

For completeness, you can do the same in Ecto:

    from t in MyTable, where: t.key >= 12, select: ["field1", "field2", "field3"]
However, that will still allocate a "MyTable" struct in Ecto. And if that struct is large (say 50 fields), slots for it are allocated (but none of the actual data on the fields).

I am not familiar with Python Object Model enough but, if your Python example still allocates an object with slots for all 47 other fields besides the three selected, then you may still find yourself under circumstances you would rather have a "slim" version of the model and reduce memory allocation. This may be needed when you need to load thousands of entries into memory for processing or similar.

I am not saying this is a must have but I am just trying to clarify what my initial comment was about. :)

Re:Elixir for Humans Who Know Python by traverseda on Saturday February 25, 2023 @12:23PM (#34938391)
>I am not familiar with Python Object Model enough but, if your Python example still allocates an object with slots for all 47 other fields besides the three selected, then you may still find yourself under circumstances you would rather have a "slim" version of the model and reduce memory allocation.

Honestly at that point probably don't be using python. ORM objects don't use slots, they use a hashmap to look up everything every time, and probably that hashmap points to a method that points to another hashmap that points to some kind of data structure.

If you're worried about that than django probably is not for you. That being said it scales out very nicely, you probably are not loading dozens of thousands of entries in to memory at once but are instead using a celery distributed task queue. If you did need to load that much in to memory for some reason (presumably some kind of data science?) You can use the `in_bulk("field1","field2","etc")` query option to return raw data as dictionaries/lists instead of ORM objects. You don't get the benefit of ORM methods but you're not instantiating an ORM object for every row and probably for that kind of thing you're better off taking a more functional approach any way.

Re:Elixir for Humans Who Know Python by josevalim on Saturday February 25, 2023 @12:49PM (#34938649)
That’s great, thanks for taking the time! Appreciated.
Re:Elixir for Humans Who Know Python by Xzya on Saturday February 25, 2023 @12:57PM (#34938750)
> I am not familiar with Python Object Model enough but, if your Python example still allocates an object with slots for all 47 other fields besides the three selected, then you may still find yourself under circumstances you would rather have a "slim" version of the model and reduce memory allocation. This may be needed when you need to load dozens of thousands of entries into memory for processing or similar.

Alternatively you can also tell the ORM to return a dictionary rather than a model instance: https://docs.djangoproject.com/en/4.1/ref/models/querysets/#...

For example, you may get a list of dictionaries with only "field1", "field2" and "field3" from a model like this:

  SomeModel.objects.filter(...).values("field1", "field2", "field3")

  [{"field1": "foo", "field2": "bar", "field3": "baz"}]
Re:Elixir for Humans Who Know Python by spapas82 on Saturday February 25, 2023 @02:10PM (#34939482)
Please notice that nothing stops you from defining a non managed (ie Django migrations won't mess with it) model pointing to the same table and having declared only three of the fields of the original model.
Re:Elixir for Humans Who Know Python by traverseda on Saturday February 25, 2023 @03:02PM (#34939886)
That is more elegant than using a proxy model
Re:Elixir for Humans Who Know Python by spapas82 on Saturday February 25, 2023 @03:27PM (#34940126)
The Django docs also mention that https://docs.djangoproject.com/en/4.1/topics/db/models/#diff...

> If you are mirroring an existing model or database table and don’t want all the original database table columns, use Meta.managed=False.

Re:Elixir for Humans Who Know Python by neillyons on Saturday February 25, 2023 @02:27PM (#34939624)
You can use `managed = False` and `db_table` to have another model pointing to the same table. `managed = False` will stop Django automatically creating schema migrations.

eg.

```

class ReadOnlyUser(models.Model):

    email = models.CharField(max_length=255)

    class Meta:
        db_table = "users"
        managed = False
```
Re:Elixir for Humans Who Know Python by POiNTx on Saturday February 25, 2023 @02:31PM (#34939657)
Just wanted to say thank you for all the great work you've been doing. I'm a recent Elixir/Phoenix convert coming from a couple of years of full time Django and it makes me so happy to see you being interested in the way Django handles models. Imo it's one of the last things I'm struggling leaving behind (together with its tight integration of the Django admin, which you get for free).

I haven't looked close into Ash but to me it seems their approach is interesting and offers a solution, not the way Django does it but it is a solution, although a bit hard to grasp your head around at first. Django models are more simple and straightforward.

Re:Elixir for Humans Who Know Python by theptip on Saturday February 25, 2023 @01:51PM (#34939313)
> I mean you can using proxy models but that's not really a thing with django.

I think this is over-opinionated. You can using Proxy models, and this is a thing in Django. It’s not “Django 101” sure, but it’s not discouraged.

One reason you might want proxy models is if you want to only fetch certain columns for a specific type of usage. Another common use for proxy models is as a hack to get multiple Django Admin pages for the same model (again, perhaps pulling different fields or offering different views).

Re:Elixir for Humans Who Know Python by ralmidani on Saturday February 25, 2023 @10:15AM (#34937240)
Hi José, thanks for joining the discussion!

The sibling to this post (by “traverseda”) is more informed and informative than I could hope to write. I will add that Django has Manager classes that have default behavior included for interacting with the query API, and they can be customized to your needs. You can even have multiple managers for a given model.

https://docs.djangoproject.com/en/4.1/topics/db/managers/

It’s been a few years since I used Django, but I remember its model layer fondly, and still follow the framework’s release processes.

One of the areas where Django lags is being fully async (Python imposed a lot of limitations on async operations for most of its life). The upcoming 4.2 release of Django is laying the groundwork for async DB operations.

Re:Elixir for Humans Who Know Python by pg_1234 on Saturday February 25, 2023 @11:12AM (#34937712)
I don't think this is something that will come down to technical merits.

Unfortunately it is a popularity contest, where entrenched network effects are working against Elixir.

As a Python programmer, I truly believe Elixir/Phoenix is the best stack out there from a technical perspective.

However the incredible breadth of libs and resources ... and most importantly the mind share (both available devs, but more importantly available jobs for seniors who commit to Elixir) means that it is still just not a competitive choice.

Almost all commercial software dev is not about technical excellence, but rather about applying the available tech to a particular business domain. And there Python (and .Net, Java, PHP, Ruby ... even Go) are so far ahead that sadly Elixir doesn't look like it will make it.

I don't know that anything can be done about this. For all that I think Elixir is better, I, like most devs, am not about to sacrifice (or even impair) my career for the cause. Personal remuneration wins out.

Re:Elixir for Humans Who Know Python by Alacart on Saturday February 25, 2023 @12:05PM (#34938231)
That does seem like the same arguments that are made prior to just about anything becoming mainstream though. I'm not saying it means nothing that python has so much mindshare, but the same could have been said to pythons predecessors as well. You're right about commercialization not being about technical excellence, in the short term. In the medium and long term, it's the advantages of recent technical excellence that drives a lot of commercialization.
Re:Elixir for Humans Who Know Python by pg_1234 on Saturday February 25, 2023 @01:50PM (#34939302)
It's also the same arguments that are made prior to things falling by the wayside.

Not that I think Elixir will ever fall away, just that it won't go fully mainstream.

Re:Elixir for Humans Who Know Python by Alacart on Saturday February 25, 2023 @06:42PM (#34941617)
Very true, maybe it's not a strong predictor either way.
Re:Elixir for Humans Who Know Python by theptip on Saturday February 25, 2023 @01:48PM (#34939292)
> 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/#dat...

Re:Elixir for Humans Who Know Python by dudul on Saturday February 25, 2023 @08:39AM (#34936565)
Aren'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?

Re:Elixir for Humans Who Know Python by ralmidani on Saturday February 25, 2023 @09:50AM (#34937085)
I’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.

Re:Elixir for Humans Who Know Python by josevalim on Saturday February 25, 2023 @10:16AM (#34937256)
It 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!).

Re:Elixir for Humans Who Know Python by ralmidani on Saturday February 25, 2023 @01:26PM (#34939092)
Django’s approach is to run all the migrations, from the migration files, if the database gets wiped clean. In other words, if you’re developing on your local machine, you can just drop the DB and start new (or if using SQLite, just delete a file), and after running migrations, you never have to worry about whether all the necessary files are in sync with the database. As Neo might say:“there is no spoon…” seriously, structure.sql is simply not needed, so you won’t find yourself making creative use of git reset/checkout on a specific file as you’re iterating.

To answer an earlier question you had, Django’s “makemigrations” command tries its best to help you avoid having to manually edit migration files; if you rename a field in models.py, it’s usually smart enough to ask you, at the command line, if you renamed it, and proceed to make the migration file for you. Of course, for advanced migrations the command can only take you so far, but again, if you’re trying to iterate quickly, you really need to experience Django’s ORM and DB tools to be able to appreciate them fully.

Regarding changesets, I love the functional API, and at the same time I wish we could automatically derive a “default_changeset” function from the schema itself for straightforward cases, and then let devs run diverging validations in their own changeset functions. Maybe an interesting idea to explore with a macro?

By the way, I’m a huge fan of your work, and hope I get the opportunity to work with Elixir for a long time. Any criticisms or comparisons are to try to bring improvements to the ecosystem I love. :)

Re:Elixir for Humans Who Know Python by josevalim on Saturday February 25, 2023 @02:31PM (#34939654)
Thanks! FWIW, structure.sql is not needed in Ecto either. You must do that when:

1. You want to start discarding migrations (because you have too many) 2. You had an existing database

Are those never a "trigger" on Django side? I appreciate the opportunity to learn!

Re:Elixir for Humans Who Know Python by maxmorlocke on Saturday February 25, 2023 @03:40PM (#34940237)
Generally speaking, it's inadvisable to 'discard' migrations with django. If the migration set is getting too large, the general practice is to 'squash' migrations, which is a django-supported function for merging the migrations down to a single app. You can do the less blessed, but more simple function of nuking the migrations, updating the migrations table, and remaking your migrations... but you have to coordinate that in every environment.

For an existing database, you can easily create django models for each table, etc. There's a --fake option to update the migration table to make it think you've applied these migrations, but not actually apply them. This convinces django you've brought the database in sync. May your deity or deities help you if you did not actually bring it in sync. I've used this quite a bit in some java ee->python migrations I've done in the past.

Re:Elixir for Humans Who Know Python by ralmidani on Saturday February 25, 2023 @04:30PM (#34940678)
TIL you actually don’t need structure.sql for Ecto! In that case, discarding migrations on a large running app seems like a not-always-good practice, right? FWIW, my experience with Ecto has always been in joining existing codebases that are already big.

I do also want to add that Django can kind of do the reverse of auto-generating migrations: it can “inspect” an existing database and generate Python classes that allow you to use the Django API as if you wrote those classes yourself: https://docs.djangoproject.com/en/4.1/howto/legacy-databases...

Of course, the feature is not going to be perfect, especially if a team has been using odd naming conventions in the legacy database, but it seems helpful at least in theory.

Re:Elixir for Humans Who Know Python by bostonvaulter2 on Saturday February 25, 2023 @05:45PM (#34941226)
Inspecting a database and coming up with models is a frequent pain point that I see new elixir devs experiencing. I’d love to see someone take a stab at generators for that. I wonder if someone would do an integration for https://ash-hq.org/ that does that.
Re:Elixir for Humans Who Know Python by bostonvaulter2 on Saturday February 25, 2023 @05:42PM (#34941199)
Hmmm, if we’re willing to accept a dependency on git than it might be interesting to write a `mix ecto.migration.rerun` that rolled back any migrations that are untracked or dirty according to git, then re-ran them. That would simplify a pain point I encounter when I’m iterating on a migration because sometimes I rollback too far accidentally and finding the right argument to rollback feels like a chore. The nice thing is that could be prototypes outside of ecto quite easily.