Tango With Django: User Authentication - User being saved but not receiving confirmation
Question: I am on chapter 9 of Tango With Django:

<http://www.tangowithdjango.com/book17/chapters/login.html#demo>

Whenever I create a user, I get an error page on my browser as shown below:

    
    
        IntegrityError at /rango/register/
        rango_userprofile.user_id may not be NULL
        Request Method: POST
        Request URL:    http://127.0.0.1:8000/rango/register/
        Django Version: 1.7.1
        Exception Type: IntegrityError
        Exception Value:    
        rango_userprofile.user_id may not be NULL
        Exception Location: C:\Python27\lib\site-packages\django\db\backends\sqlite3\base.py in execute, line 485
        Python Executable:  C:\Python27\python.exe
        Python Version: 2.7.8
        Python Path:    
        ['C:\\Users\\Paul.Zovighian\\desktop\\tango\\tango_with_django_project',
         'C:\\Python27\\lib\\site-packages\\pandas-0.14.1-py2.7-win32.egg',
         'C:\\Python27\\lib\\site-packages\\pytz-2014.7-py2.7.egg',
         'C:\\Windows\\system32\\python27.zip',
         'C:\\Python27\\DLLs',
         'C:\\Python27\\lib',
         'C:\\Python27\\lib\\plat-win',
         'C:\\Python27\\lib\\lib-tk',
         'C:\\Python27',
         'C:\\Python27\\lib\\site-packages',
         'C:\\Python27\\lib\\site-packages\\win32',
         'C:\\Python27\\lib\\site-packages\\win32\\lib',
         'C:\\Python27\\lib\\site-packages\\Pythonwin']
        Server time:    Fri, 12 Dec 2014 16:50:14 +0000
    

I can see that there is an integrity error, but I am not sure why this is the
message I get. If I try registering that user again, it won't let me because
it says that that username already exists. So it's like, working for
registering new users, but it just doesn't acknowledge the successful
registration.

Here is my code:

models.py

    
    
    from django.db import models
    from django.template.defaultfilters import slugify
    from django.contrib.auth.models import User
    
    class Category(models.Model):
            name = models.CharField(max_length=128, unique=True)
            views = models.IntegerField(default=0)
            likes = models.IntegerField(default=0)
            slug = models.SlugField(unique=True)
    
            def save(self, *args, **kwargs):
                    self.slug = slugify(self.name)
                    super(Category, self).save(*args, **kwargs)
    
            def __unicode__(self):
                    return self.name
    
    class Page(models.Model):
        category = models.ForeignKey(Category)
        title = models.CharField(max_length=128)
        url = models.URLField()
        views = models.IntegerField(default=0)
    
        def __unicode__(self):
            return self.title
    
    class UserProfile(models.Model):
        # This line is required. Links UserProfile to a User model instance.
        user = models.OneToOneField(User)
    
        # The additional attributes we wish to include.
        website = models.URLField(blank=True)
        picture = models.ImageField(upload_to='profile_images', blank=True)
    
        # Override the __unicode__() method to return out something meaningful!
        def __unicode__(self):
            return self.user.username
    

