aboutcode-org / aboutcode-org/vulnerablecode
Use models.py Package QuerySet method in place of views.py method
- 主要语言
- Python
- 星标
- 702
- 派生
- 328
- 平均合并
- 3 天 8 小时
- 30 天内合并 PR
- 3
描述
From @tdruez 's comments in https://github.com/nexB/vulnerablecode/issues/875 .
Excerpts:

This should be moved to the models.py as a method on the Package QuerySet.
[me]
Re the rationale -- I modelled `def _related_packages(self)` on `def _package_vulnerabilities(self)`, which is just above it inside `class PackageUpdate(UpdateView)`. Why does one of these belong in `models.py` as a method on the `Package` QuerySet while the other belongs in the view?
And another newb question: does adding it as a QuerySet method just mean adding it as a method to the model, e.g, `def related_packages(self, package_url)` -- no preceding `_` -- and figuring out how to pass this to the view/template?
[@tdruez]
> What's the reason to do this, and how would I implement?
To keep thing properly organized, re-usable, and easier to test.
In a Django app, code related to the database transaction belongs in the models.
> Why does one of these belong in models.py as a method on the Package QuerySet while the other belongs in the view?
I don't see any QuerySet logic in `_package_vulnerabilities`.
> And another newb question: does adding it as a QuerySet method just mean adding it as a method to the model, e.g, def related_packages(self, package_url) -- no preceding _ -- and figuring out how to pass this to the view/template?
Not exactly, it means adding this on the QuerySet class associated with a model.
Some examples:
https://github.com/nexB/scancode.io/blob/main/scanpipe/models.py#L1215
https://github.com/nexB/scancode.io/blob/main/scanpipe/models.py#L960
The first step is to add a QuerySet class to your Package model:
```
class PackageQuerySet(models.QuerySet):
```
You can then start to break your various filters and annotation into reusable methods on the PackageQuerySet class:
```
return list(
models.Package.objects.all()
.filter(
Q(
type=purl.type,
namespace=purl.namespace,
name=purl.name,
subpath=purl.subpath,
qualifiers=purl.qualifiers,
)
)
.order_by("version")
.annotate(
vulnerability_count=Count(
"vulnerabilities",
filter=Q(packagerelatedvulnerability__fix=False),
),
# TODO: consider renaming to fixed in the future
patched_vulnerability_count=Count(
"vulnerabilities",
filter=Q(packagerelatedvulnerability__fix=True),
),
)
.prefetch_related()
)
```
Could become:
```
class PackageQuerySet(models.QuerySet):
def for_package_url(self, purl):
return self.filter(
type=purl.type,
namespace=purl.namespace,
name=purl.name,
subpath=purl.subpath,
qualifiers=purl.qualifiers,
)
def with_vulnerability_counts(self):
return self.annotate(
vulnerability_count=Count(
"vulnerabilities",
filter=Q(packagerelatedvulnerability__fix=False),
),
patched_vulnerability_count=Count(
"vulnerabilities",
filter=Q(packagerelatedvulnerability__fix=True),
),
)
```
Then on the view side, you have something much more readable such as:
```
return list(
models.Package.objects
.for_package_url(purl)
.order_by("version")
.with_vulnerability_counts()
.prefetch_related()
)
```
Add this on your Package model before the "Meta" class and after all fields, such as https://github.com/nexB/scancode.io/blob/main/scanpipe/models.py#L1469
```
objects = PackageQuerySet.as_manager()
````
You now have the database logic organized in the models, you can re-use the count annotations without copy pasting the code into other views, and you view logic is more focused and readable.
Note: The code above is not tested, simply here to illustrate, it'll have to be adapted and refined.
贡献指南
这个仓库没有索引到贡献指南
评估
这个 Issue 还没有评估数据。