lucidgreen-external-identifiers 1.0.2
A module that provides classes and util to support External IDs for models, admins, and APIs.
License: MIT
Requires Python: >=3
Keywords: external identifiers 3rd-party

# lucidgreen-external-identifiers

## Installation
1. Install lucidgreen-external-identifiers package
    ```shell
    pipenv install lucidgreen-external-identifiers
    ```
2. Add `external_identifiers` to `INSTALLED_APPS`
3. Add external system choices you want to support in `settings.py` as following:
    ```python
    EXTERNAL_SYSTEM_CHOICES = (
        ('external_system_1', 'External System 1'),
        ('external_system_2', 'External System 2'),
        ('external_system_3', 'External System 3'),
    )
    ```

## Usage
###Models
**Associate External System attributes to a modal**
1. Create a modal that extends `AbstractExternalIdentifier`
2. Add a foreign key to the modal you want to attribute with external system info
   ```python
   class ProductExternalIdentifier(external_identifiers.models.AbstractExternalIdentifier):
       product = django.db.models.ForeignKey(
           'products.Product', related_name='external_identifiers', on_delete=django.db.models.CASCADE)
   ```
   Note: Make sure that `related_name` is set to `external_identifiers` because this module assumes this is how it is set.
3. Generate and run migrations.

###Views
**Add support to search by External ID in List Views**
* Extend `ExternalIdentifierSearchViewMixin` in the list view.

**Display External Systems table in Details template**
* Include `external_identifiers_info` HTML
  ```html
   {% include 'external_identifiers/external_identifiers_info.html' %}
   ```

###Admin
**Support searching by External ID and filtering by External System in Admin**
* Extend `ExternalIdentifierAdminMixin` by you ModelAdmin class

**Add inline admin display for the associated external identifiers**
1. Create an InlineAdmin class the extends `ExternalIdentifierInlineAdmin`
   ```python
   class ProductExternalIdentifierInlineAdmin(external_identifiers.admin.ExternalIdentifierInlineAdmin):
       model = models.ProductExternalIdentifier
   ```
2. Add the new inline admin to your admin class

###API
**API to search for object by External ID**
1. Extend or Create Details API for your object with `lookup_field` set to `external_identifiers__external_id`
   ```python
   class ProductDetailByExternalIDPublicAPIView(ProductDetailPublicAPIView):
       """
       Retrieve product details by external system ID
       """
       lookup_field = 'external_identifiers__external_id'
       # external_id here is what the kwarg is named in the defined url path entry
       lookup_url_kwarg = 'external_id'

       def get_queryset(self):
           return super().get_queryset().distinct()
   ```
2. Extend `ObjectExternalIdentifiersSerializer` by the serializer you use to serialize object details

**API to associate external system info to a model object**
1. Create a serializer that extends `ExternalIdentifierPublicInfoSerializer`
   ```python
   class ProductExternalIdentifierCreateSerializer(
            external_identifiers_serializers.ExternalIdentifierPublicInfoSerializer,
            rest_framework.serializers.ModelSerializer):
        product = native_shortuuid.fields.NativeShortUUIDSerializerField()

        def validate_product(self, value):
            try:
                return models.Product.objects.get(uuid=value)
            except models.Product.DoesNotExist:
                raise rest_framework.validators.ValidationError(f'Product {value} does not exist.')

        class Meta:
            model = models.ProductExternalIdentifier
            fields = ['product', 'external_system', 'external_id', 'external_account']
   ```
2. Create an API View that uses the serializer above as its serializer class
   ```python
   class ProductExternalIdentifierPublicAPIView(rest_framework.generics.CreateAPIView):
       serializer_class = serializers.ProductExternalIdentifierCreateSerializer
   ```