Djangoでユーザー名としてメールアドレスを受け入れる


回答:


35

これを実行したい他の人は、django-email-as-usernameを確認することをお勧めします。これは、createsuperuser管理コマンドと管理コマンドのパッチを適用するなど、非常に包括的なソリューションです。

編集:Django 1.5以降では、django-email-as-usernameの代わりにカスタムユーザーモデルの使用を検討する必要があります


3
カスタムユーザーモデルが最適なオプションであると判断した場合は、caktusグループブログでこのチュートリアルを確認することをお勧めします。これは、django docsに記載されているこの例に似ていますが、本番環境に必要な詳細(アクセス許可など)を考慮しています。
gaboroncancio 2015年

4
Django 1.5以降の場合、django-custom-userアプリには、これを実装する既定のカスタムユーザーモデルが含まれています。
Josh Kelley 2015年

29

これが私たちの仕事です。これは「完全な」ソリューションではありませんが、探していることの多くを実行します。

from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

class UserForm(forms.ModelForm):
    class Meta:
        model = User
        exclude = ('email',)
    username = forms.EmailField(max_length=64,
                                help_text="The person's email address.")
    def clean_email(self):
        email = self.cleaned_data['username']
        return email

class UserAdmin(UserAdmin):
    form = UserForm
    list_display = ('email', 'first_name', 'last_name', 'is_staff')
    list_filter = ('is_staff',)
    search_fields = ('email',)

admin.site.unregister(User)
admin.site.register(User, UserAdmin)

私のために働きます。これは将来のメンテナにとって混乱を招くと思いますが。
ニックボルトン

それはおそらく私が今までSOで見た中で最も賢い答えです。
エメットB

admin Create User this:exclude =( 'email'、)で、key ['email']がUserFormにないというエラーがスローされます。これは明らかです。
CristiC777

22

ユーザー名と電子メールの両方が受け入れられるようにする1つの方法は次のとおりです。

from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.forms import ValidationError

class EmailAuthenticationForm(AuthenticationForm):
    def clean_username(self):
        username = self.data['username']
        if '@' in username:
            try:
                username = User.objects.get(email=username).username
            except ObjectDoesNotExist:
                raise ValidationError(
                    self.error_messages['invalid_login'],
                    code='invalid_login',
                    params={'username':self.username_field.verbose_name},
                )
        return username

デフォルトの認証フォームを設定するための設定があるかどうかはわかりませんが、urls.pyのURLを上書きすることもできます

url(r'^accounts/login/$', 'django.contrib.auth.views.login', { 'authentication_form': EmailAuthenticationForm }, name='login'),

ValidationErrorを上げると、無効な電子メールが送信されたときに500エラーを防ぐことができます。スーパーの「invalid_login」の定義を使用すると、エラーメッセージがあいまいになります(特定の「その電子メールによるユーザーが見つかりません」に対して)。これは、電子メールアドレスがサービスのアカウントにサインアップしているかどうかのリークを防ぐために必要です。その情報がアーキテクチャで安全でない場合は、より有益なエラーメッセージが表示される方が便利な場合があります。


auth.views.loginを使用していません。カスタムを使用すると、これは私のURL url(r'accounts / login '、' login_view '、)です。EmailAuthenticationFormを指定している場合、エラーはlogin_view()が予期しないキーワード引数 'authentication_form'を取得したことです
Raja Simon

これは私にとってはきれいに機能し、実際にはカスタムユーザープロファイルを使用しました(私のアーキテクチャでは、ユーザーが電子メールアドレスを添付することが保証されていないため)。ユーザーモデルをカスタマイズしたり、ユーザー名を電子メールと同じにするよりもはるかに優れているようです(たとえば、ユーザー名を公開している間、友人の電子メールを非公開に保つことができるため)。
owenfi 2014

8

Djangoは、管理者とフォームを備えた拡張認証システムの完全な例を提供するようになりました:https//docs.djangoproject.com/en/stable/topics/auth/customizing/#a-full-example

基本的にはコピー/貼り付けして適応させることができます(date_of_birth私の場合は必要ありませんでした)。

実際にはDjango1.5以降で利用可能であり、現在も利用可能です(django1.7)。


私はdjangoprojectチュートリアルに従い、それは完全に機能します
Evhz 2016

7

ユーザーモデルを拡張する場合は、とにかくカスタムユーザーモデルを実装する必要があります。

これがDjango1.8の例です。Django 1.7はもう少し作業が必要で、主にデフォルトのフォームを変更します(1.7で必要なのはUserChangeFormUserCreationForminを見django.contrib.auth.formsてください)。

user_manager.py:

from django.contrib.auth.models import BaseUserManager
from django.utils import timezone

class SiteUserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        today = timezone.now()

        if not email:
            raise ValueError('The given email address must be set')

        email = SiteUserManager.normalize_email(email)
        user  = self.model(email=email,
                          is_staff=False, is_active=True, **extra_fields)

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password, **extra_fields):
        u = self.create_user(email, password, **extra_fields)
        u.is_staff = True
        u.is_active = True
        u.is_superuser = True
        u.save(using=self._db)
        return u

models.py:

from mainsite.user_manager import SiteUserManager

from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin

class SiteUser(AbstractBaseUser, PermissionsMixin):
    email    = models.EmailField(unique=True, blank=False)

    is_active   = models.BooleanField(default=True)
    is_admin    = models.BooleanField(default=False)
    is_staff    = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'

    objects = SiteUserManager()

    def get_full_name(self):
        return self.email

    def get_short_name(self):
        return self.email

forms.py:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from mainsite.models import SiteUser

class MyUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm.Meta):
        model = SiteUser
        fields = ("email",)


class MyUserChangeForm(UserChangeForm):
    class Meta(UserChangeForm.Meta):
        model = SiteUser


class MyUserAdmin(UserAdmin):
    form = MyUserChangeForm
    add_form = MyUserCreationForm

    fieldsets = (
        (None,              {'fields': ('email', 'password',)}),
        ('Permissions',     {'fields': ('is_active', 'is_staff', 'is_superuser',)}),  
        ('Groups',          {'fields': ('groups', 'user_permissions',)}),
    )

    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'password1', 'password2')}
        ),
    )

    list_display = ('email', )       
    list_filter = ('is_active', )    
    search_fields = ('email',)       
    ordering = ('email',)


admin.site.register(SiteUser, MyUserAdmin)

settings.py:

AUTH_USER_MODEL = 'mainsite.SiteUser'

Iテストあなたのアプローチと作品をしましたが、私の場合には、私が追加したusernameにフィールドをSiteUser私が実行したときに、あるため、モデルpython manage.py makemigrations ...のコマンドを私はこの出力を得る:ERRORS: <class 'accounts.admin.UserAdmin'>: (admin.E033) The value of 'ordering[0]' refers to 'username', which is not an attribute of 'accounts.User'.
bgarcial

その属性usernameを使用して、フィールドをUserモデルに追加しましたnull=True。このペーストビンエントリでは、実装を表示したかったのです。pastebin.com/W1PgLrD9
bgarcial

2

他の選択肢は私には複雑すぎるように見えるので、ユーザー名、電子メール、またはその両方を使用して認証でき、大文字と小文字を区別できるようにするスニペットを作成しました。django-dual-authenticationとしてpipにアップロードしました。

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.conf import settings

###################################
"""  DEFAULT SETTINGS + ALIAS   """
###################################


try:
    am = settings.AUTHENTICATION_METHOD
except:
    am = 'both'
try:
    cs = settings.AUTHENTICATION_CASE_SENSITIVE
except:
    cs = 'both'

#####################
"""   EXCEPTIONS  """
#####################


VALID_AM = ['username', 'email', 'both']
VALID_CS = ['username', 'email', 'both', 'none']

if (am not in VALID_AM):
    raise Exception("Invalid value for AUTHENTICATION_METHOD in project "
                    "settings. Use 'username','email', or 'both'.")

if (cs not in VALID_CS):
    raise Exception("Invalid value for AUTHENTICATION_CASE_SENSITIVE in project "
                    "settings. Use 'username','email', 'both' or 'none'.")

############################
"""  OVERRIDDEN METHODS  """
############################


class DualAuthentication(ModelBackend):
    """
    This is a ModelBacked that allows authentication
    with either a username or an email address.
    """

    def authenticate(self, username=None, password=None):
        UserModel = get_user_model()
        try:
            if ((am == 'email') or (am == 'both')):
                if ((cs == 'email') or cs == 'both'):
                    kwargs = {'email': username}
                else:
                    kwargs = {'email__iexact': username}

                user = UserModel.objects.get(**kwargs)
            else:
                raise
        except:
            if ((am == 'username') or (am == 'both')):
                if ((cs == 'username') or cs == 'both'):
                    kwargs = {'username': username}
                else:
                kwargs = {'username__iexact': username}

                user = UserModel.objects.get(**kwargs)
        finally:
            try:
                if user.check_password(password):
                    return user
            except:
                # Run the default password hasher once to reduce the timing
                # difference between an existing and a non-existing user.
                UserModel().set_password(password)
                return None

    def get_user(self, username):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=username)
        except UserModel.DoesNotExist:
            return None


1
     if user_form.is_valid():
        # Save the user's form data to a user object without committing.
        user = user_form.save(commit=False)
        user.set_password(user.password)
        #Set username of user as the email
        user.username = user.email
        #commit
        user.save()

完璧に動作しています... django1.11.4の場合



0

最も簡単な方法は、ログインビューの電子メールに基づいてユーザー名を検索することです。そうすれば、他のすべてをそのままにしておくことができます。

