Metadata-Version: 2.4
Name: cuz-toolkit
Version: 0.1.0
Summary: A collection of Django utilities: Admin mixins, testing helpers, and common patterns
Project-URL: Homepage, https://github.com/froggen/django-toolkit
Project-URL: Repository, https://github.com/froggen/django-toolkit
Author-email: Ivan <iven12345678900@gmail.com>
License: MIT
Keywords: admin,django,mixin,permission,testing,toolkit
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.0
Classifier: Framework :: Django :: 4.1
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: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.13
Requires-Dist: django>=4.0
Provides-Extra: unfold
Requires-Dist: django-unfold>=0.85.0; extra == 'unfold'
Description-Content-Type: text/markdown

# Django Toolkit

A collection of Django utilities: Admin mixins, testing helpers, and common patterns.

## Installation

```bash
# From GitHub
uv add git+https://github.com/froggen/django-toolkit.git

# Local development
uv add --editable ../django-toolkit
```

## Modules

### toolkit.admin - Admin Mixins

```python
from django.contrib import admin
from toolkit.admin import OwnerMixin, ReadOnlyMixin
from unfold.admin import ModelAdmin

@admin.register(Project)
class ProjectAdmin(OwnerMixin, ModelAdmin):
    owner_field = "user"  # Users can only edit their own projects
```

### toolkit.testing - Testing Utilities

```python
from toolkit.testing import has_index_on_columns, get_table_constraints

# Verify database indexes exist
def test_status_index_exists():
    assert has_index_on_columns("invoice", ["status", "created_at"])
```

## Available Admin Mixins

### Permission Control

| Mixin | Description |
|-------|-------------|
| `SuperuserOnlyMixin` | Only superusers can access |
| `ReadOnlyMixin` | No add/change/delete, view only |
| `CRUDMixin` | Basic CRUD for active users |
| `OwnerMixin` | Users can only access their own data |
| `ReadOnlyOwnerMixin` | View-only for owned data |

### Special Patterns

| Mixin | Description |
|-------|-------------|
| `ActionsOnlyMixin` | Hide save buttons, use actions only |
| `SingleObjectMixin` | Redirect to edit/add page (for one-per-user models) |

### Inline Mixins

| Mixin | Description |
|-------|-------------|
| `InlineCRUDMixin` | CRUD for inline models |
| `ReadOnlyInlineMixin` | Read-only inline |

### UI Helpers

| Mixin | Description |
|-------|-------------|
| `HideRelatedFieldButtonsMixin` | Hide add/edit/delete buttons on FK fields |

## Testing Utilities

| Function | Description |
|----------|-------------|
| `get_table_constraints(table_name)` | Get all constraints and indexes for a table |
| `has_index_on_columns(table_name, columns)` | Check if an index exists on specified columns |

## Usage Examples

### Owner-based Permission

```python
@admin.register(Article)
class ArticleAdmin(OwnerMixin, ModelAdmin):
    owner_field = "author"
    list_display = ["title", "author", "created_at"]
```

### Nested Owner Field

```python
@admin.register(Comment)
class CommentAdmin(OwnerMixin, ModelAdmin):
    owner_field = "post__author"  # Comment -> Post -> Author
```

### Read-only with Owner Filter

```python
@admin.register(Order)
class OrderAdmin(ReadOnlyOwnerMixin, ModelAdmin):
    owner_field = "customer"
```

### Single Object (e.g., User Profile)

```python
@admin.register(UserProfile)
class UserProfileAdmin(SingleObjectMixin, OwnerMixin, ModelAdmin):
    owner_field = "user"
```

### Actions Only (e.g., Approval Workflow)

```python
@admin.register(WithdrawalRequest)
class WithdrawalRequestAdmin(ActionsOnlyMixin, ModelAdmin):
    readonly_fields = ["amount", "status", "created_at"]
    actions_submit_line = ["approve_action", "reject_action"]
```

### Index Validation in Tests

```python
import pytest
from toolkit.testing import has_index_on_columns

@pytest.mark.django_db
class TestDatabaseIndexes:
    def test_composite_index_exists(self):
        assert has_index_on_columns("booking", ["status", "start"])

    def test_single_column_index_exists(self):
        assert has_index_on_columns("payment", ["transaction_id"])
```

## License

MIT
