Как отобразить посторонний ключ, вызывающий ProtectedError? - PullRequest
0 голосов
/ 12 января 2020

Когда django выдает ProtectedError (потому что on_delete=models.PROTECT в некотором поле модели), он запускает этот исходный код для отображения ответа:

class ProtectedError(IntegrityError):
    def __init__(self, msg, protected_objects):
        self.protected_objects = protected_objects
        super().__init__(msg, protected_objects)

    def PROTECT(collector, field, sub_objs, using):
        raise ProtectedError(
            "Cannot delete some instances of model '%s' because they are "
            "referenced through a protected foreign key: '%s.%s'" % (
                field.remote_field.model.__name__, sub_objs[0].__class__.__name__, 
                field.name
            ),
            sub_objs
        )

При обработке я знаю, что эту ошибку достаточно просто переопределить delete() в представлении:

def delete(self, request, *args, **kwargs):
    self.object = self.get_object()
    try:
        self.object.delete()
        messages.error(self.request, 'Ok')
    except ProtectedError:
        messages.error(self.request, 'Whoops')
    return HttpResponse('myurl')

Однако я бы хотел быть более точным в сообщении, которое я передаю. Как я могу получить доступ к field.remote_field.model.__name__, sub_objs[0].__class__.__name__ и field.name из функции PROTECT на мой взгляд?

1 Ответ

1 голос
/ 12 января 2020

Я не думаю, что можно получить нужную информацию из функции PROTECT, кроме извлечения ее из сгенерированного сообщения с некоторыми регулярными выражениями.

Что возможно для создания собственного подкласса ProtectedError и ваш собственный метод PROTECT, например:

class MyProtectError(models.deletion.ProtectedError):

    def __init__(self, msg, protected_objects, **kwargs):
        self.kwargs = kwargs
        super().__init__(msg, protected_objects)

def MY_PROTECT(collector, field, sub_objs, using):
    kwargs = {
        'field': field,
        'sub_objs': sub_objs,
        'using': using
    }
    raise MyProtectError(
        "Cannot delete some instances of model '%s' because they are "
        "referenced through a protected foreign key: '%s.%s'" % (
            field.remote_field.model.__name__, sub_objs[0].__class__.__name__, field.name
        ),
        sub_objs, **kwargs
    )

Затем используйте MY_PROTECT в вашей модели, например,

class ProtectChild(models.Model):

    title = models.CharField(max_length=100)
    master = models.ForeignKey(ProtectMaster, on_delete=MY_PROTECT)

и используйте свой пользовательский класс в перезаписанном delete() метод:

def delete(self, request, *args, **kwargs):
        self.object = self.get_object()
        try:
            self.object.delete()
            messages.error(self.request, 'Ok')
        except MyProtectError as e:
            messages.error(self.request, 'Whoops')
            print('Model', e.kwargs['field'].remote_field.model.__name__)
            print('Field', e.kwargs['field'].name)
            print('Submodel', e.kwargs['sub_objs'][0].__class__.__name__)
        return render(request, 'myurl')
...