数据库写入时发生Django间歇性错误

时间:2014-10-20 22:44:50

标签: python django postgresql nginx gunicorn

我有一个使用nginx在gunicorn上运行的Django 1.6应用程序。我间歇性地收到错误,它似乎发生在任何写入数据库的页面上。

错误是:

Exception Value: 'module' object has no attribute '\__newobj__'

Traceback:
File "/home/django/venvs/cantifix-direct/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  112.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/django/apps/cantifix-direct/products/views.py" in dispatch_product_form
  359.             return cls.as_view()(request)
File "/home/django/venvs/cantifix-direct/local/lib/python2.7/site-packages/django/views/generic/base.py" in view
  69.             return self.dispatch(request, *args, **kwargs)
File "/home/django/venvs/cantifix-direct/local/lib/python2.7/site-packages/django/contrib/formtools/wizard/views.py" in dispatch
  236.         response = super(WizardView, self).dispatch(request, *args, **kwargs)
File "/home/django/venvs/cantifix-direct/local/lib/python2.7/site-packages/django/views/generic/base.py" in dispatch
  87.         return handler(request, *args, **kwargs)
File "/home/django/venvs/cantifix-direct/local/lib/python2.7/site-packages/django/contrib/formtools/wizard/views.py" in post
  272.         management_form = ManagementForm(self.request.POST, prefix=self.prefix)
File "/home/django/venvs/cantifix-direct/local/lib/python2.7/site-packages/django/forms/forms.py" in __init__
  100.         self.fields = copy.deepcopy(self.base_fields)
File "/usr/lib/python2.7/copy.py" in deepcopy
  174.                 y = copier(memo)
File "/home/django/venvs/cantifix-direct/local/lib/python2.7/site-packages/django/utils/datastructures.py" in __deepcopy__
  144.                                for key, value in self.items()])
File "/usr/lib/python2.7/copy.py" in deepcopy
  174.                 y = copier(memo)
File "/home/django/venvs/cantifix-direct/local/lib/python2.7/site-packages/django/forms/fields.py" in __deepcopy__
  188.         result = copy.copy(self)
File "/usr/lib/python2.7/copy.py" in copy
  88.             rv = reductor(2)

Exception Type: AttributeError at /products/configure/
Exception Value: 'module' object has no attribute '__newobj__'

我得到的这个可能是写入数据库的所有页面的三分之一,这很奇怪。

这是我的某个应用中的模型:

https://gist.github.com/anonymous/6648fd6233717c11a462

这是我的views.py

import json
from itertools import groupby

from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404, render, redirect
from django.views.generic import DetailView, ListView, FormView, UpdateView
from django.contrib.formtools.wizard.views import SessionWizardView
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.shortcuts import resolve_url
from django.conf import settings
from django.contrib.auth.views import redirect_to_login


from prices import Price

from core.utils import DistanceAwareJSONEncoder, is_expert, apply_vat
from .models import (Product, ProductCustomisation, ProductConfiguration,
                     PROJECT_TYPE_CHOICES, PROPERTY_TYPE_CHOICES,
                     OptionsRecommendation, ProductGroup)
from .mixins import ProductOptions
from sales.models import Estimate, Pallet
from . import forms
from customer.models import Address
from delivery.models import DeliveryMethod


class ProductDetailView(DetailView):
    model = Product


class ProductGroupListView(ListView):
    model = ProductGroup
    template_name = 'products/product_groups.html'

    def get_queryset(self):
        return ProductGroup.objects.filter(hidden=False).order_by('name')


class ProductListView(ListView):
    model = Product
    template_name = 'products/product_list.html'

    def get_queryset(self):
        return Product.objects.filter(hidden=False, 
                                      group=self.kwargs['pk']).order_by('name')


#------------------------------------------------------------------------------


class GetRangesMixin(object):
    def get_dimension_ranges(self):
        return {'max_width': self.product.max_frame_width_mm,
                'min_height': self.product.min_frame_height_mm,
                'max_height': self.product.max_frame_height_mm,
                'min_width': self.product.min_frame_width_mm}


