Well, no answers... Will try to explain, what I found.
Flag --fake
Better to use to align Django state to the real database state. For example, database table contains columns "col1" and "col2", but Django model contains only fields "col1", "col2" and "col3". We remove field "col3" and run migration with flag --fake. Now consider this example: we have "col1" and "col2" in database table and in Django model. We remove field "col1" from model, run migration with --fake, expecting to drop the column later. But then we decide to roll back migration before dropping the column. This will fail, because Django will try to restore removed "col2", while it exists in the database table.
SeparateDatabaseAndState
This option is more secure - no issues with roll back, because this migration doesn't work with database (except table django_migration). It may be used to the case, when we want to postpone database changes. In our company we chose this option to remove unused field from Django model (we can't apply changes to the database immediately, because of running processes, that use the old state of Django models). On the second step we make another migration - without SeparateDatabaseAndState (basically the same - with field removal inside), that will be applied to the database and will drop the column.