13

I am not very advanced user of Django. I have seen many different methods online, but they all are for modified models or too complicated for me to understand.
I am reusing the UserCreationForm in my MyRegistrationForm

class MyRegistrationForm(UserCreationForm):

    email = forms.EmailField(required=True)

    class Meta:
        model = User
        fields = ('username', 'email', 'password1', 'password2')

    def save(self, commit=True):
        user = super(MyRegistrationForm, self).save(commit=False)
        user.email = self.cleaned_data['email']
        user.set_password(self.cleaned_data["password1"])

        if commit:
            user.save()

        return user

I struggle to understand or find a way to check if the username that user enters is already taken or not. So I just use this to redirect me to html where it says bad username or passwords do not match:

def register_user(request):
    if request.method == 'POST':
        form = MyRegistrationForm(request.POST)
        if form.is_valid():
            form.save()

            return HttpResponseRedirect('/accounts/register_success')
        else:
            return render_to_response('invalid_reg.html')
  

    args = {}
    args.update(csrf(request))

    args['form'] = MyRegistrationForm()
    print args
    return render_to_response('register.html', args)

Here is my registration template(if needed):

{% extends "base.html" %}

{% block content %}

<section>
<h2 style="text-align: center">Register</h2>
<form action="/accounts/register/" method="post">{% csrf_token %}

<ul>
{{form.as_ul}}
</ul>
<input type="submit" value="Register" onclick="validateForm()"/>

</form>

</section>
{% endblock %}

But I need to raise some kind of exception or something like that before user gets redirected. Maybe when user presses register they would get the error/warning saying that username is already taken. Is that possible?

Amit Joshi
  • 15,448
  • 21
  • 77
  • 141
pptt
  • 675
  • 2
  • 8
  • 16

4 Answers4

24

You can use exists:

from django.contrib.auth.models import User

if User.objects.filter(username=self.cleaned_data['username']).exists():
    # Username exists
    ...
phoenix
  • 7,988
  • 6
  • 39
  • 45
JuniorCompressor
  • 19,631
  • 4
  • 30
  • 57
6

You can check if the username exists with the clean_username method and raise ValidationError:

def clean_username(self, username):
    user_model = get_user_model() # your way of getting the User
    try:
        user_model.objects.get(username__iexact=username)
    except user_model.DoesNotExist:
        return username
    raise forms.ValidationError(_("This username has already existed."))

If this case, you can show the error in the signup form and do not need to redirect to another page.

update:

As per @Spacedman pointed out a valid point regarding to race conditions on checking username uniqueness on Form logic against DB level's, although your chance of getting this is very unlikely, in case you do here are the relevant SO answers that may worth reading:

How to avoid race condition with unique checks in Django

Race conditions in django

Another update

As per OP's comment, here's another change can be made for the views:

def register_user(request):
    # be DRY, the form can be reused for both POST and GET
    form = MyRegistrationForm(request.POST or None)

    # check both request is a POST and the form is valid
    # as you don't need to redirect for form errors, remove else block
    # otherwise it's going to redirect even form validation fails
    if request.method == 'POST' and form.is_valid():
        form.save()
        return HttpResponseRedirect('/accounts/register_success')
    # I use render so need not update the RequestContext, Django does it for you
    html = render(request, 'register.html', {'form': form})
    return HttpResponse(html)

Hope this helps.

Community
  • 1
  • 1
Anzel
  • 19,825
  • 5
  • 51
  • 52
  • 3
    usernames are unique, so just try and create it and trap the resulting error. That way you'll avoid race conditions when another session tests for the same username between your session testing for existence and creating it. – Spacedman Apr 12 '15 at 11:36
  • @Spacedman, good point. But normally a `user_model` will have other related models (like `address` etc.) which under `form.save()`, the `user_model` may fail to create due to duplicate `username` but an `address` record may have been created? – Anzel Apr 12 '15 at 11:40
  • @Spacedman, seems like there will be compromise in terms of cleanness of Form logic and to deal with race conditions. For a general traffic website this should not be a problem, but for the edge cases, I have updated the answer to provide some useful links how to deal with them (although I still feel like there isn't any cleanest way to achieve the best of both). Thanks for pointing this out and I have also benefited from reading those answers :-) – Anzel Apr 12 '15 at 11:56
  • hello again guys, i was trying to do this for days, but i cant do it. this seems impossible. when i use code u guys suggested nothing changes i am still redirected to the static page where it says bad username or password. i really want to learn how to make error near the username field :(( – pptt Apr 18 '15 at 17:13
  • @pptt, remember to place the `clean_username` code block in your `MyRegistrationForm`, not on **views**. Also, your `views` can be improved, I'm not sure why you need to update `csrf`, it should be renewed by Django. I will make another update to my answer to reflect this, please check and report if this works for you. – Anzel Apr 20 '15 at 01:07
2

if you are using django's built in Usercreationform then simply type in your template:

{{form.errors}}

This will check almost everything like:

  1. whether user is created or not
  2. passwords are matching or not
markwalker_
  • 12,078
  • 7
  • 62
  • 99
Tanvesh
  • 127
  • 1
  • 10
0

You can use unique=True

in models.py

username = models.CharField(max_length=30, blank=True, null=True, unique=True)
DoDr
  • 85
  • 7