class ProductWizardMixin(GetRangesMixin):

    def get_recommended_options(self, proj_type, prop_type):
        if proj_type is not None:
            proj_type = str(proj_type)
        if prop_type is not None:
            prop_type = str(prop_type)
        product = self.get_form_instance(self.steps.current).product
        proj_name = dict(PROJECT_TYPE_CHOICES).get(proj_type)
        prop_name = dict(PROPERTY_TYPE_CHOICES).get(prop_type)
        try:
            proj = product.options_recommendation.get(type=proj_type)
        except OptionsRecommendation.DoesNotExist:
            proj = None
        try:
            prop = product.options_recommendation.get(type=prop_type)
        except OptionsRecommendation.DoesNotExist:
            prop = None
        recommendations = {}
        for name in ProductOptions.get_fields():
            proj_val, prop_val = (getattr(proj, name, None),
                                  getattr(prop, name, None))
            text = []
            if proj_val:
                text.append("project type (%s)" %proj_name.lower())
            if prop_val:
                text.append("property type (%s)" %prop_name.lower())
            if not len(text):
                continue
            text = 'Recommended due to ' + ' and '.join(text)
            recommendations[name] = (proj_val or prop_val, text)
        return recommendations

    def done(self, form_list, **kwargs):
        form_data = self.get_all_cleaned_data()
        session = self.request.session
        self.instance.apply_option_switches()
        self.instance.save()
        if self.request.user.is_anonymous():
            session['project_type'] = form_data.get('project_type')
            session['property_type'] = form_data.get('property_type')
        else:
            user_profile = self.request.user.profile
            user_profile.default_project_type = form_data.get('project_type')
            user_profile.default_property_type = form_data.get('property_type')
            user_profile.save()
        approx = form_data.get('approx_dimensions', False)
        session['approx_dimensions'] = approx
        session['product_customisation_id'] = self.instance.id
        session.save()
        return redirect('products_product_summary')

    def get_template_names(self):
        template_name = 'products/%s_%s.html' %(self.template_prefix,
                                                self.steps.current)
        return [template_name, 'products/wizard_base.html']

    @property
    def product(self):
        try:
            return self.get_cleaned_data_for_step('product').get('product')
        except:
            return None

    def get_form_instance(self, step):
        if self.instance is None:
            self.instance = ProductCustomisation(product=self.product)
        return self.instance

    def get_context_data(self, *args, **kwargs):
        context = super(ProductWizardMixin, self).get_context_data(*args, **kwargs)
        context['product'] = self.product
        context['steps'] = [(s.replace('_', ' ').capitalize(), s==self.steps.current)
                            for s in self.steps.all]
        return context

    def get_form_initial(self, step):
        data = super(ProductWizardMixin, self).get_form_initial(step)
        data['user'] = self.request.user
        if step == self.project_type_step:
            if self.request.user.is_anonymous():
                data['project_type'] = self.request.session.get('project_type',None)
                data['property_type'] = self.request.session.get('property_type',None)
            else:
                data['project_type'] = self.request.user.profile.default_project_type
                data['property_type'] = self.request.user.profile.default_property_type
        if step == 'product':
            data['product_id'] = self.request.GET.get('product')
        return data


#------------------------------------------------------------------------------


class GuidedView(ProductWizardMixin, SessionWizardView):

    template_prefix = 'guided'
    approx_dimensions_step = 2
    project_type_step = 'project_type'

    instance = None
    _product = None
    _recommendations = None

    form_list = [
        ('project_type', forms.ProjectTypeForm),
        ('product', forms.ProductForm),
        ('dimensions', forms.DimensionsForm),
        ('configuration', forms.ConfigurationForm),
        ('options', forms.OptionsForm),
    ]

    def get_form_initial(self, step):
        data = super(GuidedView, self).get_form_initial(step)
        if step == "configuration":
            form_data = self.get_cleaned_data_for_step('dimensions')
            data['width'] = form_data['width']
        elif step == 'product':
            form_data = self.get_cleaned_data_for_step(self.project_type_step)
            if form_data:
                data['project_type'] = form_data['project_type']
                data['property_type'] = form_data['property_type']
        elif step == "options":
            for name, (val, _) in self.recommendations.items():
                data[name] = val
        return data

    @property
    def recommendations(self):
        if self._recommendations is None:
            product = self.get_form_instance('options').product
            form_data = self.get_cleaned_data_for_step('project_type')
            proj_type = form_data['project_type']
            prop_type = form_data['property_type']
            self._recommendations = self.get_recommended_options(
                proj_type,
                prop_type
            )
        return self._recommendations

    def get_context_data(self, form, **kwargs):
        context = super(GuidedView, self).get_context_data(form,**kwargs)
        if self.steps.current == 'dimensions':
            context['ranges'] = self.get_dimension_ranges()
        if self.steps.current == 'options':
            form_data = [(form[field], self.recommendations.get(field, None))
                         for field in form.fields]
            context['form_data'] = form_data
        return context


#------------------------------------------------------------------------------


class ExpertView(ProductWizardMixin, SessionWizardView):

    template_prefix = 'expert'
    approx_dimensions_step = 1
    project_type_step = 'customisation'

    _product = None
    instance = None


    form_list = [
        ('product', forms.ProductForm),
        ('customisation', forms.ExpertForm),
    ]

    def get_context_data(self, form, **kwargs):
        context = super(ExpertView, self).get_context_data(form, **kwargs)
        if self.steps.current == 'customisation':
            context['ranges'] = self.get_dimension_ranges()
        return context

    def post(self, request, *args, **kwargs):
        if not request.is_ajax():
            return super(ExpertView, self).post(request, *args, **kwargs)
        proj_type = request.POST.get('customisation-project_type')
        prop_type = request.POST.get('customisation-property_type')
        recommendations = self.get_recommended_options(proj_type, prop_type)
        return HttpResponse(json.dumps(recommendations),
                            content_type='application/json')


