2010-04-09 17 views
63

¿Hay algún problema con ejecutar alter table en auth_user para hacer que username sea varchar(75) para que quepa en un correo electrónico? ¿Qué rompe eso si algo?¿Puede auth_user.username de django ser varchar (75)? ¿Cómo podría hacerse eso?

Si tuviera que cambiar auth_user.username para ser varchar(75) ¿dónde tendría que modificar django? ¿Es simplemente una cuestión de cambiar de 30 a 75 en el código fuente?

username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters")) 

¿O hay alguna otra validación en este campo que deba cambiarse o alguna otra repercusión para hacerlo?

Vea la discusión del comentario con bartek a continuación sobre la razón para hacerlo.

Editar: Mirando hacia atrás en esto después de muchos meses. Para cualquier persona que no conozca la premisa: algunas aplicaciones no tienen un requisito o deseo de usar un nombre de usuario, solo usan el correo electrónico para el registro & auth. Desafortunadamente en django auth.contrib, se requiere nombre de usuario. Podría comenzar a colocar correos electrónicos en el campo de nombre de usuario, pero el campo tiene solo 30 caracteres y los correos electrónicos pueden ser largos en el mundo real. Potencialmente incluso más largo que el 75 char sugerido aquí, pero 75 char acomoda la mayoría de las direcciones de correo electrónico. La pregunta está dirigida a esta situación, como lo hacen las aplicaciones basadas en autenticación de correo electrónico.

+3

antes de la edición no era una pregunta por una larga queja sobre 'auth.contrib'. Lo editaste para que fuera más civil, pero SO afirma que "el voto es demasiado viejo para cambiar". –

+0

Lo edité para ser más conciso, me molesta su implicación de que era menos civil (?). Estaba explicando la lógica para preguntar sobre auth_user.username = varchar (75). Es una situación en la que veo a muchas otras personas, por lo que vale la pena explicarlo. Pero no si hace que la gente no se tome el tiempo para responder a la pregunta. – Purrell

+4

Nota para la posteridad: Django 1.5 tiene algunas características que resuelven estos problemas de forma más elegante. Ver http://procrastinatingdev.com/django/using-configurable-user-models-in-django-1-5/ –

Respuesta

77

Hay una manera de lograr eso sin tocar el modelo principal, y sin herencia, pero definitivamente es hackish y lo usaría con mucho cuidado.

Si mira el documento de Django on signals, verá que hay uno llamado class_prepared, que se envía básicamente una vez que la metaclass haya creado cualquier clase de modelo real. Ese momento es su última oportunidad de modificar cualquier modelo antes de que se lleve a cabo magic (es decir, ModelForm, ModelAdmin, syncdb, etc.).

Así que el plan es simple, sólo se registra la señal con un controlador que detecta cuando se le llama para el modelo User, y luego cambia la propiedad max_length del campo username.

Ahora la pregunta es, ¿dónde debería vivir este código? Tiene que ejecutarse antes de que se cargue el modelo User, por lo que a menudo significa muy temprano. Lamentablemente, no puede (django 1.1.1, no ha verificado con otra versión) poner eso en settings porque al importar signals se romperán las cosas.

Una mejor opción sería la de poner en módulo de los modelos de una aplicación simulada, y poner esa aplicación en la parte superior de la lista INSTALLED_APPS/tupla (por lo que se importa antes que nada). Aquí está un ejemplo de lo que puede tener en 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 __name__ and __module__ 
    if sender.__name__ == "User" and sender.__module__ == "django.contrib.auth.models": 
     sender._meta.get_field("username").max_length = 75 

class_prepared.connect(longer_username) 

que va a hacer el truco.

Unas pocas notas aunque:

  • Es posible que desee cambiar también el help_text del campo, para reflejar la nueva longitud máxima
  • Si desea utilizar el administrador automático, tendrá que subclase UserChangeForm, UserCreationForm y AuthenticationForm ya que la longitud máxima no se deduce del campo de modelo, sino directamente en la declaración del campo de formulario.

Si está utilizando South, puede crear la siguiente migración a cambiar la columna en la base de datos subyacente:

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

Solución interesante, puedo intentar esto. Una nota para cualquiera que se encuentre con esta publicación que quiera probar esto y también esté usando mysql: con esta solución, ya que el campo todavía es varchar (30) en el db (a menos que ejecute alter table para modificar el nombre de usuario para ser varchar (75)), esto solo funcionará si mysql se ejecuta en modo 'estricto': http://dev.mysql.com/doc/refman/5.0/en/char.html; de lo contrario, los valores superiores a 30 se truncarán en el db – Purrell

+1

