0

Django アプリにユーザー定義の例外があります。

class TemplateMatchError(Exception):
    ...

通常のこの例外をキャッチするコードのセグメントがありますtry ... except:

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

DEBUG=True本番環境では、このエラーがキャッチされず、発生した場合、ブラウザーに黄色の Django スタック トレース ページが表示されることに気付きました。の場合DEBUG=False、例外がキャッチされます。

デバッグ設定が通常の python の動作を変更することを意味するため、この動作には驚きましたtry...except。これは正しい解釈ですか? もしそうなら、なぜ Django はこのように動作するのでしょうか?

更新:コメントに従って、実際のトレースバックを以下に投稿しています(名前は上記のおもちゃの例とは異なります):

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です:

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'
4

4 に答える 4

1

問題は、名前は同じなのに、models.py によって別の例外クラスがスローされていたことです。

私の settings.py は、問題の models.py が存在するアプリの完全なパスを指定していませんでした。フルパスを指定すると、例外クラスが一致し、例外がキャッチされました。素晴らしいヒントを提供してくれたすべての人に感謝します。

于 2013-09-12T13:42:29.220 に答える
0

同じモジュールからインポートされた同じクラス «TemplateMatchError» であり、キャッチしようとしていると確信していますか。

これが同じ名前の2つのクラスであるが、異なるモジュールからインポートされた場合、pythonはそれらを同じ例外として特徴付けず、catchブロックに入ることはありません。

于 2013-09-09T13:12:44.570 に答える
0

例外のキャッチに関するコードをもっと見ると役に立ちます。あなたが示したものから、見るべきことがいくつかあります:

  1. 私はTemplateMatchErrorあなたが最初に呼んだものだと思いますMyError
  2. あなたのコードは、どのようtemplate.matchに負の結果を返すか不明のようです。では、 /の戻り値serializers.pyを期待しているように見えますが、関数自体が誤ったものを返すのではなく、例外を発生させます。nilfalse
  3. あなたが示したコードセグメントにはインデントが正しくないため、エラーをキャッチできなかった可能性があります。

あなたがそれを示したように:

try:
    template.match(text)
    # Do other stuff, presumably including this:
    try:
        somethingElse()
    except TemplateMatchError as e:
        #this won't catch exceptions from template.match(text)
        continue

私はあなたがそれをどのように意味すると思いますか:

try:
    template.match(text)
except TemplateMatchError as e:
    # This will be caught
    continue

それが役立つことを願っています。

于 2013-09-09T08:37:05.597 に答える