# Additional checks before delete using DeleteView

**URL:** <https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006>\
**Category:** Using the ORM\
**Created:** [March 12, 2024, 8:22pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006 "2024-03-12T20:22:39Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![nmilner1234](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/nmilner1234/32/19690_2.png) [@nmilner1234](https://forum.djangoproject.com/u/nmilner1234)\
**Post date:** [March 12, 2024, 8:22pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/1 "2024-03-12T20:22:39Z")

</div>

I’ve implemented a simple DeleteView implmentation which works fine. However, I need to do some additional checks to make sure that that the email value in the object being deleted matches the logged in users email and if not, abort delete. So, after looking at [DeleteView](https://ccbv.co.uk/projects/Django/5.0/django.views.generic.edit/DeleteView/) I thought I could override delete and to test I simply put a breakpoint() inside but my method never gets called. Not sure why? I also tried overriding post which did get called however, super().post seems to call delete so I can’t see why my override for delete doesn’t work

```auto
class ManageEventPeopleDelete(DeleteView):
    model = EmailToEvent

    def get_context_data(self, **kwargs):

        context = super().get_context_data(**kwargs)
        context['category_id'] = self.kwargs['category_id']
        context['association_id'] = self.kwargs['association_id']
        context['event_id'] = self.kwargs['event_id']
        return context

    def delete(request, *args, **kwargs):
        breakpoint()

    def get_success_url(self):
        return reverse("showorderofplay:manage-event-people", args=[self.kwargs["category_id"], self.kwargs["association_id"], self.kwargs["event_id"]])

```

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 12, 2024, 8:50pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/2 "2024-03-12T20:50:19Z")

</div>

This is actually a change in Django 4.0.

See the release notes at [Django 4.0 release notes | Django documentation | Django](https://docs.djangoproject.com/en/5.0/releases/4.0/#generic-views)

While the class hierarchy has not changed, the flow through the methods has. The “delete” method is no longer used.

In practice, this type of pre-validation should be in the view that preceeds this. This view is not intended to be the view allowing you to choose what object to delete, but to confirm that a deletion should occur.

In other words, you should have a different view allowing the email address to be entered. _That_ view would accept the POST of the form containing the email address. If the view accepts that email address as valid, it can then redirect the user to your DeleteView for confirmation.

---

<div class="post-metadata">

**Author:** ![nmilner1234](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/nmilner1234/32/19690_2.png) [@nmilner1234](https://forum.djangoproject.com/u/nmilner1234)\
**Post date:** [March 12, 2024, 9:05pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/3 "2024-03-12T21:05:21Z")

</div>

But the fact the delete url is still exposed is a risk though, someone malocious could find out what the url is for the delete confirmation and then start adding random id numbers on the end meaning records could get spuriously deleted. For example this is part of the url for the delete confirmation form I have

```auto
/delete_person_to_event/11/

```

where 11 is the object id

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 12, 2024, 9:38pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/4 "2024-03-12T21:38:34Z")

</div>

Doing this across two steps does _not_ remove the absolute and definite requirement of **always** validating data being submitted.

The first step, of testing the submitted email address in the selection view, is only a matter of UI/UX convenience.

However, that does _not_ remove the necessity of verifying what’s being done in the second view. You must _never_, under _any_ circumstance trust what is being submitted from a browser.

What that means in this specific example is that you probably want to implement something like the UserPassesTestMixin to verify the user’s authority to even request the GET for the confirmation.

---

<div class="post-metadata">

**Author:** ![nmilner1234](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/nmilner1234/32/19690_2.png) [@nmilner1234](https://forum.djangoproject.com/u/nmilner1234)\
**Post date:** [March 13, 2024, 4:17pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/5 "2024-03-13T16:17:35Z")

</div>

Sure, that makes perfect sense. I’ve protected against people not logged in trying to use create/update/delete url’s with the use of the @login\_required decorator in the classes. Now I’m protecting against logged in people fiddling the integers within the url to access records that they shouldn’t have access to. I’ve implemented a check within form\_valid for one of my CreateView’s which works but when I call form\_invalid it doesn’t render the CreateView form again but instead seems to fall back to the url before the form. My questions today are:

1. Why is form valid not completing render\_to\_response()
2. Could I actually redirect to a totally different url from form\_valid()

```auto
    def form_invalid(self, form):
        return self.render_to_response(self.get_context_data(form=form))

    def form_valid(self, form):

        # Before we save, double check that someone hasn't fiddled the URL to save an event in 
        # an association they don't have access
        association = Association.objects.get(pk=self.kwargs['association_id'])
        
        if not self.request.user.has_perm('check_in_associations',association):
            self.form_invalid(form)

       ...

```

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 13, 2024, 4:31pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/6 "2024-03-13T16:31:49Z")

</div>

> [@nmilner1234](#):
>
> Could I actually redirect to a totally different url from form\_valid()

Absolutely. That’s actually the design intent of the edit CBVs. (That’s the purpose of the `success_url` attribute.)

> [@nmilner1234](#):
>
> Why is form valid not completing render\_to\_response()

I’m kind of lost here with what view and logic you’re trying to refer to.

First, you’re performing this test in the wrong location. The form\_valid method should be able to rely upon the fact that the form has been completely tested and validated.

The form\_invalid method is intented to redisplay the existing form, rendering any error messages that may be applicable.

Your `form_invalid` method is not returning a response. It looks like it’s trying to return a dict, which would throw an error. Also your attempt to call `form_invalid` isn’t doing anything with the data being returned from the call.

---

<div class="post-metadata">

**Author:** ![nmilner1234](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/nmilner1234/32/19690_2.png) [@nmilner1234](https://forum.djangoproject.com/u/nmilner1234)\
**Post date:** [March 13, 2024, 4:40pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/7 "2024-03-13T16:40:24Z")

</div>

I do the test in form\_valid because the form itself (form.py) does not have the field ‘association’ (foriegn key) as there is no need to send this to the client to then come back again. Active association id is defined from the integer in the url so I want to test that someone hasn’t altered the integer for association to trick an edit/create/delete in somewhere they would not have accessed if they had navigated naturally. Are you saying though that I could use `def clean(self):` in forms.py to check for this problem because association\_id is in kwargs and I have user info in `self.requests.user` ?

---

<div class="post-metadata">

**Author:** ![nmilner1234](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/nmilner1234/32/19690_2.png) [@nmilner1234](https://forum.djangoproject.com/u/nmilner1234)\
**Post date:** [March 13, 2024, 5:00pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/8 "2024-03-13T17:00:27Z")

</div>

ah but kwargs aren’t available in ModelForm 🤦‍♂️

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 13, 2024, 5:13pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/9 "2024-03-13T17:13:58Z")

</div>

Briefly, yes. Your clean method in your form is not limited to what is submitted by the form.

You can pass additional data to the form from the view. One of the easiest ways is to override get\_form, and add attributes to the form that is returned by `super().get_form(...)`.

Or, you can override `get_form_kwargs` such that these parameters are passed to ` __init__ `. You then define a custom ` __init__ ` method on the form to save those parameters.

Either way, you would then have those variables available to you in your clean method of the form.

---

<div class="post-metadata">

**Author:** ![nmilner1234](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/nmilner1234/32/19690_2.png) [@nmilner1234](https://forum.djangoproject.com/u/nmilner1234)\
**Post date:** [March 13, 2024, 5:30pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/10 "2024-03-13T17:30:08Z")

</div>

I’ve selected the easy option and passed kwarg value and `request.user` in `get_form()` but it seems clumsy to me. Please could you show me what the equivalent implmentation would be using **init** as this is something I have not yet tried?

```auto
def get_form(self, form_class=None):
        form = super(EventCreate, self).get_form(form_class)
        form.fields['when'].widget = AdminDateWidget(attrs={'type': 'date'})
        form.association_id = self.kwargs['association_id']
        form.current_user = self.request.user
        return form

```

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [March 13, 2024, 5:54pm UTC](https://forum.djangoproject.com/t/additional-checks-before-delete-using-deleteview/29006/11 "2024-03-13T17:54:43Z")

</div>

> [@nmilner1234](#):
>
> but it seems clumsy to me.

It’s actually the less-clumsy of the two options.

See the code in the thread at [django custom form - removing fields based on user](https://forum.djangoproject.com/t/django-custom-form-removing-fields-based-on-user/4291) for a sample implementation. (You don’t need to pay a lot of attention to the text, you’re primarily interested in the code.)
