Hi Aaron. On Fri, Sep 20, 2013 at 8:00 AM, Aaron Merriam <aaronmerr...@gmail.com>wrote:
> Russell, my reply there isn't meant to be confrontational and yet > re-reading it I can see it being easily interpreted as such. > No worries - I didn't read your response as confrontational, but I'll make sure I continue to not do so :-) > My intention is to ask whether there is something about swappable that I > am missing. It seems to work the way I stated above but your statement > seems to imply that their is some missing piece that needs extra mechanisms > surrounding model loading. So, am I missing something? > I think you are. Lets try via example. django.contrib.auth contains 2 User models -- User, and EmailUser. The (massively truncated) definitions for these models are: class User(Model): username = models.CharField() USERNAME_FIELD = 'username' class Meta: swappable = 'AUTH_USER_MODEL' class EmailUser(Model): email = models.CharField() USERNAME_FIELD = 'email' Note that EmailUser *doesn't* have a Meta: swappable definition. There is nothing on this model that says "I am swappable", or "I am an appropriate substitute for User". So - Lets look at two cases. Case 1: You set AUTH_USER_MODEL = 'auth.EmailModel'. You run syncdb. EmailUser is synchronised because it's a model in an app. User *isn't* synchronised because it's explicitly marked as being swappable, and the swappable attribute doesn't point at itself. Problem solved, right? We've got the right models syncronized! Well, no - we still have a problem -- We can't just say "contrib.auth.forms.AuthenticationForm", because this form needs to change depending on the model that is in use. We have similar problems with admin, and potentially with views and URLs. Case 2: The default case: I just want the default User model. I install django.contrib.auth in INSTALLED_APPS. I run syncdb. User is synchronised, as we expect. Question: How does Django identify that EmailUser *shouldn't* be synchronised to the database? EmailUser isn't mentioned in any setting (because AUTH_USER_MODEL == 'auth.User'). There's nothing on EmailUser that flags it as being a "User" model. It's in a models file with other models (Group, Permission) which *will* be synchronized. Absent of any additional information, EmailUser will be synchronised, because it looks like a normal model. Answer: We have 2 options. We need to modify contrib/auth/models.py to read: class User(Model): … if settings.AUTH_USER_MODEL == 'auth.EmailUser': class EmailUser(Model): …. and make the same changes in forms, admin, views, URLs etc. Alternatively, we need to add something to the definition of EmailUser that says "I'm a User Model. Don't sync me unless I'm active": class EmailUser(Model): email = models.CharField() class Meta: swappable_using = 'AUTH_USER_MODEL' And then we *still* need to do the "if settings.AUTH_USER_MODEL == auth.EmailUser" dance with views, forms, admin, etc. However, this also means: 1) We need to a bunch of internal plumbing in the app loader, Meta classes and so on to determine whether or not a model should be synchronised, and "hide" it in some way if it shouldn't be there. If you think this logic is simple, I've got a bridge to sell you :-) 2) We have to introduce a backwards incompatible change to pluggable models. At present you don't have to pre-declare "my model is a User model". All existing swappable User models in the wild need to be updated to be "1.7 compliant". 3) We introduce an implicit requirement for predeclaration. I need to know ahead of time that my model is going to be a User model. In the case of User, this probably isn't a huge impediment -- after all, I'm gonna know if I'm a User model. But looking longer term: the infrastructure for swappable Users isn't User specific. By design, swappable is a generic mechanism. You *could* use it as a limited substitute for generic foreign keys (e.g., a comments model pointing at a specific content type). But now you have to pre-declare that my model "can be swapped for X" -- which means that as a model designer, you need to know all the models that you *might* be swapped out as. Consider the comments example -- I want people to be able to comment on Users. But I can't, because I haven't pre-declared User to be "swappable as" a content object for comments. Predeclaration imposes a restriction on one side of the swappable agreement that isn't always under your control. The alternative -- we put EmailUser in a separate app. That app contains it's own forms, admin, views and URLs. No extra Meta attributes are required. No special "if" logic is required internal to models, views, forms, admin, etc. It's completely backwards compatible. It doesn't impose any pre-declaration restrictions. And it requires no modifications to the app loading internals. The only overhead - you have to declare an extra app in INSTALLED_APPS -- which is hardly surprising to me, given that it's being *distributed* as an app. Does that clarify the problem for you? Yours, Russ Magee %-) -- You received this message because you are subscribed to the Google Groups "Django developers" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-developers+unsubscr...@googlegroups.com. To post to this group, send email to django-developers@googlegroups.com. Visit this group at http://groups.google.com/group/django-developers. For more options, visit https://groups.google.com/groups/opt_out.