On Fri, Mar 9, 2012 at 10:36 AM, Luke Plant <l.plant...@cantab.net> wrote:
What you are really saying is this: being pragmatic means that we > prioritise *your* immediate need above the need to keep the code and the > docs maintainable, and above the need to maintain compatibility with > existing installations. > Of course not. Firstly, this is not *my* need; it's a feature that many have requested. Take a survey of the web applications that you use on a weekly basis. How many of them allow you to sign in with an email address? Compared to that, how many required you to invent a username? Of those, I would wager that a good many are Django apps. Secondly, I'm advocating for a backwards-compatible solution that won't break existing installations. That point keeps getting missed, as you and others keep assuming that we need schema migrations for this change—we don't. People who are happy with the existing user model can keep using it, as-is. But people who aren't happy with it should be able to provide their own. > There are a million-and-one hacks we would add if we took that approach, > and Django would have a million-and-one more bugs, or possibly much > worse - if you add just 10 boolean switches like the one you suggested > in an earlier email, you've got over 1000 combinations to test and > debug. I was giving an example of a pragmatic solution: sometimes an `if` statement is better than a far-reaching architectural change. I don't advocate boolean flags for solving the auth.User problem, I'm merely trying to encourage a shift in thinking from lofty aspiration to humble accomplishment. > I believe that all the problems you have can be solved already: > > - you can write your own authentication backend to authenticate > by email > True. > - you can write your own login and account creation views > True. > - you can even override admin views by inserting items into > urls.py > True. But these things aren't the problem. It's the auth.User model that poses the challenge. If you haven't implemented email-based authentication in your own apps, let me suggest that you try it before you tell me how easy it is. At some point, you will run into these limitations posed by the User model: - The username field can't be used to store emails (it's too short and restrictive). No problem, let's use the email field; - But the username field is required. OK, we can just fill it with a random string; - The email field does not have a unique constraint. But we can do a reasonably good job applying the constraint in python; - The email field is not indexed. We don't want full table scans whenever someone logs in, so let's write a custom (and possibly non-portable) SQL migration to add an index. All this, and the email field is still too short to hold all valid email addresses. On top of that, we've littered our database and admin interface with random strings for usernames, and littered our application with the code to generate them. Yes, these are all more-or-less hacky. And they have problems. For > instance, what if you have two accounts with the same email address? (I > have that in some of my Django projects, BTW). The current code allows > it, so we can't change anything that would make it impossible, at either > the DB level or the Python level. This is the kind of issue that means > you can't just use the existing models. > Of course, which is why we need to allow developers to supply their own User model, while maintaining backwards compatibility for those who are happy with the built-in model. A full and correct solution that didn't introduce dozens of new bugs is > quite hard - and I'm talking about just the code required to fix this > one feature for you, without doing anything more generally useful. > Again, it's not just for me. And, yes, I appreciate that it's not a trivial problem. It's hard, but all the problems worth solving are. That is why we're not going to put hacks like this in core - we would > have to support these hacks for at least 3 versions if we did. We are > interested in *removing* existing hacks from our code base, and this is > vital if Django is going to see increases in features and functionality. > We are not interested in adding more hacks. > I can appreciate the appeal of a pristine, hack-free code base, but I've never seen one. Eventually reality always trumps grand design. If my example of the `if` statement above has led you to believe that I'm proposing a mindless hack, then I apologize. I'm proposing something between a mindless hack and grand design. Something pragmatic. Some examples from a quick Google groups search for "auth User": > > http://goo.gl/swTpr Some hand waving ("It's deceptively difficult"), with no examples, and no mention of #3011. http://goo.gl/fFlKh This is someone with mediocre design skills trying to get paid to hack on Django over the summer, but failing to convince anyone that he has a clue about how to solve this problem. Save for some humorous jibes from Russell, the thread is almost devoid of good content, except for Russell's example of the COMMENTS_APP setting, which sounds a lot like "the idea of a 'pluggable' User model"—a proposal that the GSoC page indicates has already been rejected, but without explanation. > Ticket #3011 was rejected without much of a > > reason. HM asked for an explanation both in the ticket itself and on > > django-dev; no explanation was ever given. > > I don't know what thread you are talking about. Hanne Moa brings up the > subject here and gets two replies from Russell: > > http://goo.gl/7p1JN I was thinking of this thread: http://goo.gl/nehrO wherein Carl Meyer (I confused CM with HM) enquires about #3011 and receives no reply. In the thread you mention, there isn't any detail about #3011, other than that the patch it contains is "*completely* the wrong approach." > The GSoC page > > (https://code.djangoproject.com/wiki/SummerOfCode2011#Enhancedauth.user) > > is a frustrating read. It goes on and on about how hard the problem is > > and how wrong your solution is, but doesn't provide any detail as to why > > it's hard or why it's wrong. > > It does provide detail - it gives a list of issues you have to consider, > and tells you where to search for the other issues. > It poses a list of questions (how can we represent a generic user? how should we approach authentication vs. authorization? how do we handle migrations?) that all presuppose a grand redesign of auth.User instead of an incremental improvement, but it doesn't discuss at all why an incremental improvement won't be considered. And then it refers would-be implementers to read up on Lazy Foreign Keys (LFKs), which I contend are neither sufficient nor necessary for an auth.User refactor. To illustrate this last point, consider some of the compatibility problems we're liable to face when replacing django.contrib.auth.models.User: 1. Parts of Django, numerous third-party apps, and countless bespoke software installations have models with FKs to User. LFKs could help here, in that we could delay binding to the precise model until that binding is known (through configuration, or whatever). But if, to use an LFK, any of the third-party apps or bespoke software has to be changed (say, from `ForeignKey(User)` to `LazyForeignKey('User')` or `ForeignKey(LazyModel('User'))`), then LFKs are not backwards compatible. They may be the place we want to end up, but they're not the place to start, as they require too much change in software we don't control. 2. Parts of Django, numerous third-party apps, and countless bespoke software installations have ModelForms built around the User model. The model is specified in the ModelForm's Meta class, and must be available at class definition time. LFKs don't help here. We could extend ModelForm to support delayed binding, but if that involves changing any existing software (say, `model = LazyModel('User')`), then, again, we've broken backwards compatibility. 3. Parts of Django, numerous third-party apps, and countless bespoke software installations import User from django.contrib.auth.models. But we're talking about allowing developers to specify their own User model, so code that imports d.c.a.m.User would no longer be valid. LFKs don't help here, either. What's needed is a way to maintain the interface defined by the existing django.contrib.auth application (a User model in django.contrib.auth.models, an AuthenticationForm in django.contrib.auth.forms, a ModelBackend in django.contrib.auth.backends, etc) while allowing the developer to specify the implementation—whether the stock User app, a reusable third-party app, or their own. I've written such a pluggable User app. By default, the User model works as before. But I've also provided a User model (and forms, admin, backends, etc) implementation that removes the limitations of the User model noted above and allows for email authentication. It's still a bit rough around the edges, but it works, insofar that `manage.py createsuperuser` doesn't ask for a username, `manage.py changepassword` takes an email address, and I can login to the admin interface with my email address. It works for me, and that's well and good since I have a client that needs this functionality, but I don't want to maintain the fork forever. I would like to provide a patch for consideration for trunk, but before I waste my time and yours, I'd like to understand why the notion of pluggable user models was rejected (as indicated on the GSoC page), when Russell seems to be advocating for a design of that sort with his reference to the COMMENTS_APP setting. And to be clear, I don't consider the pluggable user model approach that I'm taking and the Lazy Foreign Key approach to be mutually exclusive. Rather, I see my approach as something of a compatibility shim until sufficient time has elapsed to pull the plug on imports of django.contrib.auth.models.User. I propose that we continue to support the existing auth interface (defined more completely above, but for brevity, a User model at django.contrib.auth.models) but start raising a deprecation warning in the 1.4.1+ timeframe. Meanwhile, we introduce LFKs (which I have ideas about, but which, again, are not required for an incremental improvement to auth.User), so that maintainers of reusable apps can start to take advantage of delayed binding. At some point, say, 1.5, or 2.0, we drop support for the existing auth interface (which is to say, apps should not expect to find a User model at django.contrib.auth.models) and the LFK becomes the "one—and preferably only one—obvious way to do it." Cheers, Clay -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.