diff --git a/docs/media/plugins/plugin_admin_ui.png b/docs/media/plugins/plugin_admin_ui.png new file mode 100644 index 000000000..44802c5fc Binary files /dev/null and b/docs/media/plugins/plugin_admin_ui.png differ diff --git a/docs/plugins/development.md b/docs/plugins/development.md index 300522d67..584e00cae 100644 --- a/docs/plugins/development.md +++ b/docs/plugins/development.md @@ -90,5 +90,107 @@ class AnimalSoundsConfig(PluginConfig): * `url_slug` - Base path to use for plugin URLs (optional). If not specified, the project's `name` will be used. * `required_settings`: A list of configuration parameters that **must** be defined by the user * `default_settings`: A dictionary of configuration parameter names and their default values +* `min_version`: Minimum version of NetBox with which the plugin is compatible +* `max_version`: Maximum version of NetBox with which the plugin is compatible * `middleware`: A list of middleware classes to append after NetBox's build-in middleware. * `caching_config`: Plugin-specific cache configuration + +## Database Models + +Plugins can define their own Django models to record user data. A model is a Python representation of a database table. Model instances can be created, manipulated, and deleted using the [Django ORM](https://docs.djangoproject.com/en/stable/topics/db/). Models are typically defined within a plugin's `models.py` file, though this is not a strict requirement. + +Below is a simple example `models.py` file showing a model with two character fields: + +```python +from django.db import models + +class Animal(models.Model): + name = models.CharField(max_length=50) + sound = models.CharField(max_length=50) + + def __str__(self): + return self.name +``` + +Once you have defined the model(s) for your plugin, you'll need to create the necessary database schema migrations as well. This can be done using the Django `makemigrations` management command: + +```no-highlight +$ ./manage.py makemigrations netbox_animal_sounds +Migrations for 'netbox_animal_sounds': + /home/jstretch/animal_sounds/netbox_animal_sounds/migrations/0001_initial.py + - Create model Animal +``` + +Once the migration has been created, we can apply it locally with the `migrate` command: + +```no-highlight +$ ./manage.py migrate netbox_animal_sounds +Operations to perform: + Apply all migrations: netbox_animal_sounds +Running migrations: + Applying netbox_animal_sounds.0001_initial... OK +``` + +For more information on database migrations, see the [Django documentation](https://docs.djangoproject.com/en/stable/topics/migrations/). + +### Using the Django Admin Interface + +Plugins can optionally expose their models via Django's built-in [administrative interface](https://docs.djangoproject.com/en/stable/ref/contrib/admin/). This can greatly improve troubleshooting ability, particularly during development. An example `admin.py` file for the above model is shown below: + +```python +from django.contrib import admin +from .models import Animal + +@admin.register(Animal) +class AnimalAdmin(admin.ModelAdmin): + list_display = ('name', 'sound') +``` + +This will display the plugin and its model in the admin UI. Staff users can create, change, and delete model instances via the admin UI without needing to create a custom view. + +![NetBox plugin in the admin UI](../media/plugins/plugin_admin_ui.png) + +## Views + +A view is a particular page tied to a URL within NetBox. Views are typically defined in `views.py`, and URL patterns in `urls.py`. As an example, let's write a view which displays a random animal and the sound it makes. First, we'll create the view in `views.py`: + +```python +from django.shortcuts import render +from django.views.generic import View +from .models import Animal + +class RandomAnimalSoundView(View): + + def get(self, request): + animal = Animal.objects.order_by('?').first() + + return render(request, 'animal_sound.html', { + 'animal': animal, + }) +``` + +This view retrieves a random animal from the database and and passes it as a context variable when rendering ta template named `animal_sound.html`. To create this template, create a `templates/` directory within the plugin source directory and save the following: + +```jinja2 +{% extends '_base.html' %} + +{% block content %} +The {{ animal.name }} says {{ animal.sound }} +{% endblock %} +``` + +!!! note + Django renders templates with its own custom [template language](https://docs.djangoproject.com/en/stable/topics/templates/#the-django-template-language). This is very similar to Jinja2, however there are some important differences to be aware of. + +Finally, to make the view accessible to users, we need to register a URL for it. We do this in `urls.py`: + +```python +from django.urls import path +from .views import RandomAnimalSoundView + +urlpatterns = [ + path('random-sound/', RandomAnimalSoundView.as_view()) +] +``` + +This makes our view accessible at the URL `/plugins/animal-sounds/random-sound/`. (Remember, our `AnimalSoundsConfig` class sets our plugin's base URL to `animal-sounds`.) Viewing this URL should show the base NetBox template with our custom content inside it.