On Jun 5, 2:44 pm, Russell Keith-Magee <[email protected]> wrote: > Two significant reasons: > > Firstly, if we use the Meta attribute approach, we can raise a > specific validation error when the user has an app with a model that > references auth.User directly, and User has been swapped out. This is > mostly useful as a migration trigger -- as soon as you change > AUTH_USER_MODEL, you'll get a bunch of errors about all the apps that > haven't been updated and will break as a result. This catches as a > development-time validation error the most-likely set of problems that > will occur when swappable User models start getting into the wild. > Using the code you've provided, changing AUTH_USER_MODEL would > silently fail at some point in production, because auth.User still > "exists", but is now a very different beast. > > Secondly, there's some avoidance of nightmares from Django's past. > Using the approach you describe, you're making auth.User a dynamic > model reference -- the model that is referred to by auth.User is > changed at runtime, based on the value of a setting. This means that > just because you have a auth.User object, doesn't mean you can know > for sure anything about it. > > This is somewhat comparable to the bad old "magic removal" days of > Django. A lot of the problems we experienced back then were caused by > the fact that you couldn't be certain that a model existed, or if it > did exist, that it was the model that you thought it was, because > other mechanisms in the load sequence would swap out models/modules, > and/or move them somewhere else. This is a set of headaches that I'd > rather not revisit. > > There's a scaled back alternative of what you describe -- essentially, > where you only have the "else" clause from your sample code. This is > slightly better, but still leads to the situation where the failure > mode is an import failing, rather than a helpful message that points > at the model that you shouldn't be referencing. > > By using the swappable approach, a model reference is always exactly > the model that you've imported, unless you've got the reference > through a get_user_model() method call, and other validation > mechanisms work out whether that model reference is legal. At the end > of the day, these validation mechanisms aren't really that complex, > either. If you look at the additions to the validation logic, they're > essentially just "if f.rel.to.swapped, make noise". > > The only other complex part is the synchronisation code, but even that > is just layering on top of code that's already there. Abstract, proxy > and non-managed models all fall in the group of "importable, but not > synchronisable" models; swappable just adds one more to that list. > > There are two other lesser reasons that I've taken this approach. > > Firstly, I'm not wild about module level if statements. For me, a > module should be a container of logic, not an executable unit in > itself. I appreciate that there are sometimes valid reasons to make > module level code executable, and at some level a def statement is > just the execution of a declaration statement, but to my mind it's > worth keeping a clear distinction. A good practical example of why > this matters is testing -- module level if statements can only be > (easily) tested at time of import, so you really only get one chance > to test that code. > > Secondly, I don't like special cases, and a swappable Meta attribute > -- even if it's a stealth option -- means that the underlying > mechanism is generic, and doesn't require any User-specific references > anywhere. Again, testing is a good reason for why this matters. We can > test that the mechanics of swappable models work as expected with a > suite of non-User swapped/unswapped models. If the mechanism was User > specific, we'd only be able to test using User, but the swappability > would be determined as an import-time definition, so we'd only be able > to test one configuration per test run (unless we got into some messy > reload/app cache flushing). > > As a side effect, it also means that if we were to ever 'bless' > swappable models as an official feature, we just need to document what > we've already got. In the interim, we have a hidden feature that > adventurous members of the community can start playing with. As an > unofficial feature, we're not obligated to support or maintain it, but > it gives us a way to test out the waters to see whether the idea > should be encouraged as broader practice. > > This approach to API development has precedent in Django. For example, > shadow reverse attributes (i.e, related_name="+foo") existed as an > undocumented feature for some time before we decided that they weren't > actually harmful and added them as official API.
Understood & agreed (the "this model is dynamic made explicit" part seems really important specifically). I am afraid of the hard-coding of meta.swappable must be 'SOME_VAR' which then references settings.SOME_VAR, and that this is made part of the public API by the error messages. I would imagine that after app- loading refactor one would make the swappable an app-level constant, but the use of settings.AUTH_USER_MODEL directly in the code makes this change backwards incompatible. Any chance of making what the "swappable" contains more abstract? Something like suggesting making foreign keys point to auth_user_ref() instead of settings.AUTH_USER_MODEL. Another idea would be to change the "swappable" to contain a method reference, and then hint in the foreign key error message that "point to User._meta.swappable() instead". (Or, make that Model property, so that you could say to point to User.swapped_by instead, and User.swapped_by would contain the real reference by checking where User._meta.swappable points to). As for the User model as an ORM object - what lookups can be assumed to exist? It seems the only requirement is that it has a numeric primary key called 'id', otherwise there isn't anything that can be assumed of it? - Anssi -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
