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.