Skip to content

LeOndaz/djkit

Repository files navigation

DjKit

Installation

  • pip install djkit

Examples

Obfuscation

from djkit.utils import Obfuscator

Obfuscator.email("[email protected]")  # abcdq****@test.com'
Obfuscator.obfuscate('my_super_confidential_secret')  # 'my_super_confidential_s*****'

Django rest framework related examples

# models.py
from django.db import models


class Human(models.Model):
    class Level(models.IntegerChoices):
        BEGINNER = 0
        INTERMEDIATE = 1
        ADVANCED = 2

    class MilitaryStatus(models.TextChoices):
        EXEMPTED = "exempted", "Exempted"
        SERVED = "served", "Served"
        POSTPONED = "postponed", "Postponed"

    name = models.CharField(max_length=128)
    level = models.IntegerField(choices=Level.choices)
    military_status = models.CharField(
        choices=MilitaryStatus.choices,
        max_length=128
    )
# serializers.py
from rest_framework import serializers
from djkit.rest_framework.serializers import EnumSerializer
from example.core import models


class HumanSerializer(serializers.ModelSerializer):
    level = EnumSerializer(enum=models.Human.Level)
    military_status = EnumSerializer(enum=models.Human.MilitaryStatus)

    class Meta:
        model = models.Human
        fields = [
            'id',
            'level',
            'military_status',
        ]

Example serialization

# wherever.py
from example.core.serializers import HumanSerializer
from example.core.models import Human

human = Human.objects.find(id=1)
serializer = HumanSerializer(human)  # level = BEGINNER, military_status = EXEMPTED

Example deserialization

# wherever.py
from example.core.serializers import HumanSerializer

data = {
    "level": "BEGINNER",
    "MILITARY_STATUS": "EXEMPTED",
}

instance = HumanSerializer(data=data)
instance.is_valid()
instance.save()

Got some endpoints that accept files in table formats? got you

  1. Assuming Pandas
import pandas as pd
from djkit.rest_framework.pandas import TableUploadField  # or PandasTableUploadField
from rest_framework import serializers


class MySerializer(serializers.ModelSerializer):
    file = TableUploadField()

    def create(self, validated_data):
        file: pd.DataFrame = validated_data['file']
        # do logic, don't do row based validation here
        return validated_data

    # optional method
    def validate_file_row(self, row, index, table_df):
        if row.iloc[0] == "X":
            row.iloc[0] = "Y"

        # return new row, or None and changes won't be reflected
        return row
  1. Assuming Pola.rs
import polars as pl
from djkit.rest_framework.polars import TableUploadField  # or PolarsTableUploadField
from rest_framework import serializers


class MySerializer(serializers.ModelSerializer):
    file = TableUploadField()

    def create(self, validated_data):
        file: pl.DataFrame = validated_data['file']
        # do logic, don't do row based validation here
        return validated_data

    # optional method
    def validate_file_row(self, row, index, table_df):
        # do polars logic, return new row or None
        return row
  • TableUploadField is write_only
  • TableUploadField is easily extended, use your own library if you want
from djkit.rest_framework.serializers import TableUploadField as BaseTableUploadField


def read_csv(source):
    return ...


def read_excel(source):
    return ...


class TableUploadField(BaseTableUploadField):
    handlers = {
        "csv": read_csv,
        "xlsx": read_excel,
        "xls": read_excel,
        "xlsm": read_excel,
        "xlsb": read_excel,
        "odf": read_excel,
        "ods": read_excel,
        "odt": read_excel,
    }

    def update_row(self, table_object: "YourLibraryDataFrame", index, new_row):
        """how your library updates the row"""

Supported formats are the keys in handlers

can_parse_csv = "csv" in field.allowed_upload_formats

Or

can_parse_csv = field.is_allowed_format('obj') # False

Custom kwargs for handlers in TableFieldUpload

from djkit.rest_framework.pandas import PandasTableUploadField
import pandas as pd


class Serializer(...):
    pandas_file = PandasTableUploadField(handler_kwargs={
        # by format
        "xlsx": {
            # args for read_excel would be here
            "engine": "openpyxl"
        },
        # or by reference
        pd.read_csv: {
            "delimiter": "\t"
        }
    })
  • The same logic applies to all TableUploadField subclasses.

  • djkit provides easily methods for overriding most of the logic.

  • If you want a field that does aggregation or something, override process_table on TableUploadField

Obfuscation

from djkit.rest_framework.serializers import ObfuscatedCharField, ObfuscatedEmailField, ObfuscatedFieldMixin


class MySerializer(...):
    email = ObfuscatedEmailField()  # in the API, it's obfuscated
    name = ObfuscatedCharField()  # same applies here


class MyCustomObfuscatedApiKeyField(ObfuscatedFieldMixin, MyApiKeyField):
    pass

Better error handling

REST_FRAMEWORK = {
    "DEFAULT_RENDERER_CLASSES": [
        "djkit.rest_framework.renderers.JSONRenderer"
    ]
}

This will change you response schema in case of errors to

response = {
    "field_errors": [],
    "non_field_errors": []
}

Contributions

To run the project locally

  • pip install poetry
  • poetry install
  • poetry shell
  • pre-commit install

To build the docs

  • make html

To run the tests

  • pytest .