--
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-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
For more options, visit https://groups.google.com/groups/opt_out.
Russell,I would love to do the work for the email-login analogue you describe. I actually proposed just such a thing a few months ago but was rebuffed.
However, I think this would be extremely useful. Also, I am, in fact, looking to get involved with Django development, as I haven't quite navigated the hurdles successfully.I do have one request, though. Is there a core developer that would be willing to "mentor" my work on this, so I can make sure I am writing something worthy of acceptance?
--You received this message because you are subscribed to a topic in the Google Groups "Django developers" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/django-developers/rb7v9kVAK3I/unsubscribe.
To unsubscribe from this group and all its topics, send an email to django-develop...@googlegroups.com.
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-develop...@googlegroups.com.
I added the authtools approach to the wiki for completion, although I believe it to be an inferior approach.One thing I dislike is having a separate app (e.g. d.c.auth_email) that has to be installed separately. That feels pretty impure to me. I'm doing a thought exercise about potential solutions, though, and not exactly coming up aces.The best solution I can currently think of is to have User and EmailUser which are both models that live in django.contrib.auth. Then, we would have to add code to our model detection that says that *if* a model is a subclass of AbstractBaseUser, include it if and only if it is the AUTH_USER_MODEL.I can't decide if that solution is better or worse than the disease. It makes for a much more attractive set of steps to follow for people who want to use it, though -- basically, just set AUTH_USER_MODEL to 'auth.EmailUser', and done.
Luke, I'm +1 on wanting a solution that allows "just set AUTH_USER_MODEL to 'auth.EmailUser', and done".It'd be nice if 'swappable' could accomplish this for us. Something along the lines of for any set of swappable models which are swappable for the same settings value, only load the one that is named by the settings value. I am taking some time to read the source surrounding this setting and model loading to even figure out if this is a possible modification. Russel, curious about your thoughts on if this approach would be something you would sign off on.
This seems like a good compromise over adding infrastructure for not installing certain models. This would instead create infrastructure to declare a certain 'type' of model must be present, but that you may decide which one based on a settings value.This approach feels cleaner to me because we don't want to 'turn off' a model, but rather only load a single 'User' model from some set of 'User' models. In the case of 'User', we need at least one 'User' model to be loaded, and this enforces that, where as the ability to turn off certain models could introduce situations in which someone turns off a model without replacing it.
On Thu, Sep 19, 2013 at 3:39 AM, Luke Sneeringer <lu...@sneeringer.com> wrote:
I added the authtools approach to the wiki for completion, although I believe it to be an inferior approach.One thing I dislike is having a separate app (e.g. d.c.auth_email) that has to be installed separately. That feels pretty impure to me. I'm doing a thought exercise about potential solutions, though, and not exactly coming up aces.The best solution I can currently think of is to have User and EmailUser which are both models that live in django.contrib.auth. Then, we would have to add code to our model detection that says that *if* a model is a subclass of AbstractBaseUser, include it if and only if it is the AUTH_USER_MODEL.I can't decide if that solution is better or worse than the disease. It makes for a much more attractive set of steps to follow for people who want to use it, though -- basically, just set AUTH_USER_MODEL to 'auth.EmailUser', and done.My opinion - it's worse than the disease.Option 1 involves a clean auth app that just contains a stub user, and a clean extension app providing an alternative user. You install the extension app, and say you want to use it.Option 2 makes a special case of *one particular* extension user, and makes all the internals of models, forms, views, etc embedded inside an if statement.
On Thu, Sep 19, 2013 at 8:37 AM, Aaron Merriam <aaronm...@gmail.com> wrote:
Luke, I'm +1 on wanting a solution that allows "just set AUTH_USER_MODEL to 'auth.EmailUser', and done".It'd be nice if 'swappable' could accomplish this for us. Something along the lines of for any set of swappable models which are swappable for the same settings value, only load the one that is named by the settings value. I am taking some time to read the source surrounding this setting and model loading to even figure out if this is a possible modification. Russel, curious about your thoughts on if this approach would be something you would sign off on.Can't say I'm wild about it. Explicit is better that implicit.
If we were going to go down the "single setting" model, I'd rather see it as "you put auth_email in your INSTALLED_APPS, and that automatically sets AUTH_USER_MODEL". However, that requires infrastructure that we don't have at the moment. See my permanent refrain to App Refactor :-) And even then, I'm not necessarily convinced it's the right way to go. I don't see 2 explicit settings to be especially onerous, or difficult to explain. One setting installs the app. The other tells Django which model in that app to use. They really are separate concerns.
The problem you've got here is how it knows to *not* install EmailUser. If it's a model defined in d.c.auth.models, it will get installed, irrespective of whether it is AUTH_USER_MODEL or not. This is where we have to special case the new model somehow so it is only installed if we are using it. This is far from straightforwards! By putting it in a separate app which is not installed by default, then we solve this issue trivially without more magic.
The problem you've got here is how it knows to *not* install EmailUser. If it's a model defined in d.c.auth.models, it will get installed, irrespective of whether it is AUTH_USER_MODEL or not.
I and my colleague (gavi...@gmail.com) have made some edits to the wiki to clarify the purpose of authtools, and more accurately explain what the 'authtools' approach would look like. If you previously have examined 'option 2', I would ask that you go and reread that section in the wiki.The problem you've got here is how it knows to *not* install EmailUser. If it's a model defined in d.c.auth.models, it will get installed, irrespective of whether it is AUTH_USER_MODEL or not.I wanted to verify this so I ran some basic tests and turns out that swappable already does the correct thing. If two models have the same swappable value, only the one referenced in settings will be used/installed. This makes it practical to include a second user model in d.c.auth.models without any changes to model loading or the swappable mechanism.
Whilst this works, I'm not sure it was the intention in the design of swappable. Clever though and it would allow the single-app approach.
However, we still have the issue of different forms, urls, views etc all based off the changed model. Making these change automatically would be horrible.
I still think a second app is a better plan.
If we are going to do this I think it is worth revisiting class basing the with views. A decent chunk of work was done on this at some point, and there are other implementations in the wild. It makes having the two separate versions much simpler in terms of swapping out form classes etc. One if the major issues was the lack of quality tests for some of the views.
Marc
--
I'm not sure it was the intention in the design of swappable.
However, we still have the issue of different forms, urls, views etc all based off the changed model.
Making these change automatically would be horrible.
I think you'll find that at least the Authentication form is wrong as well - at least in the sense that it calls the field username and it doesn't validate as an email. Once we get better at html5 fields as well, it won't be the correct input type.
Admin registration, the management commands, and the manager would also all need to be different.
I'd rather see auth get less magical and abstract, not more. I already find the existence of USERNAME_FIELD rather ugly. This whole approach is very implicit.
--
The problem you've got here is how it knows to *not* install EmailUser. If it's a model defined in d.c.auth.models, it will get installed, irrespective of whether it is AUTH_USER_MODEL or not. This is where we have to special case the new model somehow so it is only installed if we are using it. This is far from straightforwards! By putting it in a separate app which is not installed by default, then we solve this issue trivially without more magic.
On 19 Sep 2013 05:27, "Luke Sneeringer" <lu...@sneeringer.com> wrote:Bah, I should have planned my e-mailing better. Sorry for sending a third in such rapid succession.I just did some checking into my assumptions (quoted below) on this. It turns out that Django core already does some of what I suggest: in particular, it doesn't install the User model if django.contrib.auth is installed but another AUTH_USER_MODEL is invoked, based on a Meta option called "swappable" (see [here][1] and [here][2]).Am I misunderstanding? It seems like we are already pretty well down my proposed path.
However, if auth.User (or any other model for that matter) is set as AUTH_USER_MODEL, nothing prevents EmailUser from being installed.
Russell, my reply there isn't meant to be confrontational and yet re-reading it I can see it being easily interpreted as such.
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?
Isn't this a simpler approach? Does it have too much implications? I tested changing AUTH_USERNAME_FIELD on a sample project settings to email and it worked login to admin.I did some fast coding to show you what I'm thinking:Hi, I develop applications in Django and I started to read the developer mailing list and got interested by this topic about MailUser problem and I want to add my 50 cents, sorry if I'm taking a completely wrong approach, since I never developed the internals of Django.But what if we just add a AUTH_USERNAME_FIELD to django settings and with this modify the behaviour of User model?
https://github.com/loop0/django/compare/email_user
I like Jorge's idea to separate permissions into it's own app too.
Sorry if I wasn't supposed to post to this mailing group.
Hi all.I summarise the options with some of the issues raised, and I add my own concern.One option presented here is to have both models in d.c.auth. As pointed out by Russell and others, this causes the problem on how to define the models such that they are not installed if they are not being the AUTH_USER_MODEL. Assuming that this problem could be solved, the user's procedure would be:To use User (+ Permissions):install django.contrib.authTo use MailUser (+ Permissions):install django.contrib.authset AUTH_USER_MODEL to MailUserThe second option presented here is to add a separate app (e.g. d.c.mailuser), which would require some imports from django.contrib.auth (forms, views). The user's procedure would be:To use User (+ Permissions):install django.contrib.authTo use MailUser (+ Permissions):install django.contrib.authinstall django.contrib.mailauthset AUTH_USER_MODEL to MailUserIs this correct or I'm missing something important here?
If correct, I think the second option is cleaner, even though there is the issue to minimise the repetition of code for forms, views, which was pointed out to be easier to solve than the problem of model installation.What I want to emphasise here is that django.contrib.auth has to be installed in the case the custom model (email or other) is subclassed from PermissionsMixin.This is something that I'm bringing because it seems odd that d.c.auth has to be installed even if we want to use mailuser (or any user). Wouldn't be possible to detach permissions to a new app, say django.contrib.perms, and remove the step of installing d.c.auth? I mean, we are in authentication; permissions are not related with authentication.
But Django already has the mechanism to know not to install the model. It's the "swappable" Meta option. That is what causes User not to be installed if it is not used (contra my initial statement yesterday; I was flat wrong).Right now, User has a meta option:swappable = 'AUTH_USER_MODEL'...that does exactly what we are describing. It seems EmailUser (or whatever we call it) should just do this also.
> 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".Ah, this is were the misunderstanding was. authtools does actually set swappable on its replacement user, too[1]. The two definitions look like this:class User(Model):username = models.CharField()USERNAME_FIELD = 'username'class Meta:swappable = 'AUTH_USER_MODEL'class EmailUser(Model):email = models.CharField()USERNAME_FIELD = 'email'class Meta:swappable = 'AUTH_USER_MODEL'> How does Django identify that EmailUser *shouldn't* be synchronised to the database?The existing swappable mechanism takes care of it. Only the model referenced by settings.AUTH_USER_MODEL will be installed, not both.
> 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.It's straightforward to make the forms, views, and URLs work without checking what user model is installed, and this is the approach authtools takes. We can agree that code like `if settings.AUTH_USER_MODEL == auth.EmailUser"` is absolutely awful, so it's nice that it's not required to implement forms that work with custom user models. The forms from authtools will work with any user model, not just authtools.User and auth.User.
On Fri, Sep 20, 2013 at 10:53 AM, <gavi...@gmail.com> wrote:
> 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".
Ah, this is were the misunderstanding was. authtools does actually set swappable on its replacement user, too[1]. The two definitions look like this:class User(Model):username = models.CharField()USERNAME_FIELD = 'username'class Meta:swappable = 'AUTH_USER_MODEL'class EmailUser(Model):email = models.CharField()USERNAME_FIELD = 'email'class Meta:swappable = 'AUTH_USER_MODEL'
> How does Django identify that EmailUser *shouldn't* be synchronised to the database?
The existing swappable mechanism takes care of it. Only the model referenced by settings.AUTH_USER_MODEL will be installed, not both.
Ah. Thats… interesting. I'm moderately surprised it works at all. I'll need to dig in to the internals to work out *why* it works… swappable certainly wasn't intended to work like that. There might be some interesting land mines lying in wait.
Sent from my iPadOn Fri, Sep 20, 2013 at 10:53 AM, <gavi...@gmail.com> wrote:
> 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".Ah, this is were the misunderstanding was. authtools does actually set swappable on its replacement user, too[1]. The two definitions look like this:class User(Model):username = models.CharField()USERNAME_FIELD = 'username'class Meta:swappable = 'AUTH_USER_MODEL'class EmailUser(Model):email = models.CharField()USERNAME_FIELD = 'email'class Meta:swappable = 'AUTH_USER_MODEL'> How does Django identify that EmailUser *shouldn't* be synchronised to the database?The existing swappable mechanism takes care of it. Only the model referenced by settings.AUTH_USER_MODEL will be installed, not both.Ah. Thats… interesting. I'm moderately surprised it works at all. I'll need to dig in to the internals to work out *why* it works… swappable certainly wasn't intended to work like that. There might be some interesting land mines lying in wait.Aha, I think I found at least some of my disconnect. But I tested this yesterday, and it definitely does do that.
I know I just asked this in a previous email, but I will repeat for ease of others possibly following this thread: can you provide a brief overview of what swappable *does* do? It seems to be on observation it does exactly this. You say it's intended to do something else. What is that something else?
This is a continuation of my post on stackoverflow.With the introduction of Django 1.5, it was possible to create a custom User model which is flexible enough to have any user profile the developer wants created. However, looking at a very common problem which is using the email as the primary user identifier instead of username, the solution requires copying most of Django's internal definition of AbstractUser and that is only to remove the username field.A better solution in my opinion is make AbstractUser even more abstract by removing username field, and allowing the developer to explicitly specify the field to be used as the user identifier. This will require a tiny extra work for those that use the current default behavior, but it will also greatly reduce the work needed for the very common problem of using email as the user identifier.Please share your thoughts and opinions on this.
L
--
You received this message because you are subscribed to a topic in the Google Groups "Django developers" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/django-developers/rb7v9kVAK3I/unsubscribe.
To unsubscribe from this group and all its topics, send an email to django-develop...@googlegroups.com.
> The intention was to mark a particular model as a something that can be replaced.It's hard to find the original rationale behind swappable, but my mental model was always:A model with `swappable = 'x'` means that the model should only be loading if settings.x is set to that same model
> it's still not desirable.Why not? Swappable seems like exactly what we need here.> No other User model needs to [set swappable]This would still be the case. Only models that want to conditionally load themselves would set swappable. User models in application code probably wouldn't set it, because the project will always use that one user model.> ... made a special case of "Swappable models in the same app".I'm not sure where there's a special case. Swappable works for this without any modifications, see authtools.
> *Any* model can be swapped in as a substitute user.Yep. Nothing needs to change to keep this possible.> If we were to go down this path, the logical extension (to my mind) would be to validate that you're not swapping in a model that hasn't declared itself as swappableWhy would you want to validate this? Swappable only controls loading of the model its set on, there is no action-at-a-distance on other models.> Secondly, marking a model as swappable doesn't make it zero cost.That's fair. I'll add it to the wiki as a disadvantage.
> So far, the only convincing argument I've heard for putting the EmailUser in contrib.auth is that it avoids having to add a value to INSTALLED_APPS.I don't think we should focus so much on the optional part of the proposal. The really important part is to refactor the existing code to work better with custom users. Once this happens, projects like authtools can trivially add an EmailUser in a few lines of code, so it's not so important that there's one in core.
P. S. I'm not the sharpest knife in the drawer when it comes to social things, so I want to state explicitly: I am continuing to debate the question because I perceive the debate to be moving in a useful way. If I'm, in fact, simply being confrontational, then tell me to shut up and I'll just work on doing it your way / commenting on tanderegg's start. At the end of the day, I recognize that it is your decision to make, and at the end of the day, if I can't convince you, I'll accept it with a smile and get the work done! I definitely want to be offering a positive contribution.
Hi Luke -I just wanted to clarify the approach I'm using - The issue of whether or not the EmailUser is in contrib.auth or in a new app contrib.auth_email is a separate issue from code duplication.
We have two key questions here:1) Where does the new model live? Is it going to live in contrib.auth, or in a new contrib.email_auth package?2) Exactly what does the new class hierarchy look like? Are we going to do any refactoring of AbstractUser? How many composable bits are we going to end up with?
We've had a lot of discussion on question 1, which is risking becoming a bit of a bikeshed on the interpretation of `swappable`.What I haven't seen is a whole lot of clear discussion on point 2. Most of the discussion seems to stop at "use package/patch X", rather than a providing a good summary of the consequences.
This all hinges on the interpretation. My intention was *not* that swappable meant "This model might not be loaded". My intention *was* that swappable meant "This particular model is an extension point that might be replaced by a third party."
And this remains my biggest reason why the first interpretation isn't the right interpretation. We shouldn't be encouraging people to build monolithic apps containing dozens of "swappable" models. We should be encouraging people to write small, tight apps that fill a single purpose.
And, flipping it around -- I'm not trying to be confrontational either - also just looking out for the best interests of Django (as I see them).
- I believe that we should add an AbstractEmailUser as a subclass of AbstractBaseUser, and make AbstractUser subclass it.
- So, right now we have AbstractBaseUser -> AbstractUser -> User.
- My desired hierarchy would be:
- AbstractBaseUser
- AbstractEmailUser
- AbstractUser
- User
- EmailUser
- I assume this is mostly intuitive, but under my proposed hierarchy, everything that AbstractUser has now would be moved to AbstractEmailUser except for the username field. AbstractEmailUser would have USERNAME_FIELD set to 'email', and REQUIRED_FIELDS set to empty tuple. AbstractUser would have USERNAME_FIELD set to 'username' and REQUIRED_FIELDS set to ('email',).
- Both User and EmailUser would have the swappable Meta option set to 'AUTH_USER_MODEL'.
- I would then propose taking forms that hard-declare username as a field (e.g. UserCreationForm, but there are others) and instead determine their username field using the `fields` meta option in ModelForm.
- An example in the case of the UserCreationForm would be: fields = (model.USERNAME_FIELD, 'password1', 'password2')
- There's a potential land mine in here which is that the `clean_username` method is much less straightforward: we'd either have to determine the method name programmatically in class construction or get around the issue through double-defining it. I would favor the former because then the form is useful without modification to more third-party use cases. However, this is one of the (few, IMO) cases where there actually is a complexity hit, and I believe this problem should be noted as a disadvantage of my approach.
- I haven't looked at views as thoroughly, but I believe them to be more straightforward. I will do this dive soon.
- I believe that we should add an AbstractEmailUser as a subclass of AbstractBaseUser, and make AbstractUser subclass it.
- So, right now we have AbstractBaseUser -> AbstractUser -> User.
- My desired hierarchy would be:
- AbstractBaseUser
- AbstractEmailUser
- AbstractUser
- User
- EmailUser
- I assume this is mostly intuitive, but under my proposed hierarchy, everything that AbstractUser has now would be moved to AbstractEmailUser except for the username field. AbstractEmailUser would have USERNAME_FIELD set to 'email', and REQUIRED_FIELDS set to empty tuple. AbstractUser would have USERNAME_FIELD set to 'username' and REQUIRED_FIELDS set to ('email',).
- Both User and EmailUser would have the swappable Meta option set to 'AUTH_USER_MODEL'.
One thing to note here, is that AbstractEmailUser would have to define the email field with unique=True in order for it to be used as USERNAME_FIELD. This would mean that AbstractUser and User would fundamentally change so that email has to be unique. This could definitely wreck havoc with existing apps that don't have this requirement.One solution would be to have an intermediate class (AbstractNamedUser or something) that both AbstractEmailUser and AbstractUser inherit from, and each would define their own email fields. The other would be to stub out the email field (email = '') on AbstractEmailUser and actually implement it in each descendent class. I like the former approach myself, since it abstracts out name handling and would make it easier to create custom users that handle names differently. It might make sense to put AbstractEmailUser above AbstractNamedUser in the inheritance chain, and use the stubbing technique too.
- I would then propose taking forms that hard-declare username as a field (e.g. UserCreationForm, but there are others) and instead determine their username field using the `fields` meta option in ModelForm.
- An example in the case of the UserCreationForm would be: fields = (model.USERNAME_FIELD, 'password1', 'password2')
- There's a potential land mine in here which is that the `clean_username` method is much less straightforward: we'd either have to determine the method name programmatically in class construction or get around the issue through double-defining it. I would favor the former because then the form is useful without modification to more third-party use cases. However, this is one of the (few, IMO) cases where there actually is a complexity hit, and I believe this problem should be noted as a disadvantage of my approach.
The clean_username method is technically unnesseccary, it just provides a better error message than the ORM would. There might be a way to generalize this differently. The other issue is that username is hard coded into these forms as a regex validation. That could perhaps be moved to the AbstractUser class itself.
- I haven't looked at views as thoroughly, but I believe them to be more straightforward. I will do this dive soon.
In my experimentation, I haven't had to touch the views at all to make things work. I may have missed something though!The only other part that would have to change is UserAdmin, although it may end up being simpler just to require a custom Admin class for every custom user model, since it seems to me abstracting that would be tricky.
--
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-develop...@googlegroups.com.
Thanks for compiling a more complete solution.The biggest disadvantage as I see it is that is adds even more complexity to the implementation of those forms. In particular the `setattr` on the form class of a `clean_` method is a really nasty code smell. The code in the `create_user`/`create_superuser` commands is also a prime example of additional unnecessary complexity in a function which would be trivially avoided by separation.The authentication form renders a field called "username" which is a CharField, not an EmailField. This seems pretty bad to me.
Other things which appear to be bugs to me:- You automatically register an admin class for any custom user model, which is likely to break validation.
- If I set first_name and last_name as REQUIRED_FIELDS then your admin class will break validation or render a weird form as the field names appear twice.
The more we complexify the forms, the harder this is to do. Russell's original point about providing a complete implementation of "a custom user model" as an example is still very valid. Introducing an easier way to do say, email based auth, should not make writing a custom social based auth solution harder.
Now I admit there's not as much magic so far as I expected (though I expect it may get worse in the test suite). However I think the points regarding complexity still stand. From a maintenance perspective, changing this code in two places is bad, but always having to consider at least two (in your case actually many) distinct cases whenever changing the code is worse.
Russell, I definitely understand that you're still not sold on this method. I think that at this point, I've made my case, and if you're still not sold, I'm ready to coalesce the email_auth app implementations (two have been offered) and get that tip-top, tested, and documented.Thoughts?L
This is a problem that we ran into with authtools, what we ended up doing was just running the tests 3 times[1]. I don't think this is a realistic approach within Django core though. One of the main problems with this is that you use get_user_model at the root level of the forms module, so the Form will be declared against only one model--I don't know what you can do though. Having email_auth as a separate app would probably clear up this problem for the tests, but if that happens then we would just have to concrete specific sets of forms, instead of more generic Forms, which was the whole idea. The only situation where you should be switching settings around is in tests, so I do think it is a little unfair to compromise the generic nature of the forms just to appease the tests.
Good evening, Russell, et. al.,I had some time this afternoon. :-) Since there are already a couple of reference implementations for how to do this with an e-mail app, I decided to take a crack at an implementation that would include an EmailUser within the base auth app. I understand we've far from decided on a method, and I still stand by my promise to do whatever Russell decides, but I figured code to look at would be a good thing, and that it would give me a better idea of the task.Here's the branch: https://github.com/lukesneeringer/django/tree/ticket-20824-noemailappI have a few observations and self-criticisms:
- The big advantage, as I see it, to this approach over a separate-app approach is that it drastically reduces code duplication. Forms, the admin, etc., all use the same classes they did before, just those classes are now a bit more aware of what the USERNAME_FIELD and REQUIRED_FIELDS are. This is really the reason I believe this to be the correct approach. A separate app would have to be maintained, well, separately. :-)
- I have a few criticisms of my approach, now that I've had a chance to see it in action:
- The create_user and create_superuser methods on UserManager were a bit of a land mine. The current (1.6) implementation takes username, email, and password as positional arguments, and making this into an all-kwarg approach would break back-compat. I landed on a solution, but I'm not sure it's the right one.
- In order to maintain the form error message but not have the form itself be opinionated about the presence of "username", I removed the explicit validator on the form, simply letting ModelForm outsource it to the Model. This provides the same error message for both invalid and duplicate usernames / e-mail addresses (as appropriate). I think this actually might be a *good* thing (again, code duplication is reduced), but I don't really know if there's some reason why it was constructed the way it was before.
- There's a special message for duplicate usernames ("A user with that username already exists."), and it's not a trivial thing to just make an e-mail address correlate because of internationalization. I reverted to the previous message, which was translated ("Please correct the duplicate data for email, which must be unique."), because I really don't know what the appropriate procedure is here. (This would be an issue with a separate-app approach too, but it's on my list of "things I didn't know".)
- In starting to write tests (not committed), I hit an interesting situation where overwriting AUTH_USER_MODEL using override_settings may not be sufficient, which is an issue I would need to figure out. I expect this would not be a problem in an email_auth app, since the model wouldn't be loaded until after the override.
Russell, I definitely understand that you're still not sold on this method. I think that at this point, I've made my case, and if you're still not sold, I'm ready to coalesce the email_auth app implementations (two have been offered) and get that tip-top, tested, and documented.
Hi,I'm another one of the authtools devs.
On Tuesday, September 24, 2013 6:04:17 PM UTC-6, Luke Sneeringer wrote:
Good evening, Russell, et. al.,
This is a problem that we ran into with authtools, what we ended up doing was just running the tests 3 times[1]. I don't think this is a realistic approach within Django core though. One of the main problems with this is that you use get_user_model at the root level of the forms module, so the Form will be declared against only one model--I don't know what you can do though. Having email_auth as a separate app would probably clear up this problem for the tests, but if that happens then we would just have to concrete specific sets of forms, instead of more generic Forms, which was the whole idea. The only situation where you should be switching settings around is in tests, so I do think it is a little unfair to compromise the generic nature of the forms just to appease the tests.
Hi Rocky,On Thu, Sep 26, 2013 at 4:17 PM, Rocky Meza <rock...@gmail.com> wrote:
Hi,I'm another one of the authtools devs.On Tuesday, September 24, 2013 6:04:17 PM UTC-6, Luke Sneeringer wrote:Good evening, Russell, et. al.,This is a problem that we ran into with authtools, what we ended up doing was just running the tests 3 times[1]. I don't think this is a realistic approach within Django core though. One of the main problems with this is that you use get_user_model at the root level of the forms module, so the Form will be declared against only one model--I don't know what you can do though. Having email_auth as a separate app would probably clear up this problem for the tests, but if that happens then we would just have to concrete specific sets of forms, instead of more generic Forms, which was the whole idea. The only situation where you should be switching settings around is in tests, so I do think it is a little unfair to compromise the generic nature of the forms just to appease the tests.
Not only is it a realistic approach -- it is (within limits) what the current Django core already does. In order to validate that Django's pluggable user infrastructure actually does what it is supposed to do (i.e., let people with different user models use default auth views etc), Django's auth app ships 7 test User models, and large chunks of the test suite are run for each of those models. It's not a case of running the *entire* auth test suite repeatedly; it's matter of finding the parts of logic that are User model dependent, and running *those* tests repeatedly.
So - it's entirely possible and realistic - as long as you don't bind one specific User model into a global context. Which is one of the reasons why having a separate app helps :-)
Yours,Russ Magee %-)