views:

181

answers:

3

I'm working on a project in django which calls for having separate groups of users in their own username namespace.

So for example, I might have multiple "organizations", and username should only have to be unique within that organization.

I know I can do this by using another model that contains a username/organization id, but that still leaves this useless (and required) field on the defualt django auth User that I would have to populate with something.

I've already written by own auth backend that authenticates a user against LDAP. However, as I mentioned before, I am still stuck with the problem of how to populate / ignore the username field on the default django user.

Is there a way to drop the uniqueness constraint for the username for Django auth users?

+1  A: 

I'm not sure if this is exactly what you're looking for, but I think you could use a hack similar to what is in this answer.

The following code works, as long as it is in a place that gets executed when Django loads your models.

from django.contrib.auth.models import User

User._meta.get_field('username')._unique = False

Note that this won't change the database unique constraint on the auth_user table if it has been already been created. Therefore you need to make this change before you run syncdb. Alternatively, if you don't want to recreate your auth_user table, you could make this change and then manually alter the database table to remove the constraint.

Monika Sulik
I'm not sure that's going to work. The `unique` attribute is propagated to the database as a constraint on that field. Once that's done, tweaking Django meta info isn't going to help. If you're never going to actually *use* (i.e. display/modify) the username, then anything to make it unique will suffice. E.g. what DrBloodmoney suggests.
Peter Rowell
This *does* work (I tried it), but since it's accessing `_meta`, it's relying on the internal implementation and not the documented interface, so I'm wary to use it. +1 though, it does work.
TM
Well, it works on NOT NULL constraints (I've tried that), so I don't see why not. As long as "MyUserModel" is a class which inherits from "auth.User" and the line is put in straight after the model, this seems to work. It's just that it's a slightly ugly hack.
Monika Sulik
This actually works just fine without subclassing `auth.User`. You can just do `User._meta.get_field('username').unique = False`. It does work, as long as it exists in a file that gets loaded when Django loads your models. I'm accepting this answer, since it's functional, but I still feel like it's not a very good solution, as it may break with updates to Django (it relies on undocumented internal functionality).
TM
If this works, I need someone to explain it to me. There are (at least) 2 problems: 1) the field is `_unique`, not `unique`. (At least in 1.1.1 this is true. In 0.97pre it was `unique`.) 2) Regardless of the name, setting the `_unique` field makes no change to the *database enforced constraint* that the field is *unique*, a constraint that was created at syncdb time and which does not disappear by setting this field. I await enlightenment. Truly, I do.
Peter Rowell
@TM Oh, that's interesting... I didn't think it would work on the original User model, but that's good to know... Yeah, if you ever find a way to do this sort of thing without resorting to ugly hacks, I'm interested too!
Monika Sulik
@Peter Rowell I don't know about 1), but in terms of 2) now that I've started pondering the question and TM has said this works in more contexts than I thought, my guess is that django has to know all the models from the project before executing the CREATE TABLE sql because they need to be executed in the correct order (foreign keys). Therefore, I suppose the meta data is still possible to change even after the model declaration because the sql isn't formed until all the models are declared... Just a guess though.
Monika Sulik
@PeterRowell, yes you are correct, it has to be `_unique` not unique. My mistake. Sadly, I can't edit that comment anymore. As for issue #2: you need to make this change *before* you run syncdb to create the auth_user table. If you do this, the database constraint will not be created. Alternatively, if you already have run syncdb, you can alter the table manually. @Monika, it would be good if you could update your answer to use `_unique` and do the change directly on `User`.
TM
Sometimes I forget I can edit other peoples answers myself... I updated this answer to make it more accurate/clear, in order to address PeterRowells concerns.
TM
Lol thanks, I learnt quite a bit answering this :)
Monika Sulik
A: 

I have not personally been required to find a solution to this, but one way to tackle this (from an SAAS perspective) would be to prefix the username with an organizational identifier (presuming unique organizations). For example: subdomain.yoursite.com would equate to a user with the username: subdomain_username. You would just have to code some business logic on login to a subdomain to tack that onto the username.

DrBloodmoney
Yes, this works, and I considered, but the username is already only 30 characters, and adding the prefix quickly reduces the size to something that is unusable.
TM
A: 

What you can do is extend the User model. For the User table, generate a username (e.g. A_123, A_345) that will not be displayed at all in the site.

Then create a new model that extends User.

class AppUser(User):

username = models.CharField...
organization = models.CharField...

You then create a custom authentication backend that use AppUser instead of the User object.

dannyroa