From 2fa284d37bcb97aca87162807e0abe6e6b369cc3 Mon Sep 17 00:00:00 2001 From: eeintech Date: Fri, 4 Sep 2020 17:20:17 -0500 Subject: [PATCH 1/4] Added PartDetailFromIPN view (subclass of PartDetail) and url pattern --- InvenTree/part/urls.py | 4 +++- InvenTree/part/views.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 7d5c279e75..037c792480 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -99,8 +99,10 @@ part_urls = [ # Export data for multiple parts url(r'^export/', views.PartExport.as_view(), name='part-export'), - # Individual part + # Individual part using pk url(r'^(?P\d+)/', include(part_detail_urls)), + # Individual part using IPN as slug + url(r'^(?P[-\w]+)/', views.PartDetailFromIPN.as_view(), name='part-detail-from-ipn'), # Part category url(r'^category/(?P\d+)/', include(part_category_urls)), diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index f2d0089e60..ed6367eba4 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -647,6 +647,41 @@ class PartDetail(DetailView): return context +class PartDetailFromIPN(PartDetail): + slug_field = 'IPN' + slug_url_kwarg = 'slug' + + def get_object(self): + """ Return Part object which IPN field matches the slug value """ + queryset = self.get_queryset() + # Get slug + slug = self.kwargs.get(self.slug_url_kwarg) + + if slug is not None: + slug_field = self.get_slug_field() + # Filter by the slug value + queryset = queryset.filter(**{slug_field: slug}) + + try: + # Get unique part from queryset + part = queryset.get() + # Return Part object + return part + except queryset.model.DoesNotExist: + pass + + return None + + def get(self, request, *args, **kwargs): + """ Attempt to match slug to a Part, else redirect to PartIndex view """ + self.object = self.get_object() + + if not self.object: + return HttpResponseRedirect(reverse('part-index')) + + return super(PartDetailFromIPN, self).get(request, *args, **kwargs) + + class PartQRCode(QRCodeView): """ View for displaying a QR code for a Part object """ From 41071f0ed42cebacd396633944bab21a40d18bb3 Mon Sep 17 00:00:00 2001 From: eeintech Date: Sat, 5 Sep 2020 12:03:07 -0500 Subject: [PATCH 2/4] Moved IPN url pattern to right before Part index to avoid catching others --- InvenTree/part/urls.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 037c792480..2707a563c7 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -101,8 +101,6 @@ part_urls = [ # Individual part using pk url(r'^(?P\d+)/', include(part_detail_urls)), - # Individual part using IPN as slug - url(r'^(?P[-\w]+)/', views.PartDetailFromIPN.as_view(), name='part-detail-from-ipn'), # Part category url(r'^category/(?P\d+)/', include(part_category_urls)), @@ -126,6 +124,9 @@ part_urls = [ # Bom Items url(r'^bom/(?P\d+)/', include(part_bom_urls)), + # Individual part using IPN as slug + url(r'^(?P[-\w]+)/', views.PartDetailFromIPN.as_view(), name='part-detail-from-ipn'), + # Top level part list (display top level parts and categories) url(r'^.*$', views.PartIndex.as_view(), name='part-index'), ] From c5efce36b6ab6110c9dd63b3f2e99ce4d32fb113 Mon Sep 17 00:00:00 2001 From: eeintech Date: Mon, 7 Sep 2020 08:40:37 -0500 Subject: [PATCH 3/4] Added PartDetailFromIPN view tests --- InvenTree/InvenTree/ci_postgresql.py | 2 +- InvenTree/part/test_views.py | 50 ++++++++++++++++++++++++++++ InvenTree/part/views.py | 2 ++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/ci_postgresql.py b/InvenTree/InvenTree/ci_postgresql.py index ba9aa26bb1..67bb24540f 100644 --- a/InvenTree/InvenTree/ci_postgresql.py +++ b/InvenTree/InvenTree/ci_postgresql.py @@ -6,7 +6,7 @@ from InvenTree.settings import * # Override the 'test' database if 'test' in sys.argv: - eprint('InvenTree: Running tests - Using MySQL test database') + eprint('InvenTree: Running tests - Using PostGreSQL test database') DATABASES['default'] = { # Ensure postgresql backend is being used diff --git a/InvenTree/part/test_views.py b/InvenTree/part/test_views.py index 16711c78f6..43ba373e58 100644 --- a/InvenTree/part/test_views.py +++ b/InvenTree/part/test_views.py @@ -78,6 +78,56 @@ class PartDetailTest(PartViewTestCase): self.assertEqual(response.status_code, 200) self.assertTrue(response.context['editing_enabled']) + def test_part_detail_from_ipn(self): + """ + Test that we can retrieve a part detail page from part IPN: + - if no part with matching IPN -> return part index + - if unique IPN match -> return part detail page + - if multiple IPN matches -> return part index + """ + ipn_test = 'PART-000000-AA' + pk = 1 + + def test_ipn_match(index_result=False, detail_result=False): + index_redirect = False + detail_redirect = False + + response = self.client.get(reverse('part-detail-from-ipn', args=(ipn_test,))) + + # Check for PartIndex redirect + try: + if response.url == '/part/': + index_redirect = True + except AttributeError: + pass + + # Check for PartDetail redirect + try: + if response.context['part'].pk == pk: + detail_redirect = True + except TypeError: + pass + + self.assertEqual(index_result, index_redirect) + self.assertEqual(detail_result, detail_redirect) + + # Test no match + test_ipn_match(index_result=True, detail_result=False) + + # Test unique match + part = Part.objects.get(pk=pk) + part.IPN = ipn_test + part.save() + + test_ipn_match(index_result=False, detail_result=True) + + # Test multiple matches + part = Part.objects.get(pk=pk+1) + part.IPN = ipn_test + part.save() + + test_ipn_match(index_result=True, detail_result=False) + def test_bom_download(self): """ Test downloading a BOM for a valid part """ diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index ef5c7a41a2..58ece9d0b0 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -683,6 +683,8 @@ class PartDetailFromIPN(PartDetail): part = queryset.get() # Return Part object return part + except queryset.model.MultipleObjectsReturned: + pass except queryset.model.DoesNotExist: pass From 27241e217f77a74a0a3b25d9d0b3c1daa664fccb Mon Sep 17 00:00:00 2001 From: eeintech Date: Mon, 7 Sep 2020 08:46:39 -0500 Subject: [PATCH 4/4] Style correction --- InvenTree/part/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/test_views.py b/InvenTree/part/test_views.py index 43ba373e58..bc09784a47 100644 --- a/InvenTree/part/test_views.py +++ b/InvenTree/part/test_views.py @@ -122,7 +122,7 @@ class PartDetailTest(PartViewTestCase): test_ipn_match(index_result=False, detail_result=True) # Test multiple matches - part = Part.objects.get(pk=pk+1) + part = Part.objects.get(pk=pk + 1) part.IPN = ipn_test part.save()