aboutcode-org / aboutcode-org/vulnerablecode

Use models.py Package QuerySet method in place of views.py method

未关闭
#880 1 条评论 0 个 reaction 已指派 1 人 已被 @johnmhoran 认领 在 GitHub 查看
ui
主要语言
Python
星标
702
派生
328
平均合并
3 天 8 小时
30 天内合并 PR
3

描述

From @tdruez 's comments in https://github.com/nexB/vulnerablecode/issues/875 .

Excerpts:

![image](https://user-images.githubusercontent.com/11096678/186774517-109636a6-ee24-45eb-9c8c-1ff92e57c22d.png)

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 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。