Y at-il un problème avec l'exécution de la table de modification sur auth_user
pour que username
soit varchar(75)
afin qu'il puisse contenir un courrier électronique? Qu'est-ce que ça casse s'il y a quelque chose?
Si vous deviez changer auth_user.username
pour être varchar(75)
, où auriez-vous besoin de modifier Django? Est-ce simplement une question de changer 30 à 75 dans le code source?
username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
Ou y a-t-il une autre validation sur ce champ qui devrait être modifiée ou toute autre conséquence?
Voir ci-dessous la discussion avec Bartek sur la raison de le faire.
Edit : Je repense à cela après plusieurs mois. Pour les personnes qui ne connaissent pas le principe: certaines applications ne sont pas obligées ou désirent utiliser un nom d'utilisateur, elles n'utilisent que le courrier électronique pour l'enregistrement et l'authentification. Malheureusement, dans Django auth.contrib, un nom d'utilisateur est requis. Vous pouvez commencer à mettre des courriels dans le champ nom d'utilisateur, mais ce champ ne contient que 30 caractères et les courriels peuvent être longs dans le monde réel. Peut-être même plus long que les 75 caractères suggérés ici, mais 75 caractères sont compatibles avec la plupart des adresses électroniques saines. La question s'adresse à cette situation, telle que rencontrée par les applications basées sur email-auth.
Il existe un moyen d'y parvenir sans toucher au modèle de base, ni à l'héritage, mais c'est définitivement du piratage et je l'utiliserais avec beaucoup de précautions.
Si vous consultez la documentation de Django sur les signaux , vous verrez qu'il existe un document appelé class_prepared
, qui est en principe envoyé dès qu'une classe de modèle réelle a été créée par la métaclasse. Ce moment est votre dernière chance de modifier un modèle avant qu'un magic ait lieu (c'est-à-dire: ModelForm
, ModelAdmin
, syncdb
, etc ...).
Donc, le plan est simple, il vous suffit d'enregistrer ce signal avec un gestionnaire qui détectera le moment où il sera appelé pour le modèle User
, puis vous modifierez la propriété max_length
du champ username
.
Maintenant la question est, où ce code devrait-il vivre? Il doit être exécuté avant que le modèle User
ne soit chargé, ce qui signifie souvent que très tôt. Malheureusement, vous ne pouvez pas (Django 1.1.1, ne pas vérifier avec une autre version) mettre cela dans settings
car importer signals
entraînera des problèmes.
Un meilleur choix serait de le mettre dans le module de modèles d'une application factice et de placer cette application au-dessus de la liste INSTALLED_APPS
/Tuple (afin qu'elle soit importée avant toute autre chose). Voici un exemple de ce que vous pouvez avoir dans myhackishfix_app/models.py
:
from Django.db.models.signals import class_prepared
def longer_username(sender, *args, **kwargs):
# You can't just do `if sender == Django.contrib.auth.models.User`
# because you would have to import the model
# You have to test using __and __module__
if sender.__== "User" and sender.__module__ == "Django.contrib.auth.models":
sender._meta.get_field("username").max_length = 75
class_prepared.connect(longer_username)
Cela fera l'affaire.
Quelques notes cependant:
help_text
du champ, pour refléter la nouvelle longueur maximaleUserChangeForm
, UserCreationForm
et AuthenticationForm
car la longueur maximale n’est pas déduite du champ de modèle, mais directement dans la déclaration du champ de formulaire.Si vous utilisez South , vous pouvez créer la migration suivante pour modifier la colonne dans la base de données sous-jacente:
import datetime
from south.db import db
from south.v2 import SchemaMigration
from Django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Changing field 'User.username'
db.alter_column('auth_user', 'username', models.CharField(max_length=75))
def backwards(self, orm):
# Changing field 'User.username'
db.alter_column('auth_user', 'username', models.CharField(max_length=35))
models = {
# ... Copy the remainder of the file from the previous migration, being sure
# to change the value for auth.user / usename / maxlength
Sur la base de l'excellente réponse combinée de Clément et Matt Miller ci-dessus, j'ai créé une application rapide qui la met en œuvre. Pip installer, migrer, et c'est parti. Je mettrais cela comme un commentaire, mais vous n'avez pas encore le crédit!
https://github.com/GoodCloud/Django-longer-username
EDIT 2014-12-08
Le module ci-dessus est maintenant obsolète en faveur de https://github.com/madssj/Django-longer-username-and-email
Solution mise à jour pour la version Django 1.3 (sans modifier manage.py):
Créer une nouvelle application Django:
monkey_patch/
__init__.py
models.py
Installez-le en tant que premier: (settings.py)
INSTALLED_APPS = (
'monkey_patch',
#...
)
Voici models.py:
from Django.contrib.auth.models import User
from Django.core.validators import MaxLengthValidator
NEW_USERNAME_LENGTH = 300
def monkey_patch_username():
username = User._meta.get_field("username")
username.max_length = NEW_USERNAME_LENGTH
for v in username.validators:
if isinstance(v, MaxLengthValidator):
v.limit_value = NEW_USERNAME_LENGTH
monkey_patch_username()
Les solutions ci-dessus semblent mettre à jour la longueur du modèle. Cependant, pour refléter votre longueur personnalisée dans admin, vous devez également remplacer les formulaires admin (frustrant, ils n'héritent pas simplement de la longueur du modèle).
from Django.contrib.auth.forms import UserChangeForm, UserCreationForm
UserChangeForm.base_fields['username'].max_length = NEW_USERNAME_LENGTH
UserChangeForm.base_fields['username'].widget.attrs['maxlength'] = NEW_USERNAME_LENGTH
UserChangeForm.base_fields['username'].validators[0].limit_value = NEW_USERNAME_LENGTH
UserChangeForm.base_fields['username'].help_text = UserChangeForm.base_fields['username'].help_text.replace('30', str(NEW_USERNAME_LENGTH))
UserCreationForm.base_fields['username'].max_length = NEW_USERNAME_LENGTH
UserCreationForm.base_fields['username'].widget.attrs['maxlength'] = NEW_USERNAME_LENGTH
UserCreationForm.base_fields['username'].validators[0].limit_value = NEW_USERNAME_LENGTH
UserCreationForm.base_fields['username'].help_text = UserChangeForm.base_fields['username'].help_text.replace('30', str(NEW_USERNAME_LENGTH))
Autant que je sache, on peut remplacer le modèle utilisateur depuis Django 1.5, ce qui résoudra un problème. Exemple simple ici
Si vous modifiez simplement la table de base de données, vous devrez toujours gérer la validation de Django. Par conséquent, cela ne vous laissera pas en créer un de plus de 30 caractères. De plus, le nom d'utilisateur est validé, de sorte qu'il ne puisse pas contenir de caractères spéciaux tels que Mon mauvais, on dirait qu'il gère ça. Voici le champ de nom d'utilisateur de models.py dans Django.contrib.auth:@
. Il ne suffit donc pas de modifier la longueur du champ.
username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
La création d'authentification de messagerie n'est pas difficile. Voici un backend d'authentification super simple vous pouvez utiliser. Il vous suffit ensuite d’ajouter une validation pour vous assurer que l’adresse e-mail est unique et que vous avez terminé. C'est facile.
Oui, ça peut se faire. Au moins, je pense que cela devrait marcher. J'ai fini par remplacer le modèle entier, alors je suis prêt à être corrigé si cela ne fonctionne pas ...
Si vous n'avez pas d'enregistrements d'utilisateurs qui vous intéressent:
Si vous avez des enregistrements d'utilisateurs que vous devez conserver, c'est plus compliqué car vous devez les migrer d'une manière ou d'une autre. Le plus simple est la sauvegarde et la restauration des données de l'ancienne à la nouvelle table, par exemple:
Sinon, à l’aide de votre skillz python-Django fou, copiez les instances de modèle utilisateur d’ancienne à nouvelle et remplacez:
Ce dernier n’est pas aussi difficile que cela en a l'air, mais implique évidemment un peu plus de travail.
Fondamentalement, le problème est que certaines personnes souhaitent utiliser une adresse e-mail comme identifiant unique, alors que le système d'authentification d'utilisateur de Django requiert un nom d'utilisateur unique d'au plus 30 caractères. Cela changera peut-être à l'avenir, mais c'est le cas de Django 1.3 au moment où j'écris.
Nous savons que 30 caractères est trop court pour de nombreuses adresses électroniques; Même 75 caractères ne sont pas suffisants pour représenter certaines adresses électroniques, comme expliqué dans Quelle est la longueur optimale pour une adresse électronique dans une base de données? .
J'aime les solutions simples, je recommande donc de hacher l'adresse email en un nom d'utilisateur qui correspond aux restrictions pour les noms d'utilisateur dans Django. Selon L'authentification de l'utilisateur dans Django , un nom d'utilisateur doit comporter au maximum 30 caractères, composés de caractères alphanumériques et de _, @, +,. et -. Ainsi, si nous utilisons un codage en base 64 avec une substitution minutieuse des caractères spéciaux, nous avons jusqu'à 180 bits. Nous pouvons donc utiliser une fonction de hachage de 160 bits comme SHA-1 comme suit:
import hashlib
import base64
def hash_user(email_address):
"""Create a username from an email address"""
hash = hashlib.sha1(email_address).digest()
return base64.b64encode(hash, '_.').replace('=', '')
En bref, cette fonction associe un nom d'utilisateur à une adresse électronique. Je suis conscient qu'il existe une faible probabilité de collision dans la fonction de hachage, mais cela ne devrait pas être un problème dans la plupart des applications.
C: ...\venv\Lib\site-packages\Django\contrib\auth\models.py
first_name = models.CharField (_ ('prénom'), max_length = 30, blank = True)
changer à
first_name = models.CharField (_ ('prénom'), max_length = 75, blank = True)
enregistrer
et changement dans la base de données
Ajoutez simplement le code ci-dessous au bas de settings.py
from Django.contrib.auth.models import User
User._meta.get_field("username").max_length = 75