An open API service indexing awesome lists of open source software.

https://github.com/ixc/wagtail-instance-selector

A widget for Wagtail's admin that allows you to create and select related items
https://github.com/ixc/wagtail-instance-selector

Last synced: 2 months ago
JSON representation

A widget for Wagtail's admin that allows you to create and select related items

Awesome Lists containing this project

README

        

# wagtail-instance-selector

A widget for Wagtail's admin that allows you to create and select related items.

- [Features and screenshots](#features-and-screenshots)
- [Installation](#installation)
- [Documentation](#documentation)
- [Using the widget as a field panel](#using-the-widget-as-a-field-panel)
- [Using the widget in a stream field](#using-the-widget-in-a-stream-field)
- [Customizing the widget's display and behaviour](#customizing-the-widgets-display-and-behaviour)
- [Rationale & Credits](#rationale--credits)
- [Development notes](#development-notes)

## Features and screenshots

### Customizable widget display

By default, widgets appear similar to other Wagtail elements, but they can be customised to include images
and other items.

![](./images/fields.png)

### Item selection reuses the admin's list views to ensure consistent UIs with filtering.

![](./images/list_view.png)

### Inline creation

Items can be created within the selection widget.

![](./images/creation.png)

After creation, items can be selected from the success message or from the list view.

![](./images/post_creation.png)

## Installation

```
pip install wagtail-instance-selector
```

and add `'instance_selector'` to `INSTALLED_APPS`.

If you're using Django 3+, you will need to change Django's iframe security flag in your settings:

```python
X_FRAME_OPTIONS = 'SAMEORIGIN'
```

## Documentation

### Using the widget as a field panel

```python
from django.db import models
from instance_selector.edit_handlers import InstanceSelectorPanel

class Shop(models.Model):
pass

class Product(models.Model):
shop = models.ForeignKey(Shop, on_delete=models.CASCADE)

panels = [InstanceSelectorPanel("shop")]
```

#### Using the widget in a stream field

```python
from django.db import models
from wagtail.admin.panels import FieldPanel
from wagtail.fields import StreamField
from instance_selector.blocks import InstanceSelectorBlock

class Product(models.Model):
pass

class Service(models.Model):
pass

class Shop(models.Model):
content = StreamField([
("products", InstanceSelectorBlock(target_model="test_app.Product")),
("services", InstanceSelectorBlock(target_model="test_app.Service")),
], use_json_field=True)

panels = [FieldPanel("content")]
```

To create reusable blocks, you can subclass `InstanceSelectorBlock`.

```python
from instance_selector.blocks import InstanceSelectorBlock

class ProductBlock(InstanceSelectorBlock):
def __init__(self, *args, **kwargs):
target_model = kwargs.pop("target_model", "my_app.Product")
super().__init__(target_model=target_model, **kwargs)

class Meta:
icon = "image"

# ...

StreamField([
("products", ProductBlock()),
])
```

### Customizing the widget's display and behaviour

```python
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from instance_selector.registry import registry
from instance_selector.selectors import ModelAdminInstanceSelector
from .models import MyModel

@modeladmin_register
class MyModelAdmin(ModelAdmin):
model = MyModel

class MyModelInstanceSelector(ModelAdminInstanceSelector):
model_admin = MyModelAdmin()

def get_instance_display_title(self, instance):
if instance:
return "some title"

def get_instance_display_image_url(self, instance):
if instance:
return "/url/to/some/image.jpg"

def get_instance_display_image_styles(self, instance):
# The `style` properties set on the element, primarily of use
# to work within style+layout patterns
if instance:
return {
'max-width': '165px',
# ...
}

def get_instance_display_markup(self, instance):
# Overriding this method allows you to completely control how the
# widget will display the relation to this specific model
return "

...
"

def get_instance_display_template(self):
# The template used by `get_instance_display_markup`
return "instance_selector/instance_selector_widget_display.html"

def get_instance_selector_url(self):
# The url that the widget will render within a modal. By default, this
# is the ModelAdmin"s list view
return "/url/to/some/view/"

def get_instance_edit_url(self, instance):
# The url that the user can edit the instance on. By default, this is
# the ModelAdmin"s edit view
if instance:
return "/url/to/some/view/"

registry.register_instance_selector(MyModel, MyModelInstanceSelector())
```

Note that the `ModelAdminInstanceSelector` is designed for the common case. If your needs
are more specific, you may find some use in `instance_selector.selectors.BaseInstanceSelector`.

## Rationale & Credits

Largely, this is a rewrite of [neon-jungle/wagtailmodelchooser](https://github.com/neon-jungle/wagtailmodelchooser)
that focuses on reusing the functionality in the ModelAdmins. We had started a large build using wagtailmodelchooser
heavily, but quickly ran into UI problems when users needed to filter the objects or create them inline. After
[neon-jungle/wagtailmodelchooser#11](https://github.com/neon-jungle/wagtailmodelchooser/issues/11) received little
response, the decision was made to piece together parts from the ecosystem and replicate the flexibility of
django's `raw_id_fields`, while preserving the polish in Wagtail's UI.

Much of this library was built atop of the work of others, specifically:
- https://github.com/neon-jungle/wagtailmodelchooser
- https://github.com/springload/wagtailmodelchoosers
- https://github.com/Naeka/wagtailmodelchooser

## Development notes

### Run tests

```
pip install -r requirements.txt
python runtests.py
```

### Formatting

```
pip install -r requirements.txt
black .
```