Hacker News new | ask | show | jobs
by tptacek 3006 days ago
I've come to the conclusion --- maybe wrong? --- that Go doesn't want you to do ORM this way. For awhile I thought Go didn't want you to do ORM at all (which would be problematic for me, because I think writing SQL is a huge waste of time). But now I think it just doesn't want you to use ORMs that look like Django or ActiveRecord.

Instead of defining and marking up structs and setting up hooks and configuring registries of objects and stuff, just give up on dynamic ORM and switch to codegen.

I wrote a codegen ORM in a weekend that turned out much more pleasant to use than anything like gorm, not least because everything was just plain-ol'-Golang-code. All I really needed to write was the minimal Go code to dump a schema from Postgres, and then a bunch of text/template templates for all the functions I wanted. I got associations in just a couple functions.

I would have published, but it looks like 100 people had this thought before I did. sqlboiler seems like the most mature (I didn't look that carefully). But you could seriously just write your own. If I had to do it again, I'd probably codegen something on top of Squirrel.

4 comments

just give up on dynamic ORM and switch to codegen

Fun historical tidbit time!

Disclaimer first: I did not work at the Journal-World (company where Django was originally developed) at the time this story happened. I only started working there 6-ish months after the first public release of Django, but I was told the story by the folks who were there earlier. If I've messed up a detail, that's my memory being faulty.

Anyway. Back when Django was still an internal tool being split out from the application it was used to build, it had... a code generator for an ORM. You would write a description of your models, with some configuration, and run a script, which would generate a module of code for you, containing model class, query functions, etc. all of which had helpful comments at the top of the file reminding you they were machine-generated and should not be edited by hand (instead, edit the input and re-run the generator). Then you'd import from those files to use the ORM.

They showed this around, quietly, to some prominent Python people, who apparently suggested going with not-a-code-generator. But what actually ended up happening was that the first public announcement of Django, and the subsequent 0.90 and 0.91 releases, still had the code generator, cleverly disguised. Instead of a script you explicitly invoked to generate files of code, the ORM gathered up all the model definitions from all the installed Django apps, introspected those model definitions to construct all the relevant query code on the fly, packed that query code, along with the model classes, into in-memory Python module objects, and hacked them into sys.modules to make them importable.

This was the "magic" of the Django ORM, and is why no matter where you actually defined your model classes, you always imported them from somewhere under django.models, which was the location the ORM would hack its generated-on-the-fly code into.

Django 0.95 was the release that finally properly rewrote the ORM, and that effort was the source of the "magic-removal" moniker attached to the branch that eventually became 0.95.

I definitely get the impression that codegen is considered an important technique by the Go designers, but is not necessarily picked up on by Go adoptors, because it's not as common in other languages.
Agree 100%.

It's so easy to refactor Go and adding codegen on top is a piece of cake.

Like anything, it can be overused- but it's far better than reflect & co.

I'm glad to hear someone else shares my thoughts on SQL... there are things that are hard to express in ORM-speak but for the basic CRUD operations I much prefer writing code to make the SQL instead of spending time writing the same SQL templates over and over.

yes, codegen have better performance than reflect

but I think text/template to generate code is not really good

it hard to read and error-prone

it is more like a powerful c-macro

I think use go/ast to implement c++ likes template is other way

Whatever works! I just wrote the functions I wanted normally, tested them, then converted them to templates.