The trick is to implement these methods on your ModelAdmin subclass: https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#dja...
You can make has_add_permission(), has_change_permission() and has_delete_permission() all return False, while has_view_permission() returns True.
The readonly_fields and related get_readonly_fields() method are very useful too.
I wrote up another related trick in this TIL: https://til.simonwillison.net/django/extra-read-only-admin-i...