A
A
Arsen Abakarov2017-01-12 18:18:06
Django
Arsen Abakarov, 2017-01-12 18:18:06

Django IntegrityError on competing requests?

There is a model, it has a unique field
The model is presented in the admin panel
The problem is that if the database is loaded, then with a simultaneous request, the form passes validation and tries to save data in several streams, as a result, an IntegrityError is thrown into the console
This is clearly visible if at the moment put save points in sleep, or set break points in debug mode
Partially overcome the problem by wrapping the save block - super(ProductCardAdmin, self).save_model(request, obj, form, change) - into an atomic context manager, and wrapping it into try ... except and catch IntegrityError
Now, with requests, both work out and a redirect occurs to the page with the list of the model, but at the top in both cases it is written that the object was successfully saved, but it is necessary that in one case it was written that it was successful, and in others that it is impossible to save, because the object is already in the database
All this happens in the save_model(self, request, obj, form, change) method
In the handler I tried to do form.add_error('field_name', 'error_msg') and below del form.cleaned_data['field_name']
Doesn't help
How to win?
piece of code

spoiler

def get_form(self, request, obj=None, **kwargs):
        form = super(ProductCardAdmin, self)\
            .get_form(request, obj, fields=forms.ALL_FIELDS, **kwargs)
        if obj:
            form.base_fields.update(obj.kind.get_preview_template_fields(obj))
            try:
                template = obj.kind.template
            except Template.DoesNotExist:
                return form

            # TODO: Упростить как сделано в конфигураторе всю группу
            # методов для получения этой формы
            if template:
                form.base_fields.update(template.get_fields(obj))
        return form

    def save_model(self, request, obj, form, change):
        if obj:
            obj.has_template_changed = False
            if obj.author is None:
                obj.author = request.user
            is_checked = form.cleaned_data.get('is_checked', False)
            if is_checked:
                obj.date_checked = date.today()
                obj.is_ready = True
            if obj.is_ready:
                try:
                    template = obj.kind.template
                except Template.DoesNotExist:
                    obj.is_ready = False
        super(ProductCardAdmin, self).save_model(request, obj, form, change)
        obj.attributes.all().delete()
        obj.preview_templates.clear()
        for field_name, value in form.cleaned_data.iteritems():
            # attributes save
            if field_name.startswith('attrfield_'):
                if value is None:
                    continue
                attribute_pk = field_name.split('attrfield_')[1]
                attribute = obj.attributes.create(options_id=attribute_pk)
                attribute.value = value
                attribute.save()
            # preview template save
            if field_name.startswith('preview_template_'):
                if value is None:
                    continue
                shopping_type = field_name.split('preview_template_')[1]
                try:
                    preview_template = ProductCardPreviewTemplate.objects.get(
                        shopping_type=shopping_type,
                        pk=value
                    )
                except ProductCardPreviewTemplate.DoesNotExist:
                    continue
                obj.preview_templates.add(preview_template)

        # проверка в обработчике сигнала есть, просто заботимся о клиенте
        if obj.is_checked:
            # не стоит обновлять "слепок"... только upload_preview
            update_description_and_previews_signal.send(
                sender=ProductCard,
                codes=[obj.code],
                do_update_description=False
            )

        if obj.kind and obj.kind.template:
            productcard_change_signal.send(sender=ProductCard, instance=obj)

        # Возвращаемся к списоку карточек
        return HttpResponseRedirect(
            reverse('admin:cards_productcard_changelist')
        )

Answer the question

In order to leave comments, you need to log in

1 answer(s)
S
sim3x, 2017-01-12
@ArsenAbakarov

Make locks on the table
Make the field non-unique - observe uniqueness only by PrimaryKey

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question