from django.contrib.auth import authenticate, login as auth_login

def _is_valid_email(email):
    from django.core.validators import validate_email
    from django.core.exceptions import ValidationError
    try:
        validate_email(email)
        return True
    except ValidationError:
        return False

def login(request):

    next = request.GET.get('next', '/')

    if request.method == 'POST':
        username = request.POST['username'].lower()  # case insensitivity
        password = request.POST['password']

    if _is_valid_email(username):
        try:
            username = User.objects.filter(email=username).values_list('username', flat=True)
        except User.DoesNotExist:
            username = None
    kwargs = {'username': username, 'password': password}
    user = authenticate(**kwargs)

        if user is not None:
            if user.is_active:
                auth_login(request, user)
                return redirect(next or '/')
            else:
                messages.info(request, "<stvrong>Error</strong> User account has not been activated..")
        else:
            messages.info(request, "<strong>Error</strong> Username or password was incorrect.")

    return render_to_response('accounts/login.html', {}, context_instance=RequestContext(request))

テンプレートで、それに応じて次の変数を設定します。

<form method="post" class="form-login" action="{% url 'login' %}?next={{ request.GET.next }}" accept-charset="UTF-8">

そして、あなたのユーザー名/パスワード入力に正しい名前、すなわちユーザー名、パスワードを与えてください。

更新

または、if _is_valid_email(email):呼び出しをユーザー名のif '@'に置き換えることができます。そうすれば、_is_valid_email関数を削除できます。これは、ユーザー名の定義方法によって異なります。ユーザー名に「@」文字を許可すると機能しません。


1
ユーザー名に「@」記号を含めることもできるため、このコードにはバグがあります。したがって、「@」が存在する場合は、電子メールは必要ありません。
MrKsn 2014年

本当にあなた次第です、私はユーザー名に@記号を持たせません。その場合、別のフィルタークエリを追加して、ユーザー名でユーザーオブジェクトを検索できます。PS。ユーザー名は電子メールにすることもできるため、ユーザー管理の設計方法に注意する必要があります。
radtek 2014年

また、django.core.validatorsからimportvalidate_emailをチェックしてください。validate_email('your@email.com ')を使用してValidationErrorブロック以外の試行を行うことができます。アプリによってはまだバグがあるかもしれません。
radtek 2014年

もちろん、その通りです。ログインロジックの設定方法によって異なります。ユーザー名の「@」の可能性がdjangoのデフォルトであるため、私は言及しました。ログインコードが電子メールであると見なしているために、ユーザー名「Gladi @ tor」のユーザーがログインできない場合、誰かがコードをコピーして問題が発生する可能性があります。
MrKsn 2014年

よろしくお願いします。何をコピーしているのかを理解してもらいたい。メールの検証を追加し、現在の検証をコメントアウトします。代わりに残しておきます。@がないユーザー名を除いて、検証を使用しなかった唯一の他の理由は、コードがdjangoに依存しにくくなることだと思います。物事はバージョンからバージョンへと移動する傾向があります。乾杯!
radtek 2014年

0

最も簡単な方法は、から継承するフォームを作成してからUserCreateFormusernameフィールドをforms.EmailField。でオーバーライドすることだと思います。次に、新規登録ユーザーごとに、自分の電子メールアドレスでサインオンする必要があります。

例えば:

urls.py

...
urlpatterns += url(r'^signon/$', SignonView.as_view(), name="signon")

views.py

from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django import forms

class UserSignonForm(UserCreationForm):
    username = forms.EmailField()


class SignonView(CreateView):
    template_name = "registration/signon.html"
    model = User
    form_class = UserSignonForm

signon.html

...
<form action="#" method="post">
    ...
    <input type="email" name="username" />
    ...
</form>
...

いくつかの理由で反対票を投じました。この答えは、同じことをするためのより良い方法です。また、UserCreationFormクラスで直接使用するウィジェットを定義するだけでよいのに、なぜサブクラスなのですか?そして<input …、確かに{{form.username}}良いときに書き出すことはお勧めしません。
Jonas G. Drange 2016年

0

人々がこれを達成しようとしているかどうかはわかりませんが、保存する前に、メールを要求し、ビューのメールとしてユーザー名を設定するだけの良い(そしてクリーンな)方法を見つけました。

私のユーザーフォームには、電子メールとパスワードのみが必要です。

class UserForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput())

    class Meta:
        model = User
        fields = ('email', 'password')

次に、私の見解では、次のロジックを追加します。

if user_form.is_valid():
            # Save the user's form data to a user object without committing.
            user = user_form.save(commit=False)

            user.set_password(user.password)
            #Set username of user as the email
            user.username = user.email
            #commit
            user.save()

1
電子メールが75文字であるのに対し、ユーザー名は30文字であるため、電子メールをユーザー名に保存できないと問題が発生しますか?
user2233706 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.