0

I have created a Django Project using the standard User-form. I really need to make use of the email as login (using this guide). The problem is it should be difficult to migrate when I already have used the standard User-form.

Since we are only in the testing stage at the moment, I don't mind wiping the entire database to make this migration. Having that in mind, that losing data is not an issue, is there a way to make this migration?

EDIT (added some explanation):

Right now I have the "usual"

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

class UserRegisterForm(UserCreationForm):
    class Meta:
        model = User
        fields = ["username","email","password1","password2"]

i.e the login consists of a user-name and a password. Say I want to create my own user (here) I can understand that since I already have users in my database, it's going to be difficult.

So the question is; if I'm using the "standard" user model to create a user, and I want to go from that and using email as login (instead of username), how do I do that?

CutePoison
  • 4,679
  • 5
  • 28
  • 63

2 Answers2

0

With this code, you are going to delete the username from your Custom User. So, you can log in with the email instead of the username.

In YOUR_APP/models.py.

from django.contrib.auth.models import AbstractUser, BaseUserManager, Group
from django.conf import settings
from django.db import models
from django.utils.translation import gettext as _

class UserManager(BaseUserManager):
    """
    Model manager for User model without username field
    """
    use_in_migrations = True

    def create_user(self, email, password, **extra_fields):
        """
        Create a User with the given email and password
        """
        if not email:
            raise ValueError('The Email must be set')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password, **extra_fields):
        """
        Create a SuperUser with the given email and password
        """
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self.create_user(email, password, **extra_fields)
    
class User(AbstractUser):
    """
    Store a custom user
    """    
    username = None
    email = models.EmailField(_('email address'), unique=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserManager()

    class Meta:
        db_table = "auth_user"
    
    def __str__(self):
        return self.email
    
class ProxyUser(User):

    class Meta:
        app_label = "auth"
        proxy = True
        verbose_name = "User"
        verbose_name_plural = "Users"
    
class ProxyGroup(Group):

    class Meta:
        app_label = "auth"
        proxy = True
        verbose_name = "Group"
        verbose_name_plural = "Groups"

In YOUR_APP/forms.py

from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import User
    
class MyUserCreationForm(UserCreationForm):
    """
    Added creation of the user form.
    """
    class Meta(UserCreationForm):
        model = User
        fields = ["email"]
    
class MyUserChangeForm(UserChangeForm):
    """
    Added updating of the user form.
    """
    class Meta:
        model = User
        fields = ["email"]

In YOUR_APP/Admin.py just if you want to use Django-Admin

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext as _
from .models import User, ProxyUser, ProxyGroup
from .forms import MyUserCreationForm, MyUserChangeForm       
    
class MyUserAdmin(UserAdmin):
    """
    Django admin to Users (customizing authentication user)
    """
    add_form = MyUserCreationForm
    form = MyUserChangeForm
    model = User
    list_display = [
        "email", "first_name", "last_name", "is_staff"
    ]
    list_display_links = [
        "email"
    ]
    search_fields = [
        "email", "first_name", "last_name"
    ]
    fieldsets = [
        ("Access info", {
            "fields": ("email", "password")
        }),
        ("Personal info", {
            "fields": ("first_name", "last_name")
        }),
        ("Permissions", {
            "fields": ("is_active", "is_staff", "is_superuser", "groups", "user_permissions")
        }),
        ("Important dates", {
            "fields": ("last_login", "date_joined")
        }),
    ]
    add_fieldsets = [
        ("Access info", {
            "classes": ("wide",),
            "fields": ("email", "password1", "password2"),
        }),
    ]
    ordering = [
        "email"
    ]

    admin.register(ProxyUser, MyUserAdmin)
    admin.register(ProxyGroup)

In settings.py you need to redirect to the Custom User

AUTH_USER_MODEL = "YOUR_APP.User"

If you already did migrations before doing this. Make sure to delete/drop and create again the database, and delete the migrations folders and pycache folders of your project to avoids issues.

Finally, do the migrations

python manage.py migrate YOUR_APP 

If there is some kind of problems with the migration, you can try with the following code instead of the previous

python manage.py migrate YOUR_APP
python manage.py migrate auth
python manage.py migrate contenttypes
python manage.py migrate admin
python manage.py migrate sessions

I hope this help you.

Samir Hinojosa
  • 825
  • 7
  • 24
0

In the Django documentation, they highly recommend setting up a custom user model when starting a new project, even if the default one is sufficient for you. So to use the email as log in, first you need to add a custom user model:

# YourApp/models.py
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    email = models.EmailField('email address', unique=True)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

next, you need to point AUTH_USER_MODEL to the custom user model in settings.py:

# settings.py
AUTH_USER_MODEL = 'YouApp.User'

Also, make sure to update the forms.py and admin.py files:

# YourApp/forms.py
class YourSignUpForm(UserCreationForm):
    class Meta:
        model = User
        fields = ("username", "email", "password1","password2")
# YourApp/admin.py
from YourApp.models import User

admin.site.register(User, UserAdmin)

Finally, you need to delete the database and recreate it. If you are using postgres, for example, do this:

(venv) $ sudo -u postgres psql
postgres=# DROP DATABASE YourDB;
postgres=# CREATE DATABASE YourDB;

and run the migrations:

python manage.py migrate
Yacine Rouizi
  • 1,360
  • 2
  • 15
  • 31
  • The catch is I have already created the project, the database and started using it. That's the problem – CutePoison Mar 11 '21 at 18:29
  • ok no problem, just follow the steps I showed you and substitute the User model you are using with the custom one. For example in your `forms.py` file remove the line `from django.contrib.auth.models import User` and use this one: `from YourApp.models import User` – Yacine Rouizi Mar 11 '21 at 18:46