Metadata-Version: 2.4
Name: django-gallery-core
Version: 0.1.0
Summary: Reusable ordered gallery primitives for Django projects.
Author: Recep Enes Kaya
License: MIT License
        
        Copyright (c) 2026 Recep Enes Kaya
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
        
Project-URL: Homepage, https://github.com/rkaya21/django-gallery-core
Project-URL: Repository, https://github.com/rkaya21/django-gallery-core
Project-URL: Issues, https://github.com/rkaya21/django-gallery-core/issues
Keywords: django,gallery,images,portfolio,cms
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=4.2
Requires-Dist: djangorestframework>=3.14
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == "test"
Requires-Dist: pytest-django>=4.8; extra == "test"
Requires-Dist: pytest-cov>=5.0; extra == "test"
Requires-Dist: Pillow>=10.0; extra == "test"
Provides-Extra: lint
Requires-Dist: ruff>=0.4; extra == "lint"
Provides-Extra: dev
Requires-Dist: django-gallery-core[lint,test]; extra == "dev"
Dynamic: license-file

# django-gallery-core

Reusable Django primitives for ordered image galleries.

`django-gallery-core` is a small package for teams that want gallery behavior without pulling in a full CMS. It focuses on a narrow, stable API:

- an abstract ordered image model
- a migration-safe upload path helper
- a reusable DRF serializer base
- a reusable Django admin inline
- a service for syncing gallery uploads from multipart requests

## Why this package exists

Many Django projects need the same gallery pattern:
- a parent model such as `Project`, `CaseStudy`, or `BlogPost`
- a related ordered list of images
- admin editing support
- simple serializer output for public APIs
- safe upload handling for create and update flows

This package extracts that repeated plumbing into a reusable core while keeping app-specific decisions in your own code.

## Features

- `OrderedGalleryImage`: abstract base model for ordered image rows
- `GalleryUploadTo`: deconstructible upload helper compatible with Django migrations
- `build_gallery_upload_to(...)`: convenience factory for upload paths
- `OrderedGalleryImageSerializer`: serializer base for public API output
- `OrderedGalleryInline`: reusable Django admin inline
- `sync_gallery_uploads(...)`: service for adding and removing images during multipart form handling

## Installation

```bash
pip install django-gallery-core
```

For local development:

```bash
pip install -e ".[test]"
```

Add the app to `INSTALLED_APPS`:

```python
INSTALLED_APPS = [
    # ...
    'gallery',
]
```

## Public API

The intended public surface area is:

- `gallery.models.OrderedGalleryImage`
- `gallery.models.GalleryUploadTo`
- `gallery.models.build_gallery_upload_to`
- `gallery.serializers.OrderedGalleryImageSerializer`
- `gallery.admin.OrderedGalleryInline`
- `gallery.services.sync_gallery_uploads`
- `gallery.services.reorder_gallery`

Anything outside this surface should be treated as internal and may change more freely.

## Quick Start

### 1. Define a parent model and gallery image model

```python
from django.db import models
from gallery.models import OrderedGalleryImage, build_gallery_upload_to


class Project(models.Model):
    title = models.CharField(max_length=120)


class ProjectImage(OrderedGalleryImage):
    project = models.ForeignKey(
        Project,
        on_delete=models.CASCADE,
        related_name='gallery_images',
    )
    image = models.ImageField(upload_to=build_gallery_upload_to('projects/gallery'))
```

### 2. Expose gallery data in an API serializer

```python
from rest_framework import serializers
from gallery.serializers import OrderedGalleryImageSerializer
from gallery.services import sync_gallery_uploads
from .models import Project, ProjectImage


class ProjectImageSerializer(OrderedGalleryImageSerializer):
    class Meta:
        model = ProjectImage
        fields = ['id', 'image', 'order']


class ProjectWriteSerializer(serializers.ModelSerializer):
    gallery = ProjectImageSerializer(source='gallery_images', many=True, read_only=True)

    class Meta:
        model = Project
        fields = ['id', 'title', 'gallery']

    def update(self, instance, validated_data):
        request = self.context['request']
        # IDs to delete come from the client (e.g. a hidden field listing removed images)
        remove_ids = [int(x) for x in request.data.getlist('remove_gallery_ids') if x]
        instance = super().update(instance, validated_data)
        sync_gallery_uploads(
            instance=instance,
            related_name='gallery_images',
            image_model=ProjectImage,
            gallery_files=request.FILES.getlist('gallery_images'),
            remove_ids=remove_ids,
        )
        return instance
```

### 3. Reuse the admin inline

```python
from django.contrib import admin
from gallery.admin import OrderedGalleryInline
from .models import Project, ProjectImage


class ProjectImageInline(OrderedGalleryInline):
    model = ProjectImage


@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
    inlines = [ProjectImageInline]
```

## Example

A minimal integration example lives in [example.py](./example.py).

## Design Goals

- Stay close to native Django and DRF patterns
- Keep the package small and readable
- Avoid forcing a full gallery domain model onto consuming apps
- Preserve migration safety for upload path helpers
- Support practical admin and API use cases first

## Non-Goals

- full media management
- asset optimization pipelines
- frontend gallery UI
- polymorphic attachment systems
- complete CMS abstractions

## Development

```bash
# Install all dev dependencies (tests + linting)
pip install -e ".[dev]"

# Run tests with coverage
pytest --cov=gallery --cov-report=term-missing

# Lint
ruff check .
```

## Versioning

This package is in early-stage development and currently targets a `0.x` release line. Expect additive improvements while the public API settles.

## License

MIT. See [LICENSE](./LICENSE).
