>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.