@@ -317,28 +317,40 @@ class Meta:
317317 ordering = ["vulnerability" , "reference" ]
318318
319319
320+ def purl_to_dict (purl : PackageURL ):
321+ """
322+ Return a dict of purl components suitable for use in a queryset.
323+ We need to have specific empty values for using in querysets because of our peculiar model structure.
324+
325+ For example::
326+ >>> purl_to_dict(PackageURL.from_string("pkg:generic/postgres"))
327+ {'type': 'generic', 'namespace': '', 'name': 'postgres', 'version': '', 'qualifiers': {}, 'subpath': ''}
328+ >>> purl_to_dict(PackageURL.from_string("pkg:generic/postgres/[email protected] ?foo=bar#baz")) 329+ {'type': 'generic', 'namespace': 'postgres', 'name': 'postgres', 'version': '1.2', 'qualifiers': {'foo': 'bar'}, 'subpath': 'baz'}
330+ """
331+ if isinstance (purl , str ):
332+ purl = PackageURL .from_string (purl )
333+
334+ return dict (
335+ type = purl .type ,
336+ namespace = purl .namespace or "" ,
337+ name = purl .name ,
338+ version = purl .version or "" ,
339+ qualifiers = purl .qualifiers or {},
340+ subpath = purl .subpath or "" ,
341+ )
342+
343+
320344class PackageQuerySet (BaseQuerySet , PackageURLQuerySet ):
321345 def get_or_create_from_purl (self , purl : PackageURL ):
322346 """
323347 Return an existing or new Package (created if neeed) given a
324348 ``purl`` PackageURL.
325349 """
326- purl_fields = without_empty_values (purl .to_dict (encode = True ))
327-
328- # when there are 2 packages one with qualifiers and one without
329- # qualifiers, having all other fields same, this raises MultipleObjectsReturned
330- # so we are filling out the fields with empty value to avoid this
331- # for field in PackageURL._fields:
332- # # name, type, and version are required fields
333- # if field not in purl_fields:
334- # if field == "namespace":
335- # purl_fields[field] = ""
336- # if field == "qualifiers":
337- # purl_fields[field] = {}
338- # if field == "subpath":
339- # purl_fields[field] = ""
340-
341- package , _ = Package .objects .get_or_create (** purl_fields )
350+ if isinstance (purl , str ):
351+ purl = PackageURL .from_string (purl )
352+
353+ package , _ = Package .objects .get_or_create (** purl_to_dict (purl = purl ))
342354 return package
343355
344356 def for_package_url_object (self , purl ):
@@ -347,15 +359,12 @@ def for_package_url_object(self, purl):
347359 ``purl`` string is validated and transformed into filtering lookups. If
348360 this is a PackageURL object it is reused as-is.
349361 """
350- if isinstance (purl , PackageURL ):
351- lookups = without_empty_values (purl .to_dict (encode = True ))
352- return self .filter (** lookups )
353-
354- elif isinstance (purl , str ):
355- return self .for_package_url (purl , encode = False )
356-
357- else :
362+ if not purl :
358363 return self .none ()
364+ if isinstance (purl , str ):
365+ purl = PackageURL .from_string (purl )
366+ lookups = without_empty_values (purl .to_dict (encode = True ))
367+ return self .filter (** lookups )
359368
360369 def affected (self ):
361370 """
0 commit comments