Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: model view set test case #60

Merged
merged 11 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .venv/.gitkeep
Empty file.
17 changes: 17 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Pytest",
"type": "python",
"request": "test",
"justMyCode": false,
"presentation": {
"hidden": true
}
}
]
}
30 changes: 30 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,39 @@
{
"isort.path": [
".venv/bin/python",
"-m",
"isort"
],
"isort.args": [
"--settings-file=pyproject.toml"
],
"black-formatter.path": [
".venv/bin/python",
"-m",
"black"
],
"black-formatter.args": [
"--config",
"pyproject.toml"
],
"mypy-type-checker.path": [
".venv/bin/python",
"-m",
"mypy"
],
"mypy-type-checker.args": [
"--config-file=pyproject.toml"
],
"pylint.path": [
".venv/bin/python",
"-m",
"pylint"
],
"pylint.args": [
"--rcfile=pyproject.toml"
],
"python.testing.pytestArgs": [
"-n=auto",
"-c=pyproject.toml",
"."
],
Expand Down
9 changes: 8 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,18 @@ phonenumbers = "==8.12.12" # TODO: remove
[dev-packages]
black = "==23.1.0"
pytest = "==7.2.1"
pytest-env = "==0.8.1"
pytest-xdist = {version = "==3.5.0", extras = ["psutil"]}
pytest-django = "==4.5.2"
django-extensions = "==3.2.1"
pyparsing = "==3.0.9"
pydot = "==1.4.2"
pytest-env = "==0.8.1"
pylint = "==3.0.2"
pylint-django = "==2.5.5"
isort = "==5.13.2"
mypy = "==1.6.1"
django-stubs = {version = "==4.2.6", extras = ["compatible-mypy"]}
djangorestframework-stubs = {version = "==3.14.4", extras = ["compatible-mypy"]}

[requires]
python_version = "3.8"
862 changes: 595 additions & 267 deletions Pipfile.lock

Large diffs are not rendered by default.

23 changes: 4 additions & 19 deletions codeforlife/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
"""Helpers for module "django.db.models".
https://docs.djangoproject.com/en/3.2/ref/models/
"""
© Ocado Group
Created on 19/01/2024 at 15:20:45(+00:00).
"""

import typing as t

from django.db.models import Model as _Model


class Model(_Model):
"""A base class for all Django models.

Args:
_Model (django.db.models.Model): Django's model class.
"""

id: int
pk: int


AnyModel = t.TypeVar("AnyModel", bound=Model)
from .base import *
27 changes: 27 additions & 0 deletions codeforlife/models/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
© Ocado Group
Created on 19/01/2024 at 15:18:48(+00:00).

Base model for all Django models.
"""

import typing as t

from django.db.models import Model as _Model
from django_stubs_ext.db.models import TypedModelMeta

Id = t.TypeVar("Id")


class Model(_Model, t.Generic[Id]):
"""A base class for all Django models."""

id: Id
pk: Id

# pylint: disable-next=missing-class-docstring,too-few-public-methods
class Meta(TypedModelMeta):
abstract = True


AnyModel = t.TypeVar("AnyModel", bound=Model)
48 changes: 38 additions & 10 deletions codeforlife/models/signals/pre_save.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

import typing as t

from .. import AnyModel
from django.db.models import Model

from . import UpdateFields, _has_update_fields

AnyModel = t.TypeVar("AnyModel", bound=Model)


def was_created(instance: AnyModel):
"""Check if the instance was created.
Expand Down Expand Up @@ -37,30 +40,55 @@ def has_update_fields(actual: UpdateFields, expected: UpdateFields):
return _has_update_fields(actual, expected)


def has_previous_values(
def check_previous_values(
instance: AnyModel,
predicates: t.Dict[str, t.Callable[[t.Any, t.Any], bool]],
):
"""Check if the previous values are as expected.
"""Check if the previous values are as expected. If the model has not been
created yet, the previous values are None.

Args:
instance: The current instance.
predicates: A predicate for each field. It accepts the arguments
(previous_value, value) and returns True if the values are as expected.

Raises:
ValueError: If arg 'instance' has not been created yet.

Returns:
If all the previous values are as expected.
"""

if not was_created(instance):
raise ValueError("Arg 'instance' has not been created yet.")
if was_created(instance):
previous_instance = instance.__class__.objects.get(pk=instance.pk)

def get_previous_value(field: str):
return getattr(previous_instance, field)

previous_instance = instance.__class__.objects.get(pk=instance.pk)
else:
# pylint: disable-next=unused-argument
def get_previous_value(field: str):
return None

return all(
predicate(previous_instance[field], instance[field])
predicate(get_previous_value(field), getattr(instance, field))
for field, predicate in predicates.items()
)


def previous_values_are_unequal(instance: AnyModel, fields: t.Set[str]):
"""Check if all the previous values are not equal to the current values. If
the model has not been created yet, the previous values are None.

Args:
instance: The current instance.
fields: The fields that should not be equal.

Returns:
If all the previous values are not equal to the current values.
"""

def predicate(v1, v2):
return v1 != v2

return check_previous_values(
instance,
{field: predicate for field in fields},
)
6 changes: 6 additions & 0 deletions codeforlife/serializers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
© Ocado Group
Created on 20/01/2024 at 11:19:12(+00:00).
"""

from .base import *
21 changes: 21 additions & 0 deletions codeforlife/serializers/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
© Ocado Group
Created on 20/01/2024 at 11:19:24(+00:00).

Base model serializers.
"""

import typing as t

from django.db.models import Model
from rest_framework.serializers import ModelSerializer as _ModelSerializer

AnyModel = t.TypeVar("AnyModel", bound=Model)


class ModelSerializer(_ModelSerializer[AnyModel], t.Generic[AnyModel]):
"""Base model serializer for all model serializers."""

# pylint: disable-next=useless-parent-delegation
def update(self, instance, validated_data: t.Dict[str, t.Any]):
return super().update(instance, validated_data)
11 changes: 9 additions & 2 deletions codeforlife/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
from .api import APITestCase, APIClient
from .cron import CronTestCase, CronTestClient
"""
© Ocado Group
Created on 19/01/2024 at 17:17:23(+00:00).

Custom test cases.
"""

from .cron import CronTestCase
from .model_view_set import ModelViewSetTestCase
Loading