mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-03 22:55:43 +00:00 
			
		
		
		
	Merge pull request #962 from eeintech/part_ipn_slug
Added PartDetailFromIPN view (subclass of PartDetail) and url pattern
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 """
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -99,7 +99,7 @@ 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<pk>\d+)/', include(part_detail_urls)),
 | 
			
		||||
 | 
			
		||||
    # Part category
 | 
			
		||||
@@ -124,6 +124,9 @@ part_urls = [
 | 
			
		||||
    # Bom Items
 | 
			
		||||
    url(r'^bom/(?P<pk>\d+)/', include(part_bom_urls)),
 | 
			
		||||
 | 
			
		||||
    # Individual part using IPN as slug
 | 
			
		||||
    url(r'^(?P<slug>[-\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'),
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
@@ -663,6 +663,43 @@ 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.MultipleObjectsReturned:
 | 
			
		||||
                pass
 | 
			
		||||
            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 """
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user