0

I want my code to display a custom error message that depends on the type of error it encounters.

from .forms import NameForm, IdForm
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib import messages
from client_storage import insert
import mysql.connector.errors
from mysql.connector.errors import Error
import MySQLdb


def sign_in(request):
        #we need to handle all the data that was just typed, we'll add a condition for that
        if request.method == "POST":
            #here will construct the form with the POST data
            form = NameForm(request.POST)
            #the next part is to check that the information submitted is valid
            if form.is_valid():
                post = form.save()
                post.save()
                return HttpResponse(post.question_text)
            else:
                return HttpResponse("Form is invalid")
        else:
            form = NameForm()
        return render(request, 'checkin/base.html', {'form': form})

    #this view will be the sign-up view, where new clients will be given new ID numbers for training

def sign_up(request):

    if request.method == "POST":
        form = IdForm(request.POST)
        if form.is_valid():
            post = form.save()
            post.save()
            ID = post.id_text
            #we'll call an external function that checks membership of the users input in the database
            # query is the first element of the tuple that is returned from insert()
            query = insert(post.id_text)
            if query == 1062:
                messages.add_message(request, messages.INFO, 'Already taken ')
                return HttpResponseRedirect('sign_up')
            if query == 1054:
                messages.add_message(request, messages.INFO, 'Invalid input')
                return HttpResponseRedirect('sign_up')
            else:
                messages.add_message(request, messages.INFO, 'Thank you for signing up!')
                return HttpResponseRedirect('sign_up')

            # if the user enters a number that is already in use raise an 'duplicate' error
            # Capture the exception here
        else:
            return HttpResponse('That text is invalid')
    else:
        form = IdForm()
    return render(request, 'checkin/base.html', {'form': form})

For the `except` block I'm trying to figure out how to display either "Already taken" or "Invalid input" depending on the error code. However only "Already taken" ever appears. I feel like the problem is that the exception is being thrown before it even gets to the `if` clauses?

I'm using another file for the INSERT process:

import MySQLdb
import mysql.connector
from mysql.connector import errorcode
from django.contrib import messages

#Use a function to insert new information into the database

def insert(info):
    #open a connection
    db = MySQLdb.connect('localhost','root','password', 'CLIENTS')
    #prepare a cursor
    cursor = db.cursor()
    #prepare SQL query
    sql = "INSERT INTO clients(ID) VALUES (%s)" % info

    try:
        #execute the command
        cursor.execute(sql)
        #commit changes to the database
        print 'success'
        db.commit()
    except MySQLdb.Error as e:
        #return the first element in the tuple that contains the error message, which will be the errorcode
         if e[0] == 1062:
            return e[0]
         if e[0] == 1054:
            return e[0]

    #disconnect from server
    db.close()

EDIT The problem seems to have been that I was using mysql.connector.error instead of MySQLdb.Error.The mysql website seems to only use the former. And there isn't a lot of official documentation on the latter it seems, but thankfully I found this site. I changed the code so the sign_in view would recieve the returned info from the external insert fuction then act accordingly.

