33import json
44import logging
55import os
6- from typing import Type
76
87from . import api as inventree_api
98
10- INVENTREE_PYTHON_VERSION = "0.14 .0"
9+ INVENTREE_PYTHON_VERSION = "0.16 .0"
1110
1211
1312logger = logging .getLogger ('inventree' )
@@ -19,6 +18,11 @@ class InventreeObject(object):
1918 # API URL (required) for the particular model type
2019 URL = ""
2120
21+ @classmethod
22+ def get_url (cls , api ):
23+ """Helper method to get the URL associated with this model."""
24+ return cls .URL
25+
2226 # Minimum server version for the particular model type
2327 MIN_API_VERSION = None
2428 MAX_API_VERSION = None
@@ -85,7 +89,9 @@ def __init__(self, api, pk=None, data=None):
8589 if pk <= 0 :
8690 raise ValueError (f"Supplier <pk> value ({ pk } ) for { self .__class__ } must be positive." )
8791
88- self ._url = f"{ self .URL } /{ pk } /"
92+ url = self .get_url (api )
93+
94+ self ._url = f"{ url } /{ pk } /"
8995 self ._api = api
9096
9197 if data is None :
@@ -374,9 +380,6 @@ def bulkDelete(cls, api: inventree_api.InvenTreeAPI, items=None, filters=None):
374380
375381 """
376382
377- if api .api_version < 58 :
378- raise NotImplementedError ("bulkDelete requires API version 58 or newer" )
379-
380383 if not items and not filters :
381384 raise ValueError ("Must supply either 'items' or 'filters' argument" )
382385
@@ -395,14 +398,12 @@ def bulkDelete(cls, api: inventree_api.InvenTreeAPI, items=None, filters=None):
395398
396399
397400class Attachment (BulkDeleteMixin , InventreeObject ):
398- """
399- Class representing a file attachment object
401+ """Class representing a file attachment object."""
400402
401- Multiple sub-classes exist, representing various types of attachment models in the database.
402- """
403+ URL = 'attachment/'
403404
404- # Name of the primary key field of the InventreeObject the attachment will be attached to
405- ATTACH_TO = None
405+ # Ref: https://github.com/inventree/InvenTree/pull/7420
406+ MIN_API_VERSION = 207
406407
407408 @classmethod
408409 def add_link (cls , api , link , comment = "" , ** kwargs ):
@@ -420,9 +421,6 @@ def add_link(cls, api, link, comment="", **kwargs):
420421 data ["comment" ] = comment
421422 data ["link" ] = link
422423
423- if cls .ATTACH_TO not in kwargs :
424- raise ValueError (f"Required argument '{ cls .ATTACH_TO } ' not supplied to add_link method" )
425-
426424 if response := api .post (cls .URL , data ):
427425 logger .info (f"Link attachment added to { cls .URL } " )
428426 else :
@@ -446,18 +444,14 @@ def upload(cls, api, attachment, comment='', **kwargs):
446444 data = kwargs
447445 data ['comment' ] = comment
448446
449- if cls .ATTACH_TO not in kwargs :
450- raise ValueError (f"Required argument '{ cls .ATTACH_TO } ' not supplied to upload method" )
451-
452447 if type (attachment ) is str :
453448 if not os .path .exists (attachment ):
454449 raise FileNotFoundError (f"Attachment file '{ attachment } ' does not exist" )
455450
456451 # Load the file as an in-memory file object
457452 with open (attachment , 'rb' ) as fo :
458-
459453 response = api .post (
460- cls .URL ,
454+ Attachment .URL ,
461455 data ,
462456 files = {
463457 'attachment' : (os .path .basename (attachment ), fo ),
@@ -467,8 +461,9 @@ def upload(cls, api, attachment, comment='', **kwargs):
467461 else :
468462 # Assumes a StringIO or BytesIO like object
469463 name = getattr (attachment , 'name' , 'filename' )
464+
470465 response = api .post (
471- cls .URL ,
466+ Attachment .URL ,
472467 data ,
473468 files = {
474469 'attachment' : (name , attachment ),
@@ -490,47 +485,44 @@ def download(self, destination, **kwargs):
490485 return self ._api .downloadFile (self .attachment , destination , ** kwargs )
491486
492487
493- def AttachmentMixin (AttachmentSubClass : Type [Attachment ]):
494- class Mixin (Attachment ):
495- def getAttachments (self ):
496- return AttachmentSubClass .list (
497- self ._api ,
498- ** {AttachmentSubClass .ATTACH_TO : self .pk },
499- )
488+ class AttachmentMixin :
489+ """Mixin class which allows a model class to interact with attachments."""
500490
501- def uploadAttachment (self , attachment , comment = "" ):
502- """
503- Upload an attachment (file) against this Object.
491+ def getAttachments (self ):
492+ """Return a list of attachments associated with this object."""
504493
505- Args:
506- attachment: Either a string (filename) or a file object
507- comment: Attachment comment
508- """
494+ return Attachment .list (
495+ self ._api ,
496+ model_type = self .getModelType (),
497+ model_id = self .pk
498+ )
509499
510- return AttachmentSubClass .upload (
511- self ._api ,
512- attachment ,
513- comment = comment ,
514- ** {AttachmentSubClass .ATTACH_TO : self .pk },
515- )
500+ def uploadAttachment (self , attachment , comment = "" ):
501+ """Upload a file attachment against this model instance."""
502+
503+ return Attachment .upload (
504+ self ._api ,
505+ attachment ,
506+ comment = comment ,
507+ model_type = self .getModelType (),
508+ model_id = self .pk
509+ )
516510
517- def addLinkAttachment (self , link , comment = "" ):
518- """
519- Add an external link attachment against this Object.
520-
521- Args:
522- link: The link to attach
523- comment: Attachment comment
524- """
525-
526- return AttachmentSubClass .add_link (
527- self ._api ,
528- link ,
529- comment = comment ,
530- ** {AttachmentSubClass .ATTACH_TO : self .pk },
531- )
511+ def addLinkAttachment (self , link , comment = "" ):
512+ """Add an external link attachment against this Object.
532513
533- return Mixin
514+ Args:
515+ link: The link to attach
516+ comment: Attachment comment
517+ """
518+
519+ return Attachment .add_link (
520+ self ._api ,
521+ link ,
522+ comment = comment ,
523+ model_type = self .getModelType (),
524+ model_id = self .pk
525+ )
534526
535527
536528class MetadataMixin :
@@ -553,10 +545,6 @@ def getMetadata(self):
553545 """Read model instance metadata"""
554546 if self ._api :
555547
556- if self ._api .api_version < 49 :
557- logger .error ("API version 49 or newer required to access instance metadata" )
558- return {}
559-
560548 response = self ._api .get (self .metadata_url )
561549
562550 return response ['metadata' ]
@@ -576,10 +564,6 @@ def setMetadata(self, data, overwrite=False):
576564
577565 if self ._api :
578566
579- if self ._api .api_version < 49 :
580- logger .error ("API version 49 or newer required to access instance metadata" )
581- return None
582-
583567 if overwrite :
584568 return self ._api .put (
585569 self .metadata_url ,
0 commit comments