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:
Oliver 2018-04-13 22:36:59 +10:00
parent 77fe0dc542
commit bd46f66d6b
15 changed files with 182 additions and 25 deletions

View File

@ -38,7 +38,7 @@ class InvenTreeTree(models.Model):
abstract = True
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)
parent = models.ForeignKey('self',
on_delete=models.CASCADE,
@ -126,10 +126,11 @@ class InvenTreeTree(models.Model):
@property
def path(self):
if self.parent:
return "/".join([p.name for p in self.parentpath]) + "/" + self.name
else:
return self.name
return self.parentpath + [self]
@property
def pathstring(self):
return '/'.join([item.name for item in self.path])
def __setattr__(self, attrname, val):
""" Custom Attribute Setting function
@ -174,7 +175,7 @@ class InvenTreeTree(models.Model):
This is recursive - Make it not so.
"""
return self.path
return self.pathstring
def FilterChildren(queryset, parent):

View File

@ -3,7 +3,8 @@ from django.contrib import admin
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 stock.urls import stock_urls, stock_loc_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 . import views
admin.site.site_header = "InvenTree Admin"
apipatterns = [
@ -22,8 +25,8 @@ apipatterns = [
url(r'^stock-location/', include(stock_loc_urls)),
# Part URLs
url(r'^part/', include(part_urls)),
url(r'^part-category/', include(part_cat_urls)),
url(r'^part/', include(part_api_urls)),
url(r'^part-category/', include(part_cat_api_urls)),
#url(r'^part-param/', include(part_param_urls)),
#url(r'^part-param-template/', include(part_param_template_urls)),
@ -52,9 +55,12 @@ apipatterns = [
]
urlpatterns = [
# API URL
url(r'^api/', include(apipatterns)),
url(r'^part/', include(part_urls)),
url(r'^api-doc/', include_docs_urls(title='InvenTree API')),
url(r'^admin/', admin.site.urls),

View File

@ -3,7 +3,7 @@ from rest_framework import serializers
from .models import BomItem
class BomItemSerializer(serializers.HyperlinkedModelSerializer):
class BomItemSerializer(serializers.ModelSerializer):
class Meta:
model = BomItem

View File

@ -4,12 +4,12 @@ from .models import PartCategory, Part
class PartAdmin(admin.ModelAdmin):
list_display = ('name', 'IPN', 'stock', 'category')
list_display = ('name', 'IPN', 'description', 'stock', 'category')
class PartCategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'path', 'description')
list_display = ('name', 'pathstring', 'description')
"""
class ParameterTemplateAdmin(admin.ModelAdmin):

View File

@ -36,7 +36,7 @@ class Part(models.Model):
IPN = models.CharField(max_length=100, blank=True)
# 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_stock = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)])
@ -75,11 +75,21 @@ class Part(models.Model):
result = stocks.aggregate(total=Sum('quantity'))
return result['total']
@property
def bomItemCount(self):
return self.bom_items.all().count()
@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.
" 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_parts = self.projectpart_set.all()
@ -92,6 +102,6 @@ class Part(models.Model):
projects.append(pp.project)
return projects
"""

View File

@ -17,7 +17,8 @@ class PartParameterSerializer(serializers.HyperlinkedModelSerializer):
'units')
"""
class PartSerializer(serializers.HyperlinkedModelSerializer):
#class PartSerializer(serializers.HyperlinkedModelSerializer):
class PartSerializer(serializers.ModelSerializer):
""" Serializer for complete detail information of a part.
Used when displaying all details of a single component.
"""
@ -31,7 +32,8 @@ class PartSerializer(serializers.HyperlinkedModelSerializer):
'category',
'stock',
'units',
'trackable')
'trackable',
)
class PartCategorySerializer(serializers.HyperlinkedModelSerializer):
@ -42,7 +44,7 @@ class PartCategorySerializer(serializers.HyperlinkedModelSerializer):
'name',
'description',
'parent',
'path')
'pathstring')
"""
class PartTemplateSerializer(serializers.HyperlinkedModelSerializer):

View 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>

View 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>

View 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>

View File

@ -1,8 +1,12 @@
from django.conf.urls import url
from django.views.generic.base import RedirectView
from . import views
part_cat_urls = [
app_nam='part'
# URL list for part category API
part_cat_api_urls = [
# Part category 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())
]
part_urls = [
# URL list for part API
part_api_urls = [
# Individual part
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 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 = [
# Detail of a single part parameter

View File

@ -1,15 +1,72 @@
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 InvenTree.models import FilterChildren
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 PartCategorySerializer
#from .serializers import PartParameterSerializer
#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):
"""

View File

@ -4,7 +4,7 @@ from .models import ProjectCategory, Project, ProjectPart, ProjectRun
class ProjectCategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'path', 'description')
list_display = ('name', 'pathstring', 'description')
class ProjectAdmin(admin.ModelAdmin):

View File

@ -32,7 +32,7 @@ class ProjectCategorySerializer(serializers.HyperlinkedModelSerializer):
'name',
'description',
'parent',
'path')
'pathstring')
class ProjectRunSerializer(serializers.HyperlinkedModelSerializer):

View File

@ -5,7 +5,7 @@ from .models import StockLocation, StockItem
class LocationAdmin(admin.ModelAdmin):
list_display = ('name', 'path', 'description')
list_display = ('name', 'pathstring', 'description')
class StockItemAdmin(SimpleHistoryAdmin):

View File

@ -48,4 +48,4 @@ class LocationSerializer(serializers.HyperlinkedModelSerializer):
'name',
'description',
'parent',
'path')
'pathstring')