Amon
  • 2,725
  • 5
  • 30
  • 52
  • 2
    On [this page of the docs](https://dev.mysql.com/doc/connector-python/en/connector-python-api-errors-error.html), the third example down shows how to use a conditional for the error number of the exception. In your attempt, you never *capture* the error (or it never gets assigned to anything) so you can't compare to anything. ```bool(Error(errno=1062))``` probably evaluates to ```True``` so it always *works*. – wwii Dec 06 '16 at 05:12
  • 2
    https://docs.python.org/3/tutorial/errors.html#handling-exceptions, – wwii Dec 06 '16 at 05:17
  • 1
    The comments of @wwii are correct. You may edit your `try .. except` block and catch the right exceptions then return your desired string/type output. – Chiheb Nexus Dec 06 '16 at 05:23

1 Answers1

1

Like what @wwii said in the comments, you need to edit your try ... except block in order to catch the exception. See this page of documentations.

Also, in your actual code Error(errno=1062) return always a non nil string which validate your if statement. And this is why you got always the Already Taken message.

In order to handle this issue, you should modify your code into something like this example:

# What you need to import
import mysql.connector
from mysql.connector import errorcode

try:
    insert(post.id_text)
    messages.add_message(request, messages.INFO, 'Thank you for signing up ')
    return HttpResponseRedirect('sign_in')

# Catch the error exception
except mysql.connector.Error as err:
    # Error code 1062: https://dev.mysql.com/doc/refman/5.6/en/error-messages-server.html#error_er_dup_entry
    if err.errno == errorcode.ER_DUP_ENTRY:
        messages.add_message(request, messages.INFO, "Already taken") 
        return HttpResponseRedirect('sign_up')

    # Error code 1054: https://dev.mysql.com/doc/refman/5.6/en/error-messages-server.html#error_er_bad_field_error
    if err.errno == errorcode.ER_BAD_FIELD_ERROR:
        messages.add_message(request, messages.INFO, "Invalid input")
        return HttpResponseRedirect('sign_up')

Edit:

Your edited answer is correct within both Python2 and Python3.

Otherwise, if you're using Python2 you can do something like this.

try:
    #execute the command
    cursor.execute(sql)
    #commit changes to the database
    print 'success'
    db.commit()
except MySQLdb.Error, e:
     if e.args[0] == 1062 or e.args[0] == 1054:
        # e.args[0] is the error code in int format
        # e.args[1] is the complete error message in str format
        return e.args[1]
     else:
        # I didn't test all the cases, but this message
        # can save you more time during the debug later
        # if your code catch another error code rather than 1062 or 1054
        return "Something odd happened"

Also you can do something like this (This example if valid for both Python2 and Python3):

try:
    #execute the command
    cursor.execute(sql)
    #commit changes to the database
    print 'success'
    db.commit()
except MySQLdb.Error as e:
     if e[0] == 1062 or e[0] == 1054:
        # e[0] is the error code in int format
        # e[1] is the complete error message in str format
        return e[1]
     else:
        # Good for the debug
        return "Something odd happened"
Chiheb Nexus
  • 9,104
  • 4
  • 30
  • 43
  • I still seem to be having an issue with outputting the customized text, I'm getting the proper error code, however it's not within the website like I want. It's just displaying the Django error page – Amon Dec 06 '16 at 06:48
  • 1
    You mean in the second file where you use `except mysql.connector.Error as e: ... return e ` ? – Chiheb Nexus Dec 06 '16 at 06:51
  • 1
    Your question confuses me. In the first block of `try ... except` you'll catch the exception then add a message then return a `HttpResponseRedirect` so in the front end, after going throgh the exception, the page will redirect to your signup page. In the second block of `try ... except` you'll catch any exception of the type `mysql.connector.Error` then return the error. Maybe you should try to catch the exception code in the second block then create a custom message there. Try it then leave your feedbacks. – Chiheb Nexus Dec 06 '16 at 07:15
  • 1
    Yea I realized that I was catch exceptions in two different places, I actually deleted all the `try ... except` code from my second file and it didn't effect the output at all. It's almost 2:30am (hate when this happens so late lol) where I live so I'll try it tomorrow then report back – Amon Dec 06 '16 at 07:20
  • 1
    So you need to debug carefuly your code. if not you can edit your question and i'll try to help you as soon as possible. – Chiheb Nexus Dec 06 '16 at 07:24
  • 1
    I'm still stuck, my current code is the closest I have to a solution. Every other method doesn't seem to actually capture the `exception` – Amon Dec 08 '16 at 00:29
  • 1
    Okay, edit your question and try to put the max possible of information. I'll try to help you. – Chiheb Nexus Dec 08 '16 at 00:32
  • 1
    That's all the relevant info I can give you. However I realized I'm using two libraries: `mysql.connector` and `MySQLdb`, both of them have their own classes for handling `exceptions`. Right now I'm just focusing on the second file and trying to get it to actually `print` the message I want depending on the error code. Then I'll be able to do the rest, do you think that makes sense? Thanks again, I'll edit the second file – Amon Dec 08 '16 at 04:11
  • 1
    Okay so replacing `mysql.connector` with `MySQLdb` seems to have done the trick, it's actually `print`ing the string. I'm gonna head to bed now, I need to, I'll figure out what else I need to replace. I don't think `MySQLdb` has the attribute `errno` – Amon Dec 08 '16 at 04:20
  • 1
    So with `MySQLdb` you get the complete string ? The examples in the documentations used to use `mysql.connector` to catch only the errors codes then raise an exception. At least that's what the documentation said. Or maybe you can, also, use `mysql.connector` and use a costum error strings. I think it's not a good idea to print the standard error string in front of the user. This will guide, maybe, to SQLinjenction if there is any SQLi point injection. – Chiheb Nexus Dec 08 '16 at 04:44
  • 1
    Yea it gives me the string, and I don't intend on printing the standard error string. I just wanted the program to actually behave how I wanted. I know it uses `mysql.connector` but `MySQLdb` also has it's own set of `error` classes. Here's another Stack question using `MySQLdb` http://stackoverflow.com/questions/21721109/correct-exception-handling-with-python-mysqldb-connection – Amon Dec 08 '16 at 13:49
  • 1
    Here's a bunch of examples using it: http://www.programcreek.com/python/example/5535/MySQLdb.Error – Amon Dec 08 '16 at 13:52
  • 1
    Good. But you can still create your custom exception using the error code. Otherwise, you have the choice. Good luck. – Chiheb Nexus Dec 08 '16 at 14:24
  • 1
    Oh I'll still be using error codes, right now if I want to I can return the error message as a tuple: `(1062, "Duplicate entry '2501' for key 'PRIMARY'")`, from there I can just select the first element, which is `1062` in this case. There's also `MySQLdb.constants.ER_DUP_ENTRY`, when I finish it I'll update the post and let you know – Amon Dec 08 '16 at 14:37
  • I figured it out and edited it, let me know what you think. Thanks again for your help – Amon Dec 08 '16 at 20:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/130161/discussion-between-nexus66-and-amon). – Chiheb Nexus Dec 09 '16 at 01:12