Metadata-Version: 2.4
Name: nsd-extension-framework
Version: 0.1.0
Summary: niceDjango Extension Framework
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: django>=5.2
Requires-Dist: djangorestframework>=3.17.1
Requires-Dist: importlib>=1.0.4
Requires-Dist: wrapt>=2.0.1

# niceDjango Extension Framework

提供 Python/Django 擴展之開發框架。

## 概述

擴展 (Extension) 用來提供給呼叫端，無須知道具體實作方式，便可取得實作對象 (Target) 之機制。

每一個擴展需定義:

- ``extension_type``: 擴展的類型。使用 ``[A-z_-.][A-z0-9_-.]`` 組成之字串。
- ``extension_id``: 擴展的識別碼。使用 ``[A-z_-.][A-z0-9_-.]`` 組成之字串。空值或省略時，將以擴展的識別子 (
  類別名稱，或函數名稱) 做為識別碼。
- ``label``: 擴展的標籤，給人類看的名稱。空值或省略時，將以擴展的識別子 (類別名稱，或函數名稱) 做為標籤。
- ``description``:給人類閱讀的描述。

擴展的完整識別子 (Identifier) 為 <code><i>extension_type</i>:<i>extension_id</i></code>。

## 功能

- 簡化擴展之開發。
- 可從所有 EGG 中載入指定的擴展。
- 提供給 Django Model 使用的 Field 類別，以保存擴展資訊到資料庫。
- 提供給 Django Form 使用的 Field 類別，讓用戶可從表單選取或設定擴展。
- 提供給 Django REST Framework 使用的 Field 類別，讓 API Client 可以選取或設定擴展。

## 安裝

```shell
$ pip install nsd-extension-framework
```

## 使用

### 宣告擴展 (Declaration extension)

首先，你需要先宣告你的擴展。擴展可以是一個類別 (Class)，也可以是一個函數 (Function)。

每個擴展都需使用 ``@extension`` 裝飾器宣告其為擴展:

```python
from django.utils.translation import gettext as _

from nsd_extension_framework import extension


@extension(
    extension_type="foo",
    extension_id="boo_class",
    label=_("Boo Class Extension")
)
class BooExtension:
    def test(self):
        print("Boo Class Extension - Test")


@extension(
    extension_type="foo",
    extension_id="boo_function",
    label=_("Boo Function Extension")
)
def boo():
    print("Boo Function Extension - Test")

```

### 註冊你的擴展 (Register your extensions)

宣告擴展後，可在使用之前註冊你的擴展。註冊的方法有兩種:

- 使用代碼
- 使用 EGG EntryPoint

#### 使用代碼

例如，在 Django 中，你可以透過 ``AppConfig.ready()`` 來註冊之:

```python
from django.apps import AppConfig as _AppConfig


class AppConfig(_AppConfig):
    def ready(self):
        from myapp.extensions import BooExtension, boo  # NOQA
        from nsd_extension_framework import factory
        factory.register(BooExtension).register(boo)


```

#### 使用 EGG EntryPoint

請在你的 Python EGG 套件中，定義一個 EntryPoint。

例如 ``pyproject.toml`` 中:

```toml
[project.entry-points."AppExtensions"]
"foo:boo_class" = "myapp.extensions:BooExtension"
"foo:boo_function" = "myapp.extensions:boo"
```

或是 ``setup.py`` 中:

```python
from setuptools import setup  # NOQA

setup(
    # ...
    entry_points={
        "AppExtensions": {
            "foo:boo_class": "myapp.extensions:BooExtension",
            "foo:boo_function": "myapp.extensions:boo",
        }
    },
)

```

然後在使用之前，透過 ``nsd_extension_framework.factory.load()`` 從所有的 EGG 套件中載入所有擴展:

```python
from django.apps import AppConfig as _AppConfig


class AppConfig(_AppConfig):
    def ready(self):
        from nsd_extension_framework import factory
        factory.load(entry_point_group_name="AppExtensions")

```

### 取得擴展 (Get extension)

註冊後，可以在程序運行的任何時候，透過 ``factory.get_extension()`` 來取得擴展:

```python
from nsd_extension_framework import factory

boo_class = factory.get_extension("foo:boo_class")
extension = boo_class()
extension.test()
# Boo Class Extension - Test

boo_function = factory.get_extension("foo:boo_function")
boo_function()
# Boo Function Extension - Test

```

## 進階 (Advanced)

### Django 模型欄位 (Django Model Field)

本套件中提供 ``nsd_extension_framework.db.ExtensionField``，讓你可以將擴展資訊保存到資料庫中。

使用範例:

```python
from django.db import models

from nsd_extension_framework.db import ExtensionField
from myapp.extensions import BooExtension   # NOQA


class ExtensionModel(models.Model):
    extension = ExtensionField(
        required=False,
        db_index=True
    )


ext = ExtensionModel(extension=BooExtension)
# 回傳 BooExtension 實例
impl = ext.extension()
# 呼叫 BooExtension.test()
impl.test()

```

### Django 表單欄位 (Django Form Field)

```python
from django import forms

from nsd_extension_framework import Extension
from nsd_extension_framework.forms import ExtensionField


class BooForm(forms.Form):
    extension = ExtensionField(
        required=False,
    )


def boo_form_post(request):
    form = BooForm(request.POST)
    if form.is_valid():
        # 回傳 BooExtension 實例
        extension = form.cleaned_data["extension"]
        if extension:
            # 取得 Extension 的 FullID
            opts = Extension.get_extension_options(extension)
            if opts:
                print(opts.full_id)

```

目前也支援 Django ModelForm:

```python
from django import forms
from myapp.models import ExtensionModel  # NOQA


# 自定義的 ModelForm
class BooModelForm(forms.ModelForm):
    class Meta:
        model = ExtensionModel
        fields = '__all__'


# 使用 modelform_factory() 建立 ModelForm

ModelForm = forms.modelform_factory(ExtensionModel, fields='__all__')

```

### Django REST Framework 欄位 (Django REST Framework Field)

```python
from rest_framework import serializers

from nsd_extension_framework.drf import ExtensionField


class BooSerializer(serializers.Serializer):
    extension = ExtensionField(
        required=False,
    )

```

也支援 ModelSerializer:

```python
from rest_framework import serializers
from myapp.models import ExtensionModel  # NOQA


class ExtensionModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = ExtensionModel
        field = '__all__'

```
