1

I am following a tutorial for Login in Django and after finishing I found that the Login Form requires the Username and Password but I want to replace the username with the user's Email instead.

Here is the views.py

@login_required
def register(request):
    if request.method == 'POST':
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            form.save()
            username = form.cleaned_data.get('username')
            messages.success(request, f'Your account has been created! You are now able to log in')
            return redirect('login')
    else:
        form = UserRegisterForm()
    return render(request, 'users/register.html', {'form': form})
 
 
@login_required
def profile(request):
    if request.method == 'POST':
        u_form = UserUpdateForm(request.POST, instance=request.user)
        p_form = ProfileUpdateForm(request.POST,
                                   request.FILES,
                                   instance=request.user.profile)
        if u_form.is_valid() and p_form.is_valid():
            u_form.save()
            p_form.save()
            messages.success(request, f'Your account has been updated!')
            return redirect('profile')
 
    else:
        u_form = UserUpdateForm(instance=request.user)
        p_form = ProfileUpdateForm(instance=request.user.profile)
 
    context = {
        'u_form': u_form,
        'p_form': p_form
    }
 
    return render(request, 'users/profile.html', context)

Here is the forms.py

 
class UserRegisterForm(UserCreationForm):
    email = forms.EmailField()
 
    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']
 
 
class UserUpdateForm(forms.ModelForm):
    email = forms.EmailField()
 
    class Meta:
        model = User
        fields = ['username', 'email']
 
 
class ProfileUpdateForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ['image']

Here is the template

<form class="login100-form validate-form" method="POST">
<span class="login100-form-title p-b-34">
 Account Login
  </span>
 <fieldset class="input100 m-b-20">
  {% csrf_token %}
  {{ form|crispy }}
     </fieldset>
  <div class="container-login100-form-btn">
  <button class="login100-form-btn" type="submit" style="width: 90%">
       Sign in
   </button>
 </div>
 <div class="w-full text-center p-t-27 p-b-100">
  <a class="txt2" href="{% url 'password_reset' %}">Forgot Password ?</a>
   </div>
  </form>

My question how to change from username to user's email?

Shiko
  • 149
  • 9

5 Answers5

5

To create a Django login with email We have to overwrite Default User, We can start writing models, but first you might want to create different app for your users (its a good practice).

Models.py

class User(AbstractBaseUser):
    email = models.EmailField(
        verbose_name='email address',
        max_length=255,
        unique=True,
    )
    active = models.BooleanField(default=True)
    staff = models.BooleanField(default=False) # a admin user; non super-user
    admin = models.BooleanField(default=False) # a superuser
    # notice the absence of a "Password field", that is built in.

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = [] # Email & Password are required by default.

    def get_full_name(self):
        # The user is identified by their email address
        return self.email

Django has built-in methods for the User Manager. We have to customize them in order to make our custom user model work correctly.

class UserManager(BaseUserManager):
    def create_user(self, email, password=None):
        """
        Creates and saves a User with the given email and password.
        """
        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(
            email=self.normalize_email(email),
        )

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

Then you need to run migrations.

python manage.py makemigrations
python manage.py migrate

Here's the post you can follow: here

Lord-shiv
  • 1,103
  • 2
  • 9
  • 19
1

go to your models.py, and add

USERNAME_FIELD = 'email'

remove username from form.

1

I'd recommend using django-allauth. It's a powerful tool for authenticating users and you can set the user to log-in with email instead of username by adding this to your settings.py:

ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_AUTHENTICATION_METHOD='email'
GEV
  • 397
  • 2
  • 9
0

You can do that in two ways. By using AbstractUser or AbstractBaseUser

AbstractUser: define your user class by subclassing AbstractUser . AbstractUser class has all the necessary fields that you might need in user(you can add more). You can do something like setting email as username field and making username field as null. Use this option if you are good with the existing fields on the User model and just want to remove the username field.

class User(AbstractUser):
    username = None
    email = models.EmailField(_('email address'), unique=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

AbstractBaseUser:define your user class by subclassing AbstractBaseUser.It doesnt have existing fields.Use this option if you want to start from scratch by creating your own, completely new User model.

class User(AbstractBaseUser):
    email = models.EmailField(('email address'), unique=True)
    active = models.BooleanField(default=True)  # can login
    staff = models.BooleanField(default=False)  # staff user non super
    admin = models.BooleanField(default=False)  # superuser
    first_name = models.CharField(max_length=30, blank=True, null= True)
    last_name = models.CharField(max_length=30, blank=True, null= True)
    
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []
   

You need to write your custom manager depending on your need.

0

You can create backends.py

def authenticate(username=None, password=None):
    if '@' in username:
        kwargs = {'email': username}
    else:
        kwargs = {'username': username}
    try:
        user = User.objects.get(**kwargs)
        if user.check_password(password):
            # print(user.username)
            return user
    except User.DoesNotExist:
        return None


class EmailOrUsernameModelBackend(object):
    def authenticate(self, username=None, password=None):
        if '@' in username:
            kwargs = {'email': username}
        else:
            kwargs = {'username': username}
        try:
            user = User.objects.get(**kwargs)
            if user.check_password(password):
                # print(user.username)
                return user
        except User.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

After that, add the following code in settings.py

AUTHENTICATION_BACKENDS = (
    'users.backends.EmailOrUsernameModelBackend',
    'django.contrib.auth.backends.ModelBackend',
)

Then modify views.py

from django.contrib.auth import login as auth_login
def ajax_login(request):
    if request.method == 'POST':
        username = request.POST.get('email', '').strip()
        password = request.POST.get('password', '').strip()

        if username and password:
            user = authenticate(username=username, password=password)

            if user is not None:
                if user.is_active:
                    auth_login(request, user, backend='users.backends.EmailOrUsernameModelBackend')
                    try:
                        order = Order.objects.filter(user=request.user)
                    except Order.DoesNotExist:
                        order = None

                    if order is not None:
                        exist_order = True
                    else:
                        exist_order = False

                    data = {'success': True, 'order': exist_order}
                else:
                    data = {'success': False, 'message': 'User is not active'}
            else:
                data = {'success': False, 'message': 'Invalid username or password'}

    return JsonResponse(data)

Conda
  • 141
  • 1
  • 2
  • 12