@perrierism: sí, tiene que 'alterar la tabla' usted mismo ya que django no lo hará por usted (puede que quiera probar [south] (http://south.aeracode.org/) para eso) . Pero un 'syncdb' desde cero creará el campo con el tipo correcto (varchar (75)). –

+4

He sugerido las ediciones de la respuesta para incluir la migración para South, ya que me tomó un poco de investigación hacer ejercicio y es demasiado tiempo para hacer un comentario. –

1

Si simplemente modifica la tabla de la base de datos, igual tendrá que lidiar con la validación de Django, por lo que no le permitirá crear más de 30 caracteres de todos modos. Además, el nombre de usuario valida para que no pueda tener caracteres especiales como @ por lo que simplemente modificar la longitud del campo no funcionaría de todos modos. Mi mal, parece que maneja eso. Aquí está el campo de nombre de usuario de models.py en django.contrib.auth:

username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters")) 

Creación de autenticación de correo electrónico no es difícil. Aquí hay un super simple email auth backend que puede usar. Todo lo que debe hacer después de eso es agregar algo de validación para asegurarse de que la dirección de correo electrónico sea única y listo. Así de fácil.

+0

No del todo. ¿Qué haces con el campo de nombre de usuario? Todavía es una clave principal. – Purrell

+0

También te equivocas con los nombres de usuario de inicio de sesión @. Sí permite @. De hecho, no puedo ver dónde aplica <30 en el nombre de usuario que mira el código. No digo que no esté allí pero, ¿estás seguro de que está allí? – Purrell

+0

No es una clave principal. El nombre de usuario es fácil. Usted tiene su correo electrónico y usted y una clave principal de la base de datos, tal vez su apellido también. Ah, también tienes un generador de números aleatorios integrado en Python ... y demás. Simplemente haz que el nombre de usuario sea único. En una de mis aplicaciones acabo de usar la primera parte del correo electrónico más su identificación de usuario. Simple para aplicaciones básicas. – Bartek

1

Sí, se puede hacer. Al menos creo que esto debería funcionar; Me terminó reemplazando todo el modelo de autenticación, así que estoy dispuesto a ser corregido si esto no funciona ...

Si no tiene registros de usuario que le interesan:

  1. eliminar la tabla auth_user
  2. cambio de nombre de usuario para max_length = 75 en el modelo
  3. syncdb

Si tiene registros de usuario que necesita para retener entonces es más complicado que necesita migrar de alguna manera.Es más fácil de copia de seguridad y restauración de los datos de la vieja a la nueva tabla, algo así como:

  1. copia de seguridad de los datos de la tabla de usuario
  2. eliminar la tabla
  3. syncdb
  4. datos de usuario
  5. reimportación a la nueva tabla; teniendo cuidado de recuperar los valores de la ID original

Por otra parte, la utilización de su loca pitón-django Skillz, copiar las instancias de modelo de usuario de viejo a nuevo y reemplazar:

  1. crear su modelo personalizado y temporalmente soportarlo junto con el modelo por defecto
  2. escribir un script que copia a las instancias desde el modelo por defecto al nuevo modelo
  3. sustituir el modelo por defecto con su aduana una

Esto último no es tan difícil como suena, pero obviamente implica un poco más de trabajo.

-2

La mejor solución es utilizar el campo de correo electrónico para el correo electrónico y el nombre de usuario para el nombre de usuario .

En la validación del formulario de inicio de sesión de entrada, busque si los datos son el nombre de usuario o el correo electrónico y, si es un correo electrónico, consulte el campo de correo electrónico.

Esto solo requiere el parche de mono contrib.auth.forms.login_form, que son unas pocas líneas en la vista correspondiente.

Y es mucho mejor que tratar de modificar los modelos y las tablas de la base de datos.

+0

Creo que puede haber entendido mal el problema, se trata de cuando no estás usando nombres de usuario, estás usando correos electrónicos. Y para resolverlo sin tocar django, la solución generalmente aceptada es más de lo que describes aquí. En realidad, es para llenar el nombre de usuario con algo único (desperdiciando el campo) y crear una clase AUTHORIZATION_BACKENDS para manejar la autenticación en el correo electrónico. El problema es con la creación de usuarios. Hay un campo de correo electrónico pero también hay un campo de nombre de usuario obligatorio que no se ajusta a un correo electrónico. Aún debe crear un nombre de usuario único cada vez que crea un usuario. – Purrell

+0

perrierismo: La solución para usar correos electrónicos solo, sin tocar django, es, obviamente, usando un nombre de usuario aleatorio generado automáticamente y usando correos electrónicos para consultar (con el servidor de autenticación de correo electrónico). –

+0

No fue 'obviamente' para ti antes porque esa es realmente una solución diferente a la que publicaste. La solución que mencionas ahora no requiere cambiar contrib.auth.forms.login_form. Además, la cuestión de los nombres de usuario generados automáticamente no es tan sencilla si se desea una implementación sólida (consulte la discusión con Bartek). Finalmente, tenga en cuenta que la pregunta no es: "¿cómo se hace esto * sin * modificar django?". La solución generalmente aceptada para eso (que no ha declarado en realidad) es conocida, pero tiene limitaciones. Es decir, que aún necesita nombres de usuario únicos para la creación del usuario. – Purrell

19

solución de actualización para la versión 1.3 de Django (sin modificar manage.py):

Crear nueva aplicación django-:

monkey_patch/ 
    __init__.py 
    models.py 

instalarlo como primera: (settings.py)

INSTALLED_APPS = (
    'monkey_patch', 
    #... 
) 

Aquí es 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() 
+0

Intenté esta solución porque parece mucho más simple, pero tengo el mismo resultado "valor demasiado largo para caracteres tipo que varían (30)". Hice todo exactamente como dijiste. Estoy en django 1.3.3 – dnuske

24

En base a la gran respuesta combinada de Clément y Matt Miller, he reunido una aplicación rápida que la implementa. Pip instala, migra y listo. Pondría esto como un comentario, ¡pero aún no tengo la credibilidad!

https://github.com/GoodCloud/django-longer-username

EDITAR 2014-12-08

El módulo anterior está desfasada y en https://github.com/madssj/django-longer-username-and-email

+0

Gracias @skoczen! Funciona muy bien para mí hasta ahora – yairchu

+0

Intenté esto pero obtengo el error "No existe un módulo de base de datos South 'south.db.mysql' para su base de datos. Elija una base de datos compatible, compruebe la configuración SOUTH_DATABASE_ADAPTER [S] o eliminar al sur de INSTALLED_APPS ". –

1

Fundamentalmente, el problema es que algunas personas quieren utilizar una dirección de correo electrónico como el identificador único, mientras que el sistema de autenticación de usuario en Django requiere un nombre de usuario único de como máximo 30 caracteres. Quizás eso cambie en el futuro, pero ese es el caso con Django 1.3 mientras escribo.

Sabemos que 30 caracteres son demasiado cortos para muchas direcciones de correo electrónico; incluso 75 caracteres no son suficientes para representar algunas direcciones de correo electrónico, como se explica en What is the optimal length for an email address in a database?.

Me gustan las soluciones simples, por lo que recomiendo hash la dirección de correo electrónico en un nombre de usuario que cumpla con las restricciones para los nombres de usuario en Django. De acuerdo con User authentication in Django, un nombre de usuario debe tener como máximo 30 caracteres, que consisten en caracteres alfanuméricos y _, @, +,. y -Por lo tanto, si usamos la codificación de base 64 con una sustitución cuidadosa de los caracteres especiales, tenemos hasta 180 bits. Así que podemos utilizar una función hash de 160 bits como SHA-1 de la siguiente manera:

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 resumen, esta función asocia un nombre de usuario para cualquier dirección de correo electrónico. Soy consciente de que hay una pequeña probabilidad de una colisión en la función hash, pero esto no debería ser un problema en la mayoría de las aplicaciones.

+1

¿Por qué perder los ciclos calculando el hash y el espacio en disco que lo almacena? También tenga en cuenta que necesita escribir un backend de autorización personalizado o un login_form personalizado para que esto funcione. No es un gran problema, pero todo parece una extraña contorsión para mí. – Purrell

+0

En mi caso, los ciclos hash y el espacio en disco son mucho más baratos que el tiempo de desarrollo. Su experiencia puede ser diferente. –

5

Las soluciones anteriores parecen actualizar la longitud del modelo. Sin embargo, para reflejar su longitud personalizada en administración, también debe anular los formularios de administración (frustrantemente, no heredan simplemente la longitud del modelo).

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)) 
+1

