mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	Begin to add views for part models
- List BOM items - Show category listing as linked items - Fix some pathing issues with InvenTreeTree class
This commit is contained in:
		| @@ -38,7 +38,7 @@ class InvenTreeTree(models.Model): | |||||||
|         abstract = True |         abstract = True | ||||||
|         unique_together = ('name', 'parent') |         unique_together = ('name', 'parent') | ||||||
|  |  | ||||||
|     name = models.CharField(max_length=100) |     name = models.CharField(max_length=100, unique=True) | ||||||
|     description = models.CharField(max_length=250, blank=True) |     description = models.CharField(max_length=250, blank=True) | ||||||
|     parent = models.ForeignKey('self', |     parent = models.ForeignKey('self', | ||||||
|                                on_delete=models.CASCADE, |                                on_delete=models.CASCADE, | ||||||
| @@ -126,10 +126,11 @@ class InvenTreeTree(models.Model): | |||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def path(self): |     def path(self): | ||||||
|         if self.parent: |         return self.parentpath + [self] | ||||||
|             return "/".join([p.name for p in self.parentpath]) + "/" + self.name |  | ||||||
|         else: |     @property | ||||||
|             return self.name |     def pathstring(self): | ||||||
|  |         return '/'.join([item.name for item in self.path]) | ||||||
|  |  | ||||||
|     def __setattr__(self, attrname, val): |     def __setattr__(self, attrname, val): | ||||||
|         """ Custom Attribute Setting function |         """ Custom Attribute Setting function | ||||||
| @@ -174,7 +175,7 @@ class InvenTreeTree(models.Model): | |||||||
|             This is recursive - Make it not so. |             This is recursive - Make it not so. | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|         return self.path |         return self.pathstring | ||||||
|  |  | ||||||
|  |  | ||||||
| def FilterChildren(queryset, parent): | def FilterChildren(queryset, parent): | ||||||
|   | |||||||
| @@ -3,7 +3,8 @@ from django.contrib import admin | |||||||
|  |  | ||||||
| from rest_framework.documentation import include_docs_urls | from rest_framework.documentation import include_docs_urls | ||||||
|  |  | ||||||
| from part.urls import part_urls, part_cat_urls | from part.urls import part_api_urls, part_cat_api_urls | ||||||
|  | from part.urls import part_urls | ||||||
| from bom.urls import bom_urls | from bom.urls import bom_urls | ||||||
| from stock.urls import stock_urls, stock_loc_urls | from stock.urls import stock_urls, stock_loc_urls | ||||||
| from supplier.urls import cust_urls, manu_urls, supplier_part_urls, price_break_urls, supplier_urls | from supplier.urls import cust_urls, manu_urls, supplier_part_urls, price_break_urls, supplier_urls | ||||||
| @@ -13,6 +14,8 @@ from supplier.urls import cust_urls, manu_urls, supplier_part_urls, price_break_ | |||||||
|  |  | ||||||
| from users.urls import user_urls | from users.urls import user_urls | ||||||
|  |  | ||||||
|  | from . import views | ||||||
|  |  | ||||||
| admin.site.site_header = "InvenTree Admin" | admin.site.site_header = "InvenTree Admin" | ||||||
|  |  | ||||||
| apipatterns = [ | apipatterns = [ | ||||||
| @@ -22,8 +25,8 @@ apipatterns = [ | |||||||
|     url(r'^stock-location/', include(stock_loc_urls)), |     url(r'^stock-location/', include(stock_loc_urls)), | ||||||
|  |  | ||||||
|     # Part URLs |     # Part URLs | ||||||
|     url(r'^part/', include(part_urls)), |     url(r'^part/', include(part_api_urls)), | ||||||
|     url(r'^part-category/', include(part_cat_urls)), |     url(r'^part-category/', include(part_cat_api_urls)), | ||||||
|     #url(r'^part-param/', include(part_param_urls)), |     #url(r'^part-param/', include(part_param_urls)), | ||||||
|     #url(r'^part-param-template/', include(part_param_template_urls)), |     #url(r'^part-param-template/', include(part_param_template_urls)), | ||||||
|  |  | ||||||
| @@ -52,9 +55,12 @@ apipatterns = [ | |||||||
| ] | ] | ||||||
|  |  | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|  |  | ||||||
|     # API URL |     # API URL | ||||||
|     url(r'^api/', include(apipatterns)), |     url(r'^api/', include(apipatterns)), | ||||||
|  |  | ||||||
|  |     url(r'^part/', include(part_urls)), | ||||||
|  |  | ||||||
|     url(r'^api-doc/', include_docs_urls(title='InvenTree API')), |     url(r'^api-doc/', include_docs_urls(title='InvenTree API')), | ||||||
|  |  | ||||||
|     url(r'^admin/', admin.site.urls), |     url(r'^admin/', admin.site.urls), | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ from rest_framework import serializers | |||||||
| from .models import BomItem | from .models import BomItem | ||||||
|  |  | ||||||
|  |  | ||||||
| class BomItemSerializer(serializers.HyperlinkedModelSerializer): | class BomItemSerializer(serializers.ModelSerializer): | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = BomItem |         model = BomItem | ||||||
|   | |||||||
| @@ -4,12 +4,12 @@ from .models import PartCategory, Part | |||||||
|  |  | ||||||
| class PartAdmin(admin.ModelAdmin): | class PartAdmin(admin.ModelAdmin): | ||||||
|  |  | ||||||
|     list_display = ('name', 'IPN', 'stock', 'category') |     list_display = ('name', 'IPN', 'description', 'stock', 'category') | ||||||
|  |  | ||||||
|  |  | ||||||
| class PartCategoryAdmin(admin.ModelAdmin): | class PartCategoryAdmin(admin.ModelAdmin): | ||||||
|  |  | ||||||
|     list_display = ('name', 'path', 'description') |     list_display = ('name', 'pathstring', 'description') | ||||||
|  |  | ||||||
| """ | """ | ||||||
| class ParameterTemplateAdmin(admin.ModelAdmin): | class ParameterTemplateAdmin(admin.ModelAdmin): | ||||||
|   | |||||||
| @@ -36,7 +36,7 @@ class Part(models.Model): | |||||||
|     IPN = models.CharField(max_length=100, blank=True) |     IPN = models.CharField(max_length=100, blank=True) | ||||||
|  |  | ||||||
|     # Part category - all parts must be assigned to a category |     # Part category - all parts must be assigned to a category | ||||||
|     category = models.ForeignKey(PartCategory, on_delete=models.CASCADE) |     category = models.ForeignKey(PartCategory, on_delete=models.CASCADE, related_name='parts') | ||||||
|  |  | ||||||
|     # Minimum "allowed" stock level |     # Minimum "allowed" stock level | ||||||
|     minimum_stock = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)]) |     minimum_stock = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)]) | ||||||
| @@ -76,10 +76,20 @@ class Part(models.Model): | |||||||
|         return result['total'] |         return result['total'] | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def projects(self): |     def bomItemCount(self): | ||||||
|         """ Return a list of unique projects that this part is associated with. |         return self.bom_items.all().count() | ||||||
|         A part may be used in zero or more projects. |  | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def usedInCount(self): | ||||||
|  |         return self.used_in.all().count() | ||||||
|  |  | ||||||
|     """ |     """ | ||||||
|  |     @property | ||||||
|  |     def projects(self): | ||||||
|  |         " Return a list of unique projects that this part is associated with. | ||||||
|  |         A part may be used in zero or more projects. | ||||||
|  |         " | ||||||
|  |  | ||||||
|         project_ids = set() |         project_ids = set() | ||||||
|         project_parts = self.projectpart_set.all() |         project_parts = self.projectpart_set.all() | ||||||
| @@ -92,6 +102,6 @@ class Part(models.Model): | |||||||
|                 projects.append(pp.project) |                 projects.append(pp.project) | ||||||
|  |  | ||||||
|         return projects |         return projects | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,7 +17,8 @@ class PartParameterSerializer(serializers.HyperlinkedModelSerializer): | |||||||
|                   'units') |                   'units') | ||||||
| """ | """ | ||||||
|  |  | ||||||
| class PartSerializer(serializers.HyperlinkedModelSerializer): | #class PartSerializer(serializers.HyperlinkedModelSerializer): | ||||||
|  | class PartSerializer(serializers.ModelSerializer): | ||||||
|     """ Serializer for complete detail information of a part. |     """ Serializer for complete detail information of a part. | ||||||
|     Used when displaying all details of a single component. |     Used when displaying all details of a single component. | ||||||
|     """ |     """ | ||||||
| @@ -31,7 +32,8 @@ class PartSerializer(serializers.HyperlinkedModelSerializer): | |||||||
|                   'category', |                   'category', | ||||||
|                   'stock', |                   'stock', | ||||||
|                   'units', |                   'units', | ||||||
|                   'trackable') |                   'trackable', | ||||||
|  |                   ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class PartCategorySerializer(serializers.HyperlinkedModelSerializer): | class PartCategorySerializer(serializers.HyperlinkedModelSerializer): | ||||||
| @@ -42,7 +44,7 @@ class PartCategorySerializer(serializers.HyperlinkedModelSerializer): | |||||||
|                   'name', |                   'name', | ||||||
|                   'description', |                   'description', | ||||||
|                   'parent', |                   'parent', | ||||||
|                   'path') |                   'pathstring') | ||||||
|  |  | ||||||
| """ | """ | ||||||
| class PartTemplateSerializer(serializers.HyperlinkedModelSerializer): | class PartTemplateSerializer(serializers.HyperlinkedModelSerializer): | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								InvenTree/part/templates/cat_link.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								InvenTree/part/templates/cat_link.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | <div id="category-path" background="#99F"> | ||||||
|  | <a href="/part/list/">All</a> > | ||||||
|  | {% for path_item in category.parentpath %} | ||||||
|  | <a href="/part/list/?category={{ path_item.id }}">{{ path_item.name }}</a> > | ||||||
|  | {% endfor %} | ||||||
|  | <a href="/part/list/?category={{ category.id }}">{{ category.name }}</a> | ||||||
|  | </div> | ||||||
							
								
								
									
										30
									
								
								InvenTree/part/templates/detail.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								InvenTree/part/templates/detail.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | <h1>Part details for {{ part.name }}</h1> | ||||||
|  |  | ||||||
|  | {% include "cat_link.html" with category=part.category %} | ||||||
|  |  | ||||||
|  | <br> | ||||||
|  | Part name: {{ part.name }} | ||||||
|  | <br> | ||||||
|  | Description: {{ part.description }} | ||||||
|  | <br> | ||||||
|  | IPN: {% if part.IPN %}{{ part.IPN }}{% else %}N/A{% endif %} | ||||||
|  | <br> | ||||||
|  | Stock: {{ part.stock }} | ||||||
|  |  | ||||||
|  | <br><br> | ||||||
|  | BOM items: {{ part.bomItemCount }}<br> | ||||||
|  | Used in {{ part.usedInCount }} other parts.<br> | ||||||
|  |  | ||||||
|  | <h2>BOM</h2> | ||||||
|  | <ul> | ||||||
|  | {% for bom in part.bom_items.all %} | ||||||
|  | <li><a href="{% url 'detail' bom.sub_part.id %}">{{ bom.sub_part.name }}</a> ({{ bom.quantity }})</li> | ||||||
|  | {% endfor %} | ||||||
|  | </ul> | ||||||
|  |  | ||||||
|  | <h2>Used to make</h2> | ||||||
|  | <ul> | ||||||
|  | {% for p in part.used_in.all %} | ||||||
|  | <li><a href="{% url 'detail' p.part.id %}">{{ p.part.name }}</a></li> | ||||||
|  | {% endfor %} | ||||||
|  | </ul> | ||||||
							
								
								
									
										27
									
								
								InvenTree/part/templates/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								InvenTree/part/templates/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | <h1>Parts page!</h1> | ||||||
|  |  | ||||||
|  | {% if category %} | ||||||
|  | {% include "cat_link.html" with category=category %} | ||||||
|  | <h3>Child categories</h3> | ||||||
|  | <ul> | ||||||
|  | {% for child in category.children.all %} | ||||||
|  | <li><a href="/part/list?category={{ child.id }}">{{ child.name }}</a></li> | ||||||
|  | {% endfor %} | ||||||
|  | </ul> | ||||||
|  | {% else %} | ||||||
|  | No category! | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | <b>Here is a list of all the parts:</b> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | <table> | ||||||
|  | {% for part in parts %} | ||||||
|  | <tr> | ||||||
|  |     <td><a href="{% url 'detail' part.id %}">{{ part.name }}</a></td> | ||||||
|  |     <td>{{ part.description }}</td> | ||||||
|  | </tr> | ||||||
|  | {% endfor %} | ||||||
|  | </table> | ||||||
|  |  | ||||||
| @@ -1,8 +1,12 @@ | |||||||
| from django.conf.urls import url | from django.conf.urls import url | ||||||
|  | from django.views.generic.base import RedirectView | ||||||
|  |  | ||||||
| from . import views | from . import views | ||||||
|  |  | ||||||
| part_cat_urls = [ | app_nam='part' | ||||||
|  |  | ||||||
|  | # URL list for part category API | ||||||
|  | part_cat_api_urls = [ | ||||||
|  |  | ||||||
|     # Part category detail |     # Part category detail | ||||||
|     url(r'^(?P<pk>[0-9]+)/?$', views.PartCategoryDetail.as_view(), name='partcategory-detail'), |     url(r'^(?P<pk>[0-9]+)/?$', views.PartCategoryDetail.as_view(), name='partcategory-detail'), | ||||||
| @@ -12,7 +16,9 @@ part_cat_urls = [ | |||||||
|     url(r'^$', views.PartCategoryList.as_view()) |     url(r'^$', views.PartCategoryList.as_view()) | ||||||
| ] | ] | ||||||
|  |  | ||||||
| part_urls = [ |  | ||||||
|  | # URL list for part API | ||||||
|  | part_api_urls = [ | ||||||
|  |  | ||||||
|     # Individual part |     # Individual part | ||||||
|     url(r'^(?P<pk>[0-9]+)/?$', views.PartDetail.as_view(), name='part-detail'), |     url(r'^(?P<pk>[0-9]+)/?$', views.PartDetail.as_view(), name='part-detail'), | ||||||
| @@ -22,6 +28,17 @@ part_urls = [ | |||||||
|     url(r'^$', views.PartList.as_view()), |     url(r'^$', views.PartList.as_view()), | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | # URL list for part web interface | ||||||
|  | part_urls = [ | ||||||
|  |     # Individual | ||||||
|  |     url(r'^(?P<pk>\d+)/$', views.detail, name='detail'), | ||||||
|  |     # ex: /part/ | ||||||
|  |     url('list', views.index, name='index'), | ||||||
|  |     # ex: /part/5/ | ||||||
|  |  | ||||||
|  |      url(r'^.*$', RedirectView.as_view(url='list', permanent=False), name='index'), | ||||||
|  | ] | ||||||
|  |  | ||||||
| """ | """ | ||||||
| part_param_urls = [ | part_param_urls = [ | ||||||
|     # Detail of a single part parameter |     # Detail of a single part parameter | ||||||
|   | |||||||
| @@ -1,15 +1,72 @@ | |||||||
| from django_filters.rest_framework import FilterSet, DjangoFilterBackend | from django_filters.rest_framework import FilterSet, DjangoFilterBackend | ||||||
|  |  | ||||||
|  | # Template stuff (WIP) | ||||||
|  | from django.http import HttpResponse | ||||||
|  | from django.template import loader | ||||||
|  |  | ||||||
| from rest_framework import generics, permissions | from rest_framework import generics, permissions | ||||||
|  |  | ||||||
| from InvenTree.models import FilterChildren | from InvenTree.models import FilterChildren | ||||||
| from .models import PartCategory, Part | from .models import PartCategory, Part | ||||||
|  |  | ||||||
|  | from django.shortcuts import get_object_or_404, render | ||||||
|  | from django.http import HttpResponseRedirect | ||||||
|  | from django.urls import reverse | ||||||
|  | from django.views import generic | ||||||
|  |  | ||||||
| from .serializers import PartSerializer | from .serializers import PartSerializer | ||||||
| from .serializers import PartCategorySerializer | from .serializers import PartCategorySerializer | ||||||
| #from .serializers import PartParameterSerializer | #from .serializers import PartParameterSerializer | ||||||
| #from .serializers import PartTemplateSerializer | #from .serializers import PartTemplateSerializer | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | class IndexView(generic.ListView): | ||||||
|  |     template_name = 'index.html' | ||||||
|  |     context_object_name = 'parts' | ||||||
|  |  | ||||||
|  |     def get_queryset(self): | ||||||
|  |         "Return the last five published questions." | ||||||
|  |         return Part.objects.all() | ||||||
|  |  | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | def index(request): | ||||||
|  |     template = loader.get_template('index.html') | ||||||
|  |  | ||||||
|  |     parts = Part.objects.all() | ||||||
|  |  | ||||||
|  |     cat = None | ||||||
|  |  | ||||||
|  |     if 'category' in request.GET: | ||||||
|  |         cat_id = request.GET['category'] | ||||||
|  |  | ||||||
|  |         cat = get_object_or_404(PartCategory, pk=cat_id) | ||||||
|  |         #cat = PartCategory.objects.get(pk=cat_id) | ||||||
|  |         parts = parts.filter(category = cat_id) | ||||||
|  |  | ||||||
|  |     context = { | ||||||
|  |         'parts' : parts.order_by('category__name'), | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if cat: | ||||||
|  |         context['category'] = cat | ||||||
|  |  | ||||||
|  |     return HttpResponse(template.render(context, request)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def detail(request, pk): | ||||||
|  |     #template = loader.get_template('detail.html') | ||||||
|  |  | ||||||
|  |     part = get_object_or_404(Part, pk=pk) | ||||||
|  |  | ||||||
|  |     return render(request, 'detail.html', {'part' : part}) | ||||||
|  |  | ||||||
|  |     #return HttpResponse("You're looking at part %s." % pk) | ||||||
|  |  | ||||||
|  | #def results(request, question_id): | ||||||
|  | #    response = "You're looking at the results of question %s." | ||||||
|  | #    return HttpResponse(response % question_id) | ||||||
|  |  | ||||||
|  |  | ||||||
| class PartDetail(generics.RetrieveUpdateDestroyAPIView): | class PartDetail(generics.RetrieveUpdateDestroyAPIView): | ||||||
|     """ |     """ | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ from .models import ProjectCategory, Project, ProjectPart, ProjectRun | |||||||
|  |  | ||||||
|  |  | ||||||
| class ProjectCategoryAdmin(admin.ModelAdmin): | class ProjectCategoryAdmin(admin.ModelAdmin): | ||||||
|     list_display = ('name', 'path', 'description') |     list_display = ('name', 'pathstring', 'description') | ||||||
|  |  | ||||||
|  |  | ||||||
| class ProjectAdmin(admin.ModelAdmin): | class ProjectAdmin(admin.ModelAdmin): | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ class ProjectCategorySerializer(serializers.HyperlinkedModelSerializer): | |||||||
|                   'name', |                   'name', | ||||||
|                   'description', |                   'description', | ||||||
|                   'parent', |                   'parent', | ||||||
|                   'path') |                   'pathstring') | ||||||
|  |  | ||||||
|  |  | ||||||
| class ProjectRunSerializer(serializers.HyperlinkedModelSerializer): | class ProjectRunSerializer(serializers.HyperlinkedModelSerializer): | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ from .models import StockLocation, StockItem | |||||||
|  |  | ||||||
|  |  | ||||||
| class LocationAdmin(admin.ModelAdmin): | class LocationAdmin(admin.ModelAdmin): | ||||||
|     list_display = ('name', 'path', 'description') |     list_display = ('name', 'pathstring', 'description') | ||||||
|  |  | ||||||
|  |  | ||||||
| class StockItemAdmin(SimpleHistoryAdmin): | class StockItemAdmin(SimpleHistoryAdmin): | ||||||
|   | |||||||
| @@ -48,4 +48,4 @@ class LocationSerializer(serializers.HyperlinkedModelSerializer): | |||||||
|                   'name', |                   'name', | ||||||
|                   'description', |                   'description', | ||||||
|                   'parent', |                   'parent', | ||||||
|                   'path') |                   'pathstring') | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user