views.py

    
    
    from django.http import HttpResponse
    from django.shortcuts import render
    from rango.models import Category
    from rango.models import Page
    from rango.forms import CategoryForm
    from rango.forms import PageForm
    from rango.forms import UserForm, UserProfileForm
    
    def index(request):
        # Query the database for a list of ALL categories currently stored.
        # Order the categories by no. likes in descending order.
        # Retrieve the top 5 only - or all if less than 5.
        # Place the list in our context_dict and dictionary which will be passed to the template engine.
        category_list = Category.objects.order_by('-likes')[:5]
        page_list = Page.objects.order_by('-views')[:5]
        context_dict = {'categories': category_list, 'pages': page_list}
    
        # Render the response and send it back!
        return render(request, 'rango/index.html', context_dict)
    
    def about(request):
        context_dict = {'italicmessage': "I am italicised font from the context"}
        return render(request, 'rango/about.html', context_dict)
    
    def category(request, category_name_slug):
    
        # Create a context dictionary which we can pass to the template rendering engine
        context_dict = {}
    
        try:
            # Can we find a category name slug with the given name?
            # If we can't, the .get() method raises a DoesNotExist exception.
            # So the .get() method returns one model instance or raises an exception.
            category = Category.objects.get(slug=category_name_slug)
            context_dict['category_name'] = category.name
    
            # Retrieve all the associated pages.
            # Note that filter returns >= 1 model instance.
            pages = Page.objects.filter(category=category)
    
            # Adds our results list to the template context under name pages.
            context_dict['pages'] = pages
            # We also add the category object from the database to the context dictionary.
            # We'll use this in the template to verify that the category exists.
            context_dict['category'] = category
            context_dict['category_name_slug'] = category_name_slug
    
        except Category.DoesNotExist:
            # We get here if we didn't find the specified category.
            # Don't do anything - the template displayes the "no category message for us."
            pass
    
        # Go render the response and return it to the client.
        return render(request, 'rango/category.html', context_dict)
    
    def add_category(request):
        # A HTTP POST?
        if request.method == 'POST':
            form = CategoryForm(request.POST)
    
            # Have we been provided with a valid form?
            if form.is_valid():
                # save the new category to the database.
                form.save(commit=True)
    
                # Now call the index() view.
                # The user will be shown the homepage.
                return index(request)
            else:
                # The supplied form contained errors - just print them to the terminal.
                print form.errors
        else:
            # If the request was not a POST, display the form to enter details.
            form = CategoryForm()
    
        # Bad form (or form details), no form supplied...
        # Render the form with error messages (if any).
        return render(request, 'rango/add_category.html', {'form': form})
    
    def add_page(request, category_name_slug):
    
        try:
            cat = Category.objects.get(slug=category_name_slug)
        except Category.DoesNotExist:
            cat = None
    
        if request.method == 'POST':
            form = PageForm(request.POST)
            if form.is_valid():
                if cat:
                    page = form.save(commit=False)
                    page.category = cat
                    page.views = 0
                    page.save()
                    return category(request, category_name_slug)
        else:
            form = PageForm()
    
        context_dict = {'form': form, 'category': cat, 'category_name_slug': category_name_slug}
    
        return render(request, 'rango/add_page.html', context_dict)
    
    def register(request):
    
        # A boolean value for telling the template whether the registration was successful.
        # Set to False initially. Code changes value to True when registration succeeds.
        registered = False
    
        # If it's a HTTP POST, we're interested in processing form data.
        if request.method == 'POST':
            # Attempt to grab information from the raw form information
            # Note that we make use of both UserForm and UserProfileForm.
            user_form = UserForm(data=request.POST)
            profile_form = UserProfileForm(data=request.POST)
    
            # If the two forms are valid...
            if user_form.is_valid() and profile_form.is_valid():
                # Save the user's form data to the database.
                user = user_form.save()
    
                # Now we hash the password with the set_password method.
                # Once hashed, we can update the user object.
                user.set_password(user.password)
                user.save()
    
                # Now we sort out the UserProfile instance.
                # Since we need to set the user attribute ourselves, we set commit=False
                # This delays saving the model until we're ready to avoid integrity problems.
                profile = profile_form.save()
                profile.user = user
    
                # Did the user provide a profile picture?
                # If so, we need to get it from the input form and put it in the UserProfile model.
                if  'picture' in request.FILES:
                    profile.picture = request.FILES['picture']
    
                # Now we save the UserProfile model instance.
                profile.save()
    
                # Update our variables to tell the template registration was successful.
                registered = True
    
            # Invalid form or forms - mistakes or something else?
            # Print problems to the terminal.
            # They'll also be shown to the user.
            else:
                print user_form.errors, profile_form.errors
    
        # Not a HTTP POST, so we render our form using two ModuleForm instances.
        # These forms will be blank, ready for user input.
        else:
            user_form = UserForm()
            profile_form = UserProfileForm()
    
        # Render the template depending on the context.
        return render(request,'rango/register.html', {'user_form': user_form, 'profile_form': profile_form, 'registered': registered} )
    

forms.py

    
    
    from django import forms
    from django.contrib.auth.models import User
    from rango.models import Page, Category, UserProfile
    
    class CategoryForm(forms.ModelForm):
        name = forms.CharField(max_length=128, help_text="Please enter the category name.")
        views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
        likes = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
        slug = forms.CharField(widget=forms.HiddenInput(), required=False)
    
        # An inline class to provide additional information on the form.
        class Meta:
            # Provide an association between the ModelForm and a model
            model = Category
            fields = ('name',)
    
    class PageForm(forms.ModelForm):
        title = forms.CharField(max_length=128, help_text="Please enter the title of the page.")
        url = forms.URLField(max_length=200, help_text="Please enter the URL of the page.")
        views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
    
        class Meta:
            model = Page
            exclude = ('category',)
                    # or specify the fields to include (.i.e. not include the category field)
            #fields = ('title', 'url', 'views')
    
        def clean(self):
            cleaned_data = self.cleaned_data
            url = cleaned_data.get('url')
    
            # If url is not empty and doesn't start with 'http://', prepend 'http://'.
            if url and not url.startswith('http://'):
                url = 'http://' + url
                cleaned_data['url'] = url
    
            return cleaned_data
    
    class UserForm(forms.ModelForm):
        password = forms.CharField(widget=forms.PasswordInput())
    
        class Meta:
            model = User
            fields = ('username','email','password')
    
    class UserProfileForm(forms.ModelForm):
        class Meta:
            model = UserProfile
            fields = ('website', 'picture')
    

I think I have all relevant files included here, but let me know if I could
provide any other info to clear things up!

Thanks in advance


Answer: Your comment says "Since we need to set the user attribute ourselves, we set
commit=False", but you don't actually do that. It should be:

    
    
    profile = profile_form.save(commit=False)
    

In future, please cut your code down to the minimum that exhibits your
problem: the error was occurring on register, you didn't need to show all the
code relating to categories.