#------------------------------------------------------------------------------


class ProductSummaryView(FormView):

    form_class = forms.SummaryForm
    template_name = 'products/summary.html'

    @property
    def approx_dimensions(self):
        return self.request.session.get('approx_dimensions', False)

    def get_initial(self):
        self.custom = get_custom(self.request)
        return {'user': self.request.user,
                'address': self.request.GET.get('address'),
                'method': self.request.GET.get('method', 1)}

    def form_valid(self, form):
        return self.render_to_response(self.get_context_data(form=form))

    def get_context_data(self, **kwargs):
        context = super(ProductSummaryView, self).get_context_data(**kwargs)
        form = kwargs['form']
        approx_dimensions = self.approx_dimensions
        context['user'] = self.request.user
        context['custom'] = self.custom
        context['total_cost'] = self.custom.total_price
        context['approx_dimensions'] = approx_dimensions
        form_data = getattr(form, 'cleaned_data', None)
        if form_data and not form.errors:
            method = form_data.get('delivery_method')
            delivery = self.estimate_delivery(
                method,
                address=form_data.get('delivery_address'),
                postcode=form_data.get('delivery_postcode')
            )
        else:
            method = DeliveryMethod.objects.get(id=1).name.lower()
            delivery = Price(0, currency='GBP')
        context['delivery_method'] = method
        context['delivery_price'] = delivery
        return context


    def estimate_delivery(self, method, address=None, postcode=None):
        if address is None:
            address = '%s, UK' %postcode
        backend = method.get_backend()(
            destination=address,
            num_items=1, # TODO only one item per product?
            weight=self.custom.total_weight,
            longest_length=self.custom.longest_length_mm
        )
        val = backend.get_charge_excl_tax()
        return Price(net=val, gross=apply_vat(val), currency='GBP')


#------------------------------------------------------------------------------

class EditProductViewBase(UpdateView, GetRangesMixin):

    model = ProductCustomisation
    form_class = forms.ExpertForm
    template_name = 'products/expert_customisation.html'

    def get_initial(self, **kwargs):
        return {'user': self.request.user,
                'approx_dimensions': self.approx_dimensions}

    @property
    def reverse_args(self):
        return None

    def get_success_url(self):
        return reverse(self.reverse_url, kwargs=self.reverse_args)

    def form_valid(self, form):
        form.instance.apply_option_switches()
        return super(EditProductViewBase, self).form_valid(form)

    def get_context_data(self, **kwargs):
        context = super(EditProductViewBase, self).get_context_data(**kwargs)
        self.product = self.object
        context['ranges'] = self.get_dimension_ranges()
        context['cancel_url'] = self.get_success_url()
        return context


class EditProductView(EditProductViewBase):
    reverse_url = 'products_product_summary'

    @property
    def approx_dimensions(self):
        approx = self.request.session.get('approx_dimensions', 'approx')
        return 'approx' if approx else 'exact'

    def get_object(self, queryset=None):
        return get_custom(self.request)


#------------------------------------------------------------------------------


def dispatch_product_form(request):
    if request.GET.has_key('mode'):
        cls = None
        if request.GET['mode'] == 'guided':
            cls = GuidedView
        elif request.GET['mode'] == 'expert':
            cls = ExpertView
        if cls is not None:
            return cls.as_view()(request)
    product = request.GET.get('product')
    query = 'mode=guided%s' %('&product=%s'%product if product is not None
                              else '')
    return redirect('%s?%s' %(reverse('products_product_configure'), query))


#------------------------------------------------------------------------------


def get_custom(request):
    custom_id = request.session.get('product_customisation_id', None)
    return get_object_or_404(ProductCustomisation, id=custom_id)


def remove_custom(request):
    request.session.pop('product_customisation_id', None)


@login_required
def create_estimate(request):
    return add_for_model(Estimate, request)


@login_required
def create_pallet(request):
    return add_for_model(Pallet, request)

@login_required
def add_to_estimate(request, estimate=None):
    return add_for_model(Estimate, request, estimate)


@login_required
def add_to_pallet(request, pallet=None):
    return add_for_model(Pallet, request, pallet)


def add_for_model(model, request, id=None):
    custom = get_custom(request)
    kwargs = {}
    if model == Estimate:
        kwargs['approx_dimensions'] = request.session.get(
            'approx_dimensions',
            False
        )
    if id is not None:
        obj = get_object_or_404(model, id=id)
        if obj.user!=request.user:
            raise PermissionDenied
    else:
        obj = model.objects.create(user=request.user)
    obj.add_item(custom, **kwargs)
    remove_custom(request)
    return redirect(reverse('sales_user_%s_detail' %model.__name__.lower(),
                            args=(obj.id,)))

0 个答案:

没有答案