gracias por capturar este – Purrell

+1

es posible que también desee aplicar la misma lógica a AuthenicationForm en django.contrib.auth.forms – davidtingsu

+0

que es AuthenticationForm deletreado correctamente :) – max4ever

-2

Si está utilizando Venv (entorno virtual), la solución más sencilla probablemente se acaba de actualizar el código del núcleo directamente, es decir, la apertura de los dos archivos siguientes: - - Venv/lib/python2.7/sites-paquetes /django/contrib/auth/model.py - venv/lib/python2.7/sites-packages/django/contrib/auth/forms.py Buscar todos los campos de nombre de usuario y cambiar max_length de 30 a 100. Es seguro ya que estás usando venv para que no afecte a ningún otro proyecto de Django.

2

Por lo que yo sé, uno puede override user model desde Django 1.5, que resolverá un problema. Ejemplo sencillo de here

1

Simplemente añadiendo el siguiente código en la parte inferior de settings.py

from django.contrib.auth.models import User 
User._meta.get_field("username").max_length = 75 
+0

No funcionará en Django 1.7 y más tarde, ya que los modelos aún no se han importado – kaleissin

0

estoy usando Django 1.4.3 que hace que sea bastante fácil y no tuve que cambiar nada en mi código después de darme cuenta de que quería usar direcciones de correo electrónico largas como nombres de usuario.

Si tiene acceso directo a la base de datos, cámbiela a la cantidad de caracteres que desea, en mi caso 100 caracteres.

En su modelo de aplicación (miaplicacion/models.py) añadir el siguiente

from django.contrib.auth.models import User 

class UserProfile(models.Model): 

    # This field is required. 
    User._meta.get_field("username").max_length = 100 
    user = models.OneToOneField(User) 

Luego, en su settings.py se especifica el modelo:

AUTH_USER_MODEL = 'myapp.UserProfile' 
Cuestiones relacionadas