web-dev-qa-db-fra.com

Le nom de l'utilisateur auth_user.username de Django peut-il être varchar (75)? Comment cela pourrait-il être fait?

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.

63
Purrell

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:

  • Vous voudrez peut-être aussi changer le help_text du champ, pour refléter la nouvelle longueur maximale
  • Si vous souhaitez utiliser l’administrateur automatique, vous devez sous-classer UserChangeForm, 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
78
Clément

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

25
skoczen

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()
20
Rost

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))
5
Cerin

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

2
Dmytriy Voloshyn

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 @. Il ne suffit donc pas de modifier la longueur du champ. Mon mauvais, on dirait qu'il gère ça. Voici le champ de nom d'utilisateur de models.py dans Django.contrib.auth:

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.

1
Bartek

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:

  1. déposer la table auth_user
  2. changer le nom d'utilisateur en max_length = 75 dans le modèle
  3. syncdb

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:

  1. sauvegarder les données de la table utilisateur
  2. laisse tomber la table
  3. syncdb
  4. réimporter les données utilisateur dans la nouvelle table; en prenant soin de restaurer les valeurs id d'origine

Sinon, à l’aide de votre skillz python-Django fou, copiez les instances de modèle utilisateur d’ancienne à nouvelle et remplacez:

  1. créez votre modèle personnalisé et placez-le temporairement à côté du modèle par défaut
  2. écrire un script qui copie les instances du modèle par défaut dans le nouveau modèle
  3. remplace le modèle par défaut par votre modèle personnalisé

Ce dernier n’est pas aussi difficile que cela en a l'air, mais implique évidemment un peu plus de travail.

1
John Mee

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.

1
Greg Glockner

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
0
xiaowl