diff --git a/bootstrap_modal_forms/mixins.py b/bootstrap_modal_forms/mixins.py index 9874264..8f8be3f 100644 --- a/bootstrap_modal_forms/mixins.py +++ b/bootstrap_modal_forms/mixins.py @@ -1,7 +1,7 @@ from django.contrib import messages from django.contrib.auth import login as auth_login from django.http import HttpResponseRedirect - +from django.db.models import ProtectedError class PassRequestMixin(object): """ @@ -48,11 +48,18 @@ def save(self, commit=True): class DeleteMessageMixin(object): """ Mixin which adds message to BSModalDeleteView. + Catch exceptions fired up by "on_delete=models.PROTECT" in the model fields. """ def post(self, request, *args, **kwargs): - messages.success(request, self.success_message) - return super(DeleteMessageMixin, self).delete(request, *args, **kwargs) + try: + httpResponse = super(DeleteMessageMixin, self).delete(request, *args, **kwargs) + messages.success(request, self.success_message) + return httpResponse + except ProtectedError: + messages.error(request, "Can not remove this item, has a relation in the database.") + return HttpResponseRedirect(self.success_url) + class LoginAjaxMixin(object): diff --git a/examples/migrations/0001_initial.py b/examples/migrations/0001_initial.py index eed1f88..da8d8fa 100644 --- a/examples/migrations/0001_initial.py +++ b/examples/migrations/0001_initial.py @@ -1,6 +1,7 @@ -# Generated by Django 2.1 on 2019-03-30 16:15 +# Generated by Django 2.1 on 2020-04-10 21:15 from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): @@ -11,6 +12,13 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='Vat', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('vat_percentage', models.IntegerField()), + ], + ), migrations.CreateModel( name='Book', fields=[ @@ -22,6 +30,7 @@ class Migration(migrations.Migration): ('pages', models.IntegerField(blank=True, null=True)), ('book_type', models.PositiveSmallIntegerField(choices=[(1, 'Hardcover'), (2, 'Paperback'), (3, 'E-book')])), ('timestamp', models.DateField(auto_now_add=True)), + ('vat', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='examples.Vat')), ], ), ] diff --git a/examples/models.py b/examples/models.py index fecc6d7..d92db29 100644 --- a/examples/models.py +++ b/examples/models.py @@ -1,6 +1,9 @@ from django.db import models +class Vat(models.Model): + vat_percentage = models.IntegerField() + class Book(models.Model): HARDCOVER = 1 PAPERBACK = 2 @@ -16,5 +19,5 @@ class Book(models.Model): price = models.DecimalField(max_digits=5, decimal_places=2) pages = models.IntegerField(blank=True, null=True) book_type = models.PositiveSmallIntegerField(choices=BOOK_TYPES) - + vat = models.ForeignKey(Vat, on_delete=models.PROTECT, blank=True, null=True) timestamp = models.DateField(auto_now_add=True, auto_now=False) diff --git a/examples/templates/examples/delete_vat.html b/examples/templates/examples/delete_vat.html new file mode 100644 index 0000000..ae371fd --- /dev/null +++ b/examples/templates/examples/delete_vat.html @@ -0,0 +1,22 @@ +{% load widget_tweaks %} + +
+ {% csrf_token %} + + + + + + + +
diff --git a/examples/urls.py b/examples/urls.py index 149f1ff..15cf3d8 100644 --- a/examples/urls.py +++ b/examples/urls.py @@ -9,6 +9,7 @@ path('update/', views.BookUpdateView.as_view(), name='update_book'), path('read/', views.BookReadView.as_view(), name='read_book'), path('delete/', views.BookDeleteView.as_view(), name='delete_book'), + path('delete_vat/', views.VatDeleteView.as_view(), name='delete_vat'), path('signup/', views.SignUpView.as_view(), name='signup'), path('login/', views.CustomLoginView.as_view(), name='login'), ] diff --git a/examples/views.py b/examples/views.py index 1c68fc4..e81cdba 100644 --- a/examples/views.py +++ b/examples/views.py @@ -8,7 +8,7 @@ BSModalDeleteView) from .forms import BookForm, CustomUserCreationForm, CustomAuthenticationForm -from .models import Book +from .models import Book, Vat class Index(generic.ListView): @@ -43,7 +43,12 @@ class BookDeleteView(BSModalDeleteView): success_message = 'Success: Book was deleted.' success_url = reverse_lazy('index') - +class VatDeleteView(BSModalDeleteView): + model = Vat + template_name = 'examples/delete_vat.html' + success_message = 'Success: Vat was deleted.' + success_url = reverse_lazy('index') + class SignUpView(BSModalCreateView): form_class = CustomUserCreationForm template_name = 'examples/signup.html' diff --git a/tests/tests_unit.py b/tests/tests_unit.py index 9dd8555..5625d51 100644 --- a/tests/tests_unit.py +++ b/tests/tests_unit.py @@ -2,19 +2,27 @@ from django.contrib.messages import get_messages from django.test import TestCase +from examples.models import Vat from examples.models import Book class MixinsTest(TestCase): def setUp(self): + self.vat_1 = Vat.objects.create( + vat_percentage=28 + ) + self.vat_2 = Vat.objects.create( + vat_percentage=13 + ) self.book = Book.objects.create( title='Life of Jane Doe', publication_date='2019-01-01', author='Jane Doe', price=29.99, pages=477, - book_type=2 + book_type=2, + vat=self.vat_1 ) self.user = User.objects.create_user( username='user', @@ -56,7 +64,8 @@ def test_CreateUpdateAjaxMixin(self): 'price': 19.99, 'pages': 449, # Wrong value - 'book_type': 'wrong_value' + 'book_type': 'wrong_value', + 'vat': 1 }, HTTP_X_REQUESTED_WITH='XMLHttpRequest' ) @@ -78,7 +87,8 @@ def test_CreateUpdateAjaxMixin(self): 'author': 'John Doe', 'price': 19.99, 'pages': 449, - 'book_type': 1 + 'book_type': 1, + 'vat': 1 } ) @@ -103,7 +113,8 @@ def test_CreateUpdateAjaxMixin(self): 'price': 29.99, 'pages': 477, # Wrong value - 'book_type': 'wrong_value' + 'book_type': 'wrong_value', + 'vat': 1 }, HTTP_X_REQUESTED_WITH='XMLHttpRequest' ) @@ -125,7 +136,8 @@ def test_CreateUpdateAjaxMixin(self): 'author': 'Jane Doe', 'price': 29.99, 'pages': 477, - 'book_type': 2 + 'book_type': 2, + 'vat': 1 }, ) @@ -140,11 +152,22 @@ def test_DeleteMessageMixin(self): """ Delete object through BSModalDeleteView. """ + + response = self.client.post('/delete_vat/1') + messages = get_messages(response.wsgi_request) + self.assertEqual(len(messages), 1) + self.assertEqual(str(list(iter(messages))[0]), 'Can not remove this item, has a relation in the database.') + + response = self.client.post('/delete_vat/2') + messages = get_messages(response.wsgi_request) + self.assertEqual(str(list(iter(messages))[1]), 'Success: Vat was deleted.') # Request to delete view passes message to the response response = self.client.post('/delete/1') messages = get_messages(response.wsgi_request) - self.assertEqual(len(messages), 1) + self.assertEqual(len(messages), 3) + self.assertEqual(str(list(iter(messages))[2]), 'Success: Book was deleted.') + def test_LoginAjaxMixin(self): """