mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Exporting data from a Part table now uses the API too
- Makes use of the existing table filters - Exported data matches exactly what you see in the table!
This commit is contained in:
parent
73a32f66c8
commit
4f74a27e1a
@ -26,6 +26,8 @@ from djmoney.contrib.exchange.exceptions import MissingRate
|
||||
|
||||
from decimal import Decimal, InvalidOperation
|
||||
|
||||
from part.admin import PartResource
|
||||
|
||||
from .models import Part, PartCategory, PartRelated
|
||||
from .models import BomItem, BomItemSubstitute
|
||||
from .models import PartParameter, PartParameterTemplate
|
||||
@ -43,6 +45,7 @@ from build.models import Build
|
||||
from . import serializers as part_serializers
|
||||
|
||||
from InvenTree.helpers import str2bool, isNull, increment
|
||||
from InvenTree.helpers import DownloadFile
|
||||
from InvenTree.api import AttachmentMixin
|
||||
|
||||
from InvenTree.status_codes import BuildStatus
|
||||
@ -726,6 +729,22 @@ class PartList(generics.ListCreateAPIView):
|
||||
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
|
||||
# Check if we wish to export the queried data to a file.
|
||||
# If so, skip pagination!
|
||||
export_format = request.query_params.get('export', None)
|
||||
|
||||
if export_format:
|
||||
export_format = str(export_format).strip().lower()
|
||||
|
||||
if export_format in ['csv', 'tsv', 'xls', 'xlsx']:
|
||||
dataset = PartResource().export(queryset=queryset)
|
||||
|
||||
filedata = dataset.export(export_format)
|
||||
|
||||
filename = f"InvenTree_Parts.{export_format}"
|
||||
|
||||
return DownloadFile(filedata, filename)
|
||||
|
||||
page = self.paginate_queryset(queryset)
|
||||
|
||||
if page is not None:
|
||||
|
@ -153,9 +153,6 @@
|
||||
<h4>{% trans "Parts" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
<button type='button' class='btn btn-outline-secondary' id='part-export' title='{% trans "Export Part Data" %}'>
|
||||
<span class='fas fa-file-download'></span> {% trans "Export" %}
|
||||
</button>
|
||||
{% if roles.part.add %}
|
||||
<button type='button' class='btn btn-success' id='part-create' title='{% trans "Create new part" %}'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Part" %}
|
||||
@ -167,6 +164,9 @@
|
||||
<div id='part-button-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
<div class='btn-group' role='group'>
|
||||
<button id='part-export' class='btn btn-outline-secondary' type='button' title='{% trans "Export part data" %}'>
|
||||
<span class='fas fa-file-download'></span>
|
||||
</button>
|
||||
<button id='part-options' class='btn btn-primary dropdown-toggle' type='button' data-bs-toggle="dropdown">
|
||||
{% trans "Options" %}
|
||||
</button>
|
||||
@ -291,10 +291,7 @@
|
||||
});
|
||||
|
||||
$("#part-export").click(function() {
|
||||
|
||||
var url = "{% url 'part-export' %}?category={{ category.id }}";
|
||||
|
||||
location.href = url;
|
||||
downloadTableData($('#part-table'));
|
||||
});
|
||||
|
||||
{% if roles.part.add %}
|
||||
|
@ -58,14 +58,6 @@ class PartListTest(PartViewTestCase):
|
||||
self.assertIn('parts', keys)
|
||||
self.assertIn('user', keys)
|
||||
|
||||
def test_export(self):
|
||||
""" Export part data to CSV """
|
||||
|
||||
response = self.client.get(reverse('part-export'), {'parts': '1,2,3,4,5,6,7,8,9,10'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('streaming_content', dir(response))
|
||||
|
||||
|
||||
class PartDetailTest(PartViewTestCase):
|
||||
|
||||
|
@ -80,9 +80,6 @@ part_urls = [
|
||||
# Download a BOM upload template
|
||||
url(r'^bom_template/?', views.BomUploadTemplate.as_view(), name='bom-upload-template'),
|
||||
|
||||
# Export data for multiple parts
|
||||
url(r'^export/', views.PartExport.as_view(), name='part-export'),
|
||||
|
||||
# Individual part using pk
|
||||
url(r'^(?P<pk>\d+)/', include(part_detail_urls)),
|
||||
|
||||
|
@ -709,69 +709,6 @@ class BomUpload(InvenTreeRoleMixin, DetailView):
|
||||
template_name = 'part/upload_bom.html'
|
||||
|
||||
|
||||
class PartExport(AjaxView):
|
||||
""" Export a CSV file containing information on multiple parts """
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
def get_parts(self, request):
|
||||
""" Extract part list from the POST parameters.
|
||||
Parts can be supplied as:
|
||||
|
||||
- Part category
|
||||
- List of part PK values
|
||||
"""
|
||||
|
||||
# Filter by part category
|
||||
cat_id = request.GET.get('category', None)
|
||||
|
||||
part_list = None
|
||||
|
||||
if cat_id is not None:
|
||||
try:
|
||||
category = PartCategory.objects.get(pk=cat_id)
|
||||
part_list = category.get_parts()
|
||||
except (ValueError, PartCategory.DoesNotExist):
|
||||
pass
|
||||
|
||||
# Backup - All parts
|
||||
if part_list is None:
|
||||
part_list = Part.objects.all()
|
||||
|
||||
# Also optionally filter by explicit list of part IDs
|
||||
part_ids = request.GET.get('parts', '')
|
||||
parts = []
|
||||
|
||||
for pk in part_ids.split(','):
|
||||
try:
|
||||
parts.append(int(pk))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if len(parts) > 0:
|
||||
part_list = part_list.filter(pk__in=parts)
|
||||
|
||||
# Prefetch related fields to reduce DB hits
|
||||
part_list = part_list.prefetch_related(
|
||||
'category',
|
||||
'used_in',
|
||||
'builds',
|
||||
'supplier_parts__purchase_order_line_items',
|
||||
'stock_items__allocations',
|
||||
)
|
||||
|
||||
return part_list
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
parts = self.get_parts(request)
|
||||
|
||||
dataset = PartResource().export(queryset=parts)
|
||||
|
||||
csv = dataset.export('csv')
|
||||
return DownloadFile(csv, 'InvenTree_Parts.csv')
|
||||
|
||||
|
||||
class BomUploadTemplate(AjaxView):
|
||||
"""
|
||||
Provide a BOM upload template file for download.
|
||||
|
Loading…
Reference in New Issue
Block a user