1
votes

I am trying to make a custom registration form. Main idea is that users should use email as login method and be able to set username in profile(so I dont want to override username field). Problem with built-in django User model is that username field is required so I made my own model based on AbstractUser. Now when I am trying to register a new user I get "UNIQUE constraint failed: users_user.username".

models.py

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

forms.py

from django import forms
from .models import User

class RegisterForm(forms.ModelForm):
    username = forms.CharField(max_length=100, required=False)
    email = forms.EmailField(label='', max_length=100, widget=forms.TextInput(attrs={'placeholder': '[email protected]'}))
    password = forms.CharField(label='', max_length=100, widget=forms.PasswordInput(attrs={'placeholder': 'Password'}))

    class Meta:
        model = User
        fields = ['username', 'email', 'password']

views.py

from django.shortcuts import render, redirect
from django.contrib import messages

from .forms import RegisterForm


def register(request):
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid(): 
            form.save()
            return redirect('trade')

    else:
        form = RegisterForm()
        return render(request, 'users/register.html', {'form': form})

AUTH_USER_MODEL = 'users.User' is set

I tried to set email unique=True in models.py

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    email = models.EmailField(unique=True)

Now I get "The view users.views.register didn't return an HttpResponse object. It returned None instead" instead.

Keep in mind that this is my first django project on my own :) Help is appriciated!

EDIT with solution:

Well I solved this. All answers are in django documentation(who would've tought). The problem is if your are new as I am to python and django reading the documentation about custom user model can be very overwhelming. Main goal was to set email as login method. To do that you would have to set USERNAME_FIELD = 'email' in your model. And to be able to do that you have to make your model based not on AbstractUser, but on AbstractBaseUser(https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#specifying-a-custom-user-model) and rewrite how you create users. Seemed pretty hard to me but django has a very nice example how to do that right at the end of documentation(https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#a-full-example). I just copied the code, replaced 'date_of_birth' with 'username' and got exactly what I wanted plus a little bit of understandig how things work on top of that.

1

1 Answers

0
votes

With regards the error "The view users.views.register didn't return an HttpResponse object. It returned None instead", this is happening because register doesn't return something in all of the flows. In the case where the request is a POST the first if statement is true, so that's the branch we're in. If the form is valid, all is good, but if it's not, nothing is returned. We don't enter the else part because that only happens when request isn't a POST you could fix it by doing the following:

def register(request):
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid(): 
            form.save()
            return redirect('trade')

    else:
        form = RegisterForm()
    return render(request, 'users/register.html', {'form': form})

This way, in all situations you return something.

With regards the original error. Since you are inheriting from AbstractUser you are inheriting the username field and all the behaviours associated with it. In particular, it is still required to be unique. This is how it is defined in AbstractUser:

    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )

This is going to cause you problems if you are submitting blank values to your username. Your form will allow it, but it won't be allowed at the database level. There I would overwrite username, just add something like this:

    username = models.CharField(
        max_length=150,
        blank=True,
    )