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 pretix.
Control panel views¶
If you want to add a custom view to the control area of an event, just register an URL in your
urls.py
that lives in the /control/
subpath:
1from django.urls import re_path
2
3from . import views
4
5urlpatterns = [
6 re_path(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/mypluginname/',
7 views.admin_view, name='backend'),
8]
It is required that your URL parameters are called organizer
and event
. If you want to
install a view on organizer level, you can leave out the event
.
You can then implement the view as you would normally do. Our middleware will automatically
detect the /control/
subpath and will ensure the following things if this is an URL with
both the event
and organizer
parameters:
The user is logged in
The
request.event
attribute contains the current eventThe
request.organizer
attribute contains the event’s organizerThe user has permission to access view the current event
If only the organizer
parameter is present, it will be ensured that:
The user is logged in
The
request.organizer
attribute contains the event’s organizerThe user has permission to access view the current organizer
If you want to require specific permission types, we provide you with a decorator or a mixin for your views:
1from pretix.control.permissions import (
2 event_permission_required, EventPermissionRequiredMixin
3)
4
5class AdminView(EventPermissionRequiredMixin, View):
6 permission = 'can_view_orders'
7
8 ...
9
10
11@event_permission_required('can_view_orders')
12def admin_view(request, organizer, event):
13 ...
Similarly, there is organizer_permission_required
and OrganizerPermissionRequiredMixin
. In case of
event-related views, there is also a signal that allows you to add the view to the event navigation like this:
1from django.urls import resolve, reverse
2from django.dispatch import receiver
3from django.utils.translation import gettext_lazy as _
4from pretix.control.signals import nav_event
5
6
7@receiver(nav_event, dispatch_uid='friends_tickets_nav')
8def navbar_info(sender, request, **kwargs):
9 url = resolve(request.path_info)
10 if not request.user.has_event_permission(request.organizer, request.event, 'can_change_vouchers'):
11 return []
12 return [{
13 'label': _('My plugin view'),
14 'icon': 'heart',
15 'url': reverse('plugins:myplugin:index', kwargs={
16 'event': request.event.slug,
17 'organizer': request.organizer.slug,
18 }),
19 'active': url.namespace == 'plugins:myplugin' and url.url_name == 'review',
20 }]
Event settings view¶
A special case of a control panel view is a view hooked into the event settings page. For this case, there is a special navigation signal:
1@receiver(nav_event_settings, dispatch_uid='friends_tickets_nav_settings')
2def navbar_settings(sender, request, **kwargs):
3 url = resolve(request.path_info)
4 return [{
5 'label': _('My settings'),
6 'url': reverse('plugins:myplugin:settings', kwargs={
7 'event': request.event.slug,
8 'organizer': request.organizer.slug,
9 }),
10 'active': url.namespace == 'plugins:myplugin' and url.url_name == 'settings',
11 }]
Also, your view should inherit from EventSettingsViewMixin
and your template from pretixcontrol/event/settings_base.html
for good integration. If you just want to display a form, you could do it like the following:
1class MySettingsView(EventSettingsViewMixin, EventSettingsFormView):
2 model = Event
3 permission = 'can_change_settings'
4 form_class = MySettingsForm
5 template_name = 'my_plugin/settings.html'
6
7 def get_success_url(self, **kwargs):
8 return reverse('plugins:myplugin:settings', kwargs={
9 'organizer': self.request.event.organizer.slug,
10 'event': self.request.event.slug,
11 })
With this template:
1{% extends "pretixcontrol/event/settings_base.html" %}
2{% load i18n %}
3{% load bootstrap3 %}
4{% block title %} {% trans "Friends Tickets Settings" %} {% endblock %}
5{% block inside %}
6 <form action="" method="post" class="form-horizontal">
7 {% csrf_token %}
8 <fieldset>
9 <legend>{% trans "Friends Tickets Settings" %}</legend>
10 {% bootstrap_form form layout="horizontal" %}
11 </fieldset>
12 <div class="form-group submit-group">
13 <button type="submit" class="btn btn-primary btn-save">
14 {% trans "Save" %}
15 </button>
16 </div>
17 </form>
18{% endblock %}
Frontend views¶
Including a custom view into the participant-facing frontend is a little bit different as there is
no path prefix like control/
.
First, define your URL in your urls.py
, but this time in the event_patterns
section and wrapped by
event_url
:
1from pretix.multidomain import event_url
2
3from . import views
4
5event_patterns = [
6 event_url(r'^mypluginname/', views.frontend_view, name='frontend'),
7]
You can then implement a view as you would normally do. It will be automatically ensured that:
The requested event exists
The requested event is active (you can disable this check using
event_url(…, require_live=True)
)The event is accessed via the domain it should be accessed
The
request.event
attribute contains the correctEvent
objectThe
request.organizer
attribute contains the correctOrganizer
objectYour plugin is enabled
The locale is set correctly
Changed in version 1.7: The event_url()
wrapper has been added in 1.7 to replace the former @event_view
decorator. The
event_url()
wrapper is optional and using url()
still works, but you will not be able to set the
require_live
setting any more via the decorator. The @event_view
decorator is now deprecated and
does nothing.
REST API viewsets¶
Our REST API is built upon Django REST Framework (DRF). DRF has two important concepts that are different from standard Django request handling: There are ViewSets to group related views in a single class and Routers to automatically build URL configurations from them.
To integrate a custom viewset with pretix’ REST API, you can just register with one of our routers within the
urls.py
module of your plugin:
1from pretix.api.urls import event_router, router, orga_router
2
3router.register('global_viewset', MyViewSet)
4orga_router.register('orga_level_viewset', MyViewSet)
5event_router.register('event_level_viewset', MyViewSet)
Routes registered with router
are inserted into the global API space at /api/v1/
. Routes registered with
orga_router
will be included at /api/v1/organizers/(organizer)/
and routes registered with event_router
will be included at /api/v1/organizers/(organizer)/events/(event)/
.
In case of orga_router
and event_router
, permission checking is done for you similarly as with custom views
in the control panel. However, you need to make sure on your own only to return the correct subset of data! request
.event
and request.organizer
are available as usual.
To require a special permission like can_view_orders
, you do not need to inherit from a special ViewSet base
class, you can just set the permission
attribute on your viewset:
class MyViewSet(ModelViewSet):
permission = 'can_view_orders'
...
If you want to check the permission only for some methods of your viewset, you have to do it yourself. Note here that API authentications can be done via user sessions or API tokens and you should therefore check something like the following:
perm_holder = (request.auth if isinstance(request.auth, TeamAPIToken) else request.user)
if perm_holder.has_event_permission(request.event.organizer, request.event, 'can_view_orders'):
...
Warning
It is important that you do this in the yourplugin.urls
module, otherwise pretix will not find your
routes early enough during system startup.