From 0b77702626f6f4fc65da67a15d0bf3e0229d871d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 23 Mar 2020 13:28:56 -0400 Subject: [PATCH] Add docs for plugin models, views --- docs/media/plugins/plugin_admin_ui.png | Bin 0 -> 23831 bytes docs/plugins/development.md | 102 +++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 docs/media/plugins/plugin_admin_ui.png diff --git a/docs/media/plugins/plugin_admin_ui.png b/docs/media/plugins/plugin_admin_ui.png new file mode 100644 index 0000000000000000000000000000000000000000..44802c5fca3eb41f7f84cfe50f25de6ca0cf55e6 GIT binary patch literal 23831 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 literal 0 HcmV?d00001 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.