Creating custom views

This page describes how to provide a custom view from within your plugin. Before you start reading this page, please read and understand how URL handling works in pretalx.

Organiser panel views

If you want to add a custom view to the organiser area of an event, register an URL in your urls.py that lives in the /orga/ subpath:

 1from django.urls import re_path
 2
 3from pretalx.event.models.event import SLUG_REGEX
 4
 5from . import views
 6
 7urlpatterns = [
 8    re_path(
 9        rf"^orga/event/(?P<event>{SLUG_REGEX})/p/myplugin/$",
10        views.admin_view,
11        name="backend",
12    ),
13    re_path(
14        rf"^(?P<event>{SLUG_REGEX})/p/myplugin/$",
15        views.frontend_view,
16        name="frontend",
17    ),
18    re_path(
19        rf"^p/myplugin/$",
20        views.global_view,
21        name="global-frontend",
22    ),
23]

If you just created your urls.py file and you already had the development server running, you’ll now have to restart it for the new file to be recognised.

If your view is event-specific, you have to name one parameter in your URL event. By convention, all plugin URLs except for backend URLs start with a /p/ to avoid namespace collisions with event names and reserved URLs.

You can then write a regular view. Our middleware will automatically detect the /orga/ subpath and will ensure the following points if this is an URL with the event parameter:

  • The user has logged in

  • The request.event attribute contains the current event

  • The user has permission to view the current event

If you want to require specific permission types, we provide you with a decorator or a mixin for your views:

from pretalx.common.mixins.views import PermissionRequired

class AdminView(PermissionRequired, View):
    permission_required = "submission.orga_list_submission"

There is also a signal that allows you to add the view to the event sidebar navigation like this:

 1from django.dispatch import receiver
 2from django.urls import resolve, reverse
 3from django.utils.translation import ugettext_lazy as _
 4
 5from pretalx.orga.signals import nav_event
 6
 7
 8@receiver(nav_event, dispatch_uid="friends_tickets_nav")
 9def navbar_info(sender, request, **kwargs):
10    url = resolve(request.path_info)
11    if not request.user.has_perm("event.orga_access_event", request.event):
12        return []
13    return [{
14        "label": _("My plugin view"),
15        "icon": "heart",
16        "url": reverse("plugins:myplugin:index", kwargs={
17            "event": request.event.slug,
18        }),
19        "active": url.namespace == "plugins:myplugin" and url.url_name == "view",
20    }]

Frontend views

Frontend views work pretty much like organiser area views. Take care that your URL starts with fr"^(?P<event>[{SLUG_REGEX}]+)/p/mypluginname" for event related URLs or f"^p/mypluginname" for global views. You can then write a regular view. It will be automatically ensured that:

  • The requested event exists

  • The requested event is visible (either by being public, or if an organiser looks at it)

  • The request involves the correct domain for the event

  • The request.event attribute contains the correct Event object

  • The organiser has enabled the plugin

  • The locale middleware has processed the request

API views

You can also expose Django REST Framework API endpoints from your plugin. Register your URLs with the api/events/<event>/p/<pluginname>/ prefix using a DRF Router:

 1from rest_framework import routers
 2
 3from pretalx.event.models.event import SLUG_REGEX
 4
 5from .api import MyViewSet
 6
 7router = routers.SimpleRouter()
 8router.register(
 9    f"api/events/(?P<event>{SLUG_REGEX})/p/myplugin",
10    MyViewSet,
11    basename="myplugin",
12)
13urlpatterns += router.urls

Your view should use ApiPermission & PluginPermission as its permission classes. Set plugin_required to your plugin’s name so that the endpoint is only available for events that have your plugin enabled. Access control is handled by rules_permissions on your model:

 1from rest_framework import serializers, viewsets
 2from rules.contrib.models import RulesModelBase, RulesModelMixin
 3
 4from pretalx.api.permissions import ApiPermission, PluginPermission
 5
 6
 7class MyModel(RulesModelMixin, models.Model, metaclass=RulesModelBase):
 8    class Meta:
 9        rules_permissions = {
10            "list": my_list_rule,
11            "create": my_create_rule,
12            "update": my_create_rule,
13        }
14
15
16class MySerializer(serializers.ModelSerializer):
17    class Meta:
18        model = MyModel
19        fields = ["id", "name"]
20
21
22class MyViewSet(viewsets.ModelViewSet):
23    serializer_class = MySerializer
24    queryset = MyModel.objects.none()
25    permission_classes = [ApiPermission & PluginPermission]
26    plugin_required = "pretalx_myplugin"
27
28    def get_queryset(self):
29        return MyModel.objects.filter(event=self.request.event)

Token-based API authentication (Authorization: Token <key>) works automatically — the ApiPermission class skips the fine-grained endpoint permission check for plugin views that don’t participate in the core endpoint system, while still enforcing event access and object-level permissions via Django rules.