Django:未捕获用户定义的异常 [英] Django: User-defined exception not being caught

查看:26
本文介绍了Django:未捕获用户定义的异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的 Django 应用程序中有一个用户定义的异常:

I have a user-defined exception in my Django app:

class TemplateMatchError(Exception):
    ...

我有一段代码可以在常规 try ... except 中捕获此异常:

I have a segment of code that catches this exception in regular try ... except:

try:
  if template.match(text):
    return attrs
except TemplateMatchError as e:
  continue

我注意到在生产中,当 DEBUG=True 时,此错误不会被捕获,如果引发,我的浏览器会显示黄色的 Django 堆栈跟踪页面.当DEBUG=False时,异常被捕获.

I noticed that in production, when DEBUG=True, this error is not caught and if raised, my browser displays the yellow Django stack trace page. When DEBUG=False, the exception is caught.

我对这种行为感到惊讶,因为它意味着调试设置改变了普通 python try...except 的行为.这是一个正确的解释,如果是这样,为什么 Django 以这种方式工作?

I was surprised by this behavior since it means the debug setting changes the behavior of a normal python try...except. Is this a correct interpretation, and if so why does Django work this way?

更新:根据我在下面发布实际回溯的评论(名称与上面的玩具示例不同):

UPDATE: As per the comments I'm posting actual traceback below (names are different from toy example above):

Environment:


Request Method: POST
Request URL: http://mysite.com/api/call/6/

Django Version: 1.4.2
Python Version: 2.7.3
Installed Applications:
('longerusername',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.sites',
 'django.contrib.admin',
 'south',
 'django_extensions',
 'django.contrib.staticfiles',
 'crispy_forms',
 'api',
 'rest_framework')
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware')


Traceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
  111.                         response = callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python2.7/dist-packages/rest_framework/compat.py" in view
  121.                 return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py" in wrapped_view
  77.         return view_func(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py" in dispatch
  327.             response = self.handle_exception(exc)
File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py" in dispatch
  324.             response = handler(request, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/rest_framework/generics.py" in put
  469.         return self.update(request, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/rest_framework/mixins.py" in update
  129.         if serializer.is_valid():
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in is_valid
  479.         return not self.errors
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in errors
  471.                 ret = self.from_native(data, files)
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in from_native
  867.         instance = super(ModelSerializer, self).from_native(data, files)
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in from_native
  319.                 attrs = self.perform_validation(attrs)
File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py" in perform_validation
  260.                     attrs = validate_method(attrs, source)
File "/home/uname/api/serializers.py" in validate_text
  68.                 if template.match(text):
File "/home/uname/api/models.py" in match
  135.             raise TemplateMatchError()

Exception Type: TemplateMatchError at /api/call/6/
Exception Value: Not a match

这是我的models.py:

Here's my models.py:

import re
from datetime import datetime
from django.db import models
from django.contrib.auth.models import User as AuthUser
from otalo.ao.models import User
from otalo.surveys.models import Survey

class XCall(models.Model):
    created_on = models.DateTimeField(auto_now_add=True)
    send_on = models.DateTimeField(default=datetime.now)
    auth_user = models.ForeignKey(AuthUser, related_name='calls')
    recipient = models.ForeignKey(User)
    text = models.CharField(max_length=4096)
    backup_calls = models.IntegerField(blank=True, null=True)

    '''
    '    Associate with the survey, since
    '    all the calls can be traced through it.
    '    This billing/auditing purposes.
    '
    '    This can be lazily created at the time of
    '    scheduling the call, so make it nullable
    '
    '''
    survey = models.ForeignKey(Survey, null=True, blank=True)


    def __unicode__(self):
        return unicode(self.auth_user) + '-' + unicode(self.recipient)

class TemplateMatchError(Exception):
    STD_MESSAGE = 'Not a match'
    def __init__(self, msg=None):
        if msg is not None:
            self.msg = TemplateMatchError.STD_MESSAGE + ': ' + msg
        else:
            self.msg = TemplateMatchError.STD_MESSAGE
    def __str__(self):
        return self.msg

class XTemplate(models.Model):

    VARTYPE_NUM = '_N_'
    VARTYPE_WORD = '_W_'
    VARTYPE_DATETIME = '_DT_'
    VARTYPE_DATE = '_D_'
    VARTYPE_TIME = '_T_'

    # add grouping for regexes for easy extraction
    VARTYPE_REGEXS = {  VARTYPE_NUM: r'(\d+(?:\.\d{1,2})?)', # supports decimals up to two precision points. Could be more but then
                                                            # the prompting would start to sound weird
                        VARTYPE_WORD: r'(\w+)',
                        # Match dates and times as words
                        # so that your match function can
                        # try to convert to datetime for more
                        # detailed error messages
                        VARTYPE_DATETIME: r'(\w+)',
                        VARTYPE_DATE: r'(\w+)',
                        VARTYPE_TIME: r'(\w+)',
                      } 

    DATE_FORMATS = {
                        VARTYPE_DATETIME: '%d-%m-%y %H:%M',
                        VARTYPE_DATE: '%d-%m-%y',
                        VARTYPE_TIME: '%H:%M'
                    }

    created_on = models.DateTimeField(auto_now_add=True)
    auth_user = models.ForeignKey(AuthUser, related_name='templates')
    '''
    '    Encodes the wildcards and their type.
    '    e.g. Your account number _N_ is of type _W_ expiring at time _D_
    '''
    text = models.CharField(max_length=4096)

    '''
    '    For common prompts like numbers and dates and times
    '''
    language = models.CharField(max_length=24)

    STATUS_PENDING = 0
    STATUS_ACTIVE = 1
    STATUS_INACTIVE = 2

    STATUSES = (
    (STATUS_PENDING, 'Pending'),
    (STATUS_ACTIVE, 'Active'),
    (STATUS_INACTIVE, 'Inactive'),
    )
    status = models.IntegerField(choices=STATUSES)

    '''
    '    Compare the inupt text to this template's text;
    '    return the match object if it matches, else throw an error
    '''
    def match(self, input):
        pattern = self.text

        # first convert the template pattern into a regular expression
        vars = [var.group() for var in re.finditer('_[A-Z]_', pattern)]
        re_pattern = pattern
        for vartype, regex in XTemplate.VARTYPE_REGEXS.iteritems():
            re_pattern = re_pattern.replace(vartype, regex)

        # now try an initial match of the structure of the input
        match = re.match(re_pattern, input)

        if match:
            # make sure words are in the wordlist
            # numbers are valid numbers
            # and dates are in the proper format
            vocab = [word.text for word in self.vocabulary.all()]
            vals = match.groups()
            for i in range(len(vars)):
                if i > len(vals):
                    raise TemplateMatchError('Missing a variable in input')
                var = vars[i]
                val = vals[i]
                if var == XTemplate.VARTYPE_NUM:
                    try:
                        float(val)
                    except ValueError as e:
                        raise TemplateMatchError('Invalid number')
                elif var == XTemplate.VARTYPE_WORD:
                    if val not in vocab:
                        raise TemplateMatchError('Word not in vocabulary')
                elif var == XTemplate.VARTYPE_DATETIME or var == XTemplate.VARTYPE_DATE or var == XTemplate.VARTYPE_TIME:
                    format = XTemplate.DATE_FORMATS[var]
                    try:
                        date = datetime.strptime(val, format)
                    except ValueError as e:
                        raise TemplateMatchError('Invalid date, time, or datetime format - ' + val)
            return match
        else:
            raise TemplateMatchError()

    def __unicode__(self):
        return self.text + '-' + unicode(self.auth_user)

class XWord(models.Model):
    text = models.CharField(max_length=128)
    template = models.ForeignKey(XTemplate, related_name='vocabulary')

    def __unicode__(self):
        return self.text

有问题的序列化器类:

class CallSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(
        view_name='call-detail',
    )
    recipient = PhoneNumberField(read_only=False)
    status = SurveySerializer(source='survey', read_only=True)

    def validate_text(self, attrs, source):
        text = attrs['text']
        auth = self.context['request'].user
        templates = auth.templates.all()
        for template in templates:
            try:
                if template.match(text):
                    return attrs
            except TemplateMatchError as e:
                continue

        raise serializers.ValidationError("Call text does not match a registered template")

    class Meta:
        model = XCall
        fields = ('url', 'id', 'text', 'recipient', 'send_on', 'backup_calls', 'status')
        lookup_field= 'pk'

推荐答案

问题是models.py 抛出了不同的异常类,尽管名称相同.

The problem was that a different exception class was being thrown up by models.py, though the name was the same.

我的 settings.py 没有指定有问题的 models.py 所在的应用程序的完整路径.指定完整路径后,异常类匹配并捕获异常.感谢所有提供精彩提示的人.

My settings.py did not specify the full path of the app where the models.py in question lived. After specifying the full path, the exception classes matched and the exception was caught. Thanks to all who provided great hints.

这篇关于Django:未捕获用户定义的异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