Django:未捕获用户定义的异常 [英] Django: User-defined exception not being caught
问题描述
我的 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屋!