From c7275625a4a55eee2dfa04c578f84e5e5501fa6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20H=E1=BB=93ng=20Qu=C3=A2n?= Date: Fri, 8 Nov 2019 11:32:52 +0700 Subject: [PATCH] Introduce VietnamVehiclePlateField for Django --- README.rst | 25 +++++++++++++++++++- biensoxe/__init__.py | 2 +- biensoxe/django/__init__.py | 1 + biensoxe/django/fields.py | 47 +++++++++++++++++++++++++++++++++++++ pyproject.toml | 3 ++- 5 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 biensoxe/django/__init__.py create mode 100644 biensoxe/django/fields.py diff --git a/README.rst b/README.rst index 3a08601..51826f8 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,9 @@ BienSoXe ======== -Library to parse and validate Vietnamese vehicle plate +Library to validate and parse Vietnamese vehicle plate. + +This library is not a computer-vision-based license plate recognition software. It instead is used for validating output of such computer vision software. Imagine that you use camera to track all cars coming in and out of your parking lot, but you don't want to save false data generated from recognition process (due to wrong angle of canera, for example). Install ------- @@ -38,3 +40,24 @@ To format the plate number as in daily life, pass ``VietnamVehiclePlate`` to ``s >>> str(plate) '72-E1 011.30' + +Django +~~~~~~ + +This library provides a field type, ``VietnamVehiclePlateField``, for Django model. The field will return value as ``VietnamVehiclePlate`` object. Here is example: + +.. code-block:: python + + from biensoxe.django import VietnamVehiclePlateField + + class Vehicle(models.Model): + plate_number = VietnamVehiclePlateField(max_length=20, default='10A 00001', unique=True) + + def __str__(self): + return str(self.plate_number) or self.pk + +Note that this field stores value internally as PostgeSQL ``CIText`` data type, so you can only use this field with PostgreSQL. +You also need to activate CITextExtension_ your self. + + +.. _CITextExtension: https://docs.djangoproject.com/en/2.2/ref/contrib/postgres/operations/#citextextension diff --git a/biensoxe/__init__.py b/biensoxe/__init__.py index 9ecd6df..20b9ae7 100644 --- a/biensoxe/__init__.py +++ b/biensoxe/__init__.py @@ -1,3 +1,3 @@ -__version__ = '0.8.2' +__version__ = '0.8.3' from .core import VietnamVehiclePlate, VehicleType # NOQA diff --git a/biensoxe/django/__init__.py b/biensoxe/django/__init__.py new file mode 100644 index 0000000..47565d0 --- /dev/null +++ b/biensoxe/django/__init__.py @@ -0,0 +1 @@ +from .fields import VietnamVehiclePlateField # NOQA diff --git a/biensoxe/django/fields.py b/biensoxe/django/fields.py new file mode 100644 index 0000000..9f52c3d --- /dev/null +++ b/biensoxe/django/fields.py @@ -0,0 +1,47 @@ +"""Django model field to return VietnamVehiclePlate object.""" + +from typing import Union, Optional + +from django.db.models import Expression +from django.db.backends.postgresql.base import DatabaseWrapper +from django.core.exceptions import ValidationError +from django.contrib.postgres.fields import CICharField +from django.utils.translation import gettext_lazy as _ +from biensoxe import VietnamVehiclePlate + + +def parse_vehicleplate(number_string: str) -> VietnamVehiclePlate: + """Validate and parse input string to VietnamVehiclePlate object.""" + try: + return VietnamVehiclePlate.from_string(number_string) + except ValueError: + raise ValidationError(_('Input string does not look like Vietname plate number')) + + +class VietnamVehiclePlateField(CICharField): + """Field to store Vietnamese vehicle plate. Stored in PostgreSQL as CIText data type, to enable case-insensitive search. + + Return data as VietnamVehiclePlate type from biensoxe library. + """ + + description = _('Field to store Vietnamese vehicle plate') + + def from_db_value(self, value: Optional[str], + expression: Expression, connection: DatabaseWrapper): + # Called in all circumstances when the data is loaded from the database, + # including in aggregates and values() calls. + if value is None: + return value + return parse_vehicleplate(value) + + def to_python(self, value: Union[str, VietnamVehiclePlate, None]): + # Called by deserialization and during the clean() method used from forms. + if isinstance(value, VietnamVehiclePlate): + return value + if value is None: + return value + return parse_vehicleplate(value) + + def get_prep_value(self, value: VietnamVehiclePlate): + # Convert Python object back to query value + return value.compact diff --git a/pyproject.toml b/pyproject.toml index e99de22..3e2cbdc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "biensoxe" -version = "0.8.2" +version = "0.8.3" description = "Library to parse and validate Vietnamese vehicle plate" authors = ["Nguyễn Hồng Quân "] license = "MIT" @@ -19,6 +19,7 @@ classifiers = [ 'Topic :: Software Development :: Libraries :: Python Modules', 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', + 'Framework :: Django :: 2.2', ]