Supporting Integrations¶
Overview¶
Integrations provide a friendly way for an application to allow users to link up with multiple services. An application can provide support for any number of integrations, and give users a way to manage them. Much of this is provided out of the box for you, including administration pages, but there are a few things the application must do in order to support integrations.
This guide will cover everything you need to integrate with the Djblets Integrations framework.
Architecture¶
To start with, let’s discuss the architecture.
Integrations are subclasses of
Integration
. There’s only ever
one instance per class. Each instance may have zero or more configurations,
defined as an application-provided subclass of
BaseIntegrationConfig
.
Integrations and configurations are managed by an instance of
IntegrationManager
.
Many classes need to know about the manager instance, which can’t be
determined automatically (and, theoretically, there may be multiple managers
in a single application). These classes will inherit from
NeedsIntegrationManagerMixin
. It’s
the application’s responsibility to subclass these classes and provide the
necessary method for returning the integration, which we’ll get to later.
IntegrationConfigForm
provides UI and
logic for creating or editing integration configurations.
BaseIntegrationHook
provides base
support for an extension hook, which is useful if you’re supporting
extensions in your application.
There are views
for listing and
configuring integrations. These must be subclassed, which we’ll cover in more
detail.
Getting Started¶
Updating Settings¶
First, you’ll need to update your Django settings in order to use the integrations framework.
We’re going to assume in these examples that you’re creating a new
myproject.integrations
app. Put that and djblets.integrations
in your
settings.INSTALLED_APPS
, like so:
INSTALLED_APPS = [
...
'djblets.integrations',
'myproject.integrations',
...
]
Now add the middleware:
MIDDLEWARE_CLASSES = [
...
'djblets.integrations.middleware.IntegrationsMiddleware',
...
]
If you’re also using djblets.extensions
, make sure to include this
after the extensions middleware:
MIDDLEWARE_CLASSES = [
...
'djblets.extensions.middleware.ExtensionsMiddleware',
'djblets.integrations.middleware.IntegrationsMiddleware',
...
]
Setting up IntegrationManager¶
Your application will need an instance of
IntegrationManager
. This should only
be created once per process, and every request for this manager must receive
the same instance.
When constructing the manager, a subclass of
BaseIntegrationConfig
will need to be
provided. We’ll go into what’s needed here, but keep this in mind for now.
Let’s put this in myproject/integrations/base.py
. You’ll want
something like:
from djblets.integrations.manager import IntegrationManager
_integration_manager = None
def get_integration_manager():
global _integration_manager
if not _integration_manager:
_integration_manager = IntegrationManager(MyIntegrationConfig)
return _integration_manager
You now have a handy function for getting the same instance, and for using
your MyIntegrationConfig
(which you’ll create soon).
You’re also going to want a mixin that provides this integration manager to various classes. Add:
class GetIntegrationManagerMixin(object):
@classmethod
def get_integration_manager(self):
return get_integration_manager()
Congrats, you’re one step closer to supporting integrations!
Creating an IntegrationConfig¶
BaseIntegrationConfig
is the base
class for an integration configuration database model. This stores identifying
information used to associate the configuration with a given integration, a
description of the configuration, the enabled state, settings, and more.
Applications must have a subclass of this in a
myproject/integrations/models.py
, providing it to the
IntegrationManager
as shown above.
You’ll want to mix in your GetIntegrationManagerMixin
, like so:
from djblets.integrations.models import BaseIntegrationConfig
from myproject.integrations.base import GetIntegrationManagerMixin
class IntegrationConfig(GetIntegrationManagerMixin, BaseIntegrationConfig):
pass
That’s all you need to do to get started. If you want to add some additional fields (for example, to associate one of these with a specific user, organization, etc.), then you can add fields here. For example:
Setting Up Views¶
Now that you have the base foundation for integrations and their configuration and management, you’ll need to get some views going.
Djblets ships with base views for listing integrations and creating/editing
configurations. These are
BaseIntegrationListView
and
BaseIntegrationConfigFormView
.
It also ships with versions intended for use in the administration UI:
BaseAdminIntegrationListView
and
BaseAdminIntegrationConfigFormView
.
In these examples, we’re going to assume you’re using views for the administration UI.
Whichever views you choose to use will need to be subclassed, using your
GetIntegrationManagerMixin
above. This is as simple as placing the
following in a myproject/integrations/views.py
:
from djblets.integrations.views import (BaseAdminIntegrationConfigFormView,
BaseAdminIntegrationListView)
from myproject.integrations.base import GetIntegrationManagerMixin
class AdminIntegrationConfigFormView(GetIntegrationManagerMixin,
BaseAdminIntegrationConfigFormView):
pass
class AdminIntegrationListView(GetIntegrationManagerMixin,
BaseAdminIntegrationListView):
pass
You can customize some other behavior of these views as well. See their documentation for more information.
Setting up URLs¶
Now that you have your views, you’ll need to build URLs for them. In this
example, we’ll place them in your myproject/urls.py
:
from django.conf.urls import include, patterns, url
from djblets.integrations.urls import build_integration_urlpatterns
from myproject.integrations.views import (AdminIntegrationConfigFormView,
AdminIntegrationListView)
urlpatterns = patterns(
'',
url('^admin/integrations/', include(build_integration_urlpatterns(
list_view_cls=AdminIntegrationListView,
config_form_view_cls=AdminIntegrationConfigFormView))),
...
)
You should now be set! If you go to http://yourserver/admin/integrations/
,
you should see a list of all your integrations (none, at this moment), and
will have UI for configuring them.
You can now start writing integrations.
Advanced Usage¶
Adding Fields to IntegrationConfig¶
You may want to add some special fields to your configuration model. For instance, you may want to associate it with a user, or an organization model, or maybe you want to store something else entirely.
To do this, you’ll first need to add the fields to your configuration model. We’ll show this off with a User association:
from django.contrib.auth.models import User
from djblets.integrations.models import BaseIntegrationConfig
from myproject.integrations.base import GetIntegrationManagerMixin
class IntegrationConfig(GetIntegrationManagerMixin, BaseIntegrationConfig):
user = models.ForeignKey(User, related_name='integration_configs')
This will give us an association between users and their integration configurations.
Next, you may want a special subclass of
IntegrationConfigForm
that can work
with this new field:
from django import forms
from django.contrib.auth.models import User
from djblets.integrations.forms import (IntegrationConfigForm as
BaseIntegrationConfigForm)
class IntegrationConfigForm(BaseIntegrationConfigForm):
model_fields = (
BaseIntegrationConfigForm.model_fields +
('user',)
)
user = forms.ModelChoiceField(
label='User',
queryset=User.objects.all(),
required=True)
This gives you a form that will contain these extra fields. Note that these fields will, in the default template, be presented to the user. This may not be what you want, depending on your use case!