Skip to content

Commit

Permalink
fix: model view set test case (#60)
Browse files Browse the repository at this point in the history
* add .venv

* fix: add code checking and type hints

* data access layer

* quick save

* tidy up

* add partial_update and destroy

* base model serializer

* previous_values_are_unequal

* feedback

* feedback

* minor fixes
  • Loading branch information
SKairinos authored Jan 23, 2024
1 parent 6cd1c6a commit 9fd9129
Show file tree
Hide file tree
Showing 23 changed files with 1,593 additions and 769 deletions.
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

0 comments on commit 9fd9129

Please sign in to comment.