mirror of
https://github.com/pretix/pretix.git
synced 2026-01-05 21:32:26 +00:00
Fixed minor documentation errors and mistakes (#151)
This commit is contained in:
committed by
Raphael Michel
parent
f779b70deb
commit
bfc721978d
@@ -4,7 +4,7 @@
|
||||
Writing an exporter plugin
|
||||
==========================
|
||||
|
||||
An is a method to export the product and order data in pretix for later use in another
|
||||
An Exporter is a method to export the product and order data in pretix for later use in another
|
||||
context.
|
||||
|
||||
In this document, we will walk through the creation of an exporter output plugin. This
|
||||
@@ -18,7 +18,7 @@ Exporter registration
|
||||
The exporter API does not make a lot of usage from signals, however, it does use a signal to get a list of
|
||||
all available exporters. Your plugin should listen for this signal and return the subclass of
|
||||
``pretix.base.exporter.BaseExporter``
|
||||
that we'll soon create::
|
||||
that we'll provide in this plugin::
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
@@ -36,8 +36,7 @@ The exporter class
|
||||
|
||||
.. class:: pretix.base.exporter.BaseExporter
|
||||
|
||||
The central object of each exporter is the subclass of ``BaseExporter`` we already mentioned above.
|
||||
In this section, we will discuss it's interface in detail.
|
||||
The central object of each exporter is the subclass of ``BaseExporter``.
|
||||
|
||||
.. py:attribute:: BaseExporter.event
|
||||
|
||||
|
||||
@@ -60,8 +60,8 @@ Dashboards
|
||||
:members: event_dashboard_widgets, user_dashboard_widgets
|
||||
|
||||
|
||||
Displaying of log entries
|
||||
-------------------------
|
||||
Display of log entries
|
||||
----------------------
|
||||
|
||||
.. automodule:: pretix.base.signals
|
||||
:members: logentry_display
|
||||
|
||||
@@ -14,7 +14,7 @@ Provider registration
|
||||
The payment provider API does not make a lot of usage from signals, however, it
|
||||
does use a signal to get a list of all available payment providers. Your plugin
|
||||
should listen for this signal and return the subclass of ``pretix.base.payment.BasePaymentProvider``
|
||||
that we'll soon create::
|
||||
that the plugin will provide::
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
@@ -32,8 +32,7 @@ The provider class
|
||||
|
||||
.. class:: pretix.base.payment.BasePaymentProvider
|
||||
|
||||
The central object of each payment provider is the subclass of ``BasePaymentProvider``
|
||||
we already mentioned above. In this section, we will discuss it's interface in detail.
|
||||
The central object of each payment provider is the subclass of ``BasePaymentProvider``.
|
||||
|
||||
.. py:attribute:: BasePaymentProvider.event
|
||||
|
||||
@@ -105,9 +104,9 @@ Additional views
|
||||
For most simple payment providers it is more than sufficient to implement
|
||||
some of the :py:class:`BasePaymentProvider` methods. However, in some cases
|
||||
it is necessary to introduce additional views. One example is the PayPal
|
||||
provider. It redirects the user to a paypal website in the
|
||||
:py:meth:`BasePaymentProvider.checkout_prepare`` step of the checkout process
|
||||
and provides PayPal with an URL to redirect back to. This URL points to a
|
||||
provider. It redirects the user to a PayPal website in the
|
||||
:py:meth:`BasePaymentProvider.checkout_prepare` step of the checkout process
|
||||
and provides PayPal with a URL to redirect back to. This URL points to a
|
||||
view which looks roughly like this::
|
||||
|
||||
@login_required
|
||||
@@ -123,7 +122,7 @@ view which looks roughly like this::
|
||||
try:
|
||||
# Redirect back to the confirm page. We chose to save the
|
||||
# event ID in the user's session. We could also put this
|
||||
# information into an URL parameter.
|
||||
# information into a URL parameter.
|
||||
event = Event.objects.current.get(identity=request.session['payment_paypal_event'])
|
||||
return redirect(reverse('presale:event.checkout.confirm', kwargs={
|
||||
'event': event.slug,
|
||||
@@ -136,6 +135,6 @@ view which looks roughly like this::
|
||||
|
||||
If you do not want to provide a view of your own, you could even let PayPal
|
||||
redirect directly back to the confirm page and handle the query parameters
|
||||
inside :py:meth:`BasePaymentProvider.checkout_is_valid_session``. However,
|
||||
inside :py:meth:`BasePaymentProvider.checkout_is_valid_session`. However,
|
||||
because some external providers (not PayPal) force you to have a *constant*
|
||||
redirect URL, it might be necessary to define custom views.
|
||||
|
||||
@@ -6,7 +6,7 @@ Plugin basics
|
||||
|
||||
It is possible to extend pretix with custom Python code using the official plugin
|
||||
API. Every plugin has to be implemented as an independent Django 'app' living
|
||||
in an own python package nstalled like any other python module. There are also some
|
||||
in its own python package installed like any other python module. There are also some
|
||||
official plugins inside the ``pretix/plugins/`` directory of your pretix installation.
|
||||
|
||||
The communication between pretix and the plugins happens mostly using Django's
|
||||
@@ -16,13 +16,13 @@ on the next pages.
|
||||
|
||||
.. _`pluginsetup`:
|
||||
|
||||
To create a new plugin, create a new python package which must be a vaild `Django app`_
|
||||
To create a new plugin, create a new python package which must be a valid `Django app`_
|
||||
and must contain plugin metadata, as described below.
|
||||
|
||||
The following pages go into detail about the several types of plugins currently
|
||||
supported. While these instructions don't assume that you know a lot about pretix,
|
||||
they do assume that you have prior knowledge about Django (e.g. it's view layer,
|
||||
how it's ORM works, etc.).
|
||||
they do assume that you have prior knowledge about Django (e.g. its view layer,
|
||||
how its ORM works, etc.).
|
||||
|
||||
Plugin metadata
|
||||
---------------
|
||||
@@ -83,7 +83,7 @@ Plugin registration
|
||||
|
||||
Somehow, pretix needs to know that your plugin exists at all. For this purpose, we
|
||||
make use of the `entry point`_ feature of setuptools. To register a plugin that lives
|
||||
in a seperate python package, your ``setup.py`` sould contain something like this::
|
||||
in a separate python package, your ``setup.py`` should contain something like this::
|
||||
|
||||
setup(
|
||||
…
|
||||
@@ -129,4 +129,4 @@ your Django app label.
|
||||
.. _Django app: https://docs.djangoproject.com/en/1.7/ref/applications/
|
||||
.. _signal dispatcher: https://docs.djangoproject.com/en/1.7/topics/signals/
|
||||
.. _namespace packages: http://legacy.python.org/dev/peps/pep-0420/
|
||||
.. _entry point: https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
|
||||
.. _entry point: https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
|
||||
|
||||
@@ -17,7 +17,7 @@ Output registration
|
||||
The ticket output API does not make a lot of usage from signals, however, it
|
||||
does use a signal to get a list of all available ticket outputs. Your plugin
|
||||
should listen for this signal and return the subclass of ``pretix.base.ticketoutput.BaseTicketOutput``
|
||||
that we'll soon create::
|
||||
that we'll provide in this plugin::
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
@@ -35,8 +35,7 @@ The output class
|
||||
|
||||
.. class:: pretix.base.ticketoutput.BaseTicketOutput
|
||||
|
||||
The central object of each ticket output is the subclass of ``BaseTicketOutput``
|
||||
we already mentioned above. In this section, we will discuss it's interface in detail.
|
||||
The central object of each ticket output is the subclass of ``BaseTicketOutput``.
|
||||
|
||||
.. py:attribute:: BaseTicketOutput.event
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ The project pretix is split into several components. The main three of them are:
|
||||
create and manage their events, items, orders and tickets.
|
||||
|
||||
**pretix.presale**
|
||||
Pretixpresale is the ticket-shop itself, containing all the parts visible to the
|
||||
Pretixpresale is the ticket-shop itself, containing all of the parts visible to the
|
||||
end user.
|
||||
|
||||
Users and events
|
||||
@@ -28,25 +28,24 @@ Users and events
|
||||
Pretix is all about **events**, which are defined as something happening somewhere.
|
||||
Every event is managed by the **organizer**, an abstract entity running the event.
|
||||
|
||||
Pretix has a concept of **users** that is used for all the people who have to log
|
||||
in to the control panel to manage one or more events. No user is required to place an
|
||||
order.
|
||||
Pretix has a concept of **users** that is used for all people who have to log in to the
|
||||
control panel to manage one or more events. No user is required to place an order.
|
||||
|
||||
|
||||
Items and variations
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The purpose of pretix is to sell **items** (which belong to **events**) to **users**.
|
||||
An **item** is a abstract thing, popular examples being event tickets or a piece of
|
||||
The purpose of pretix is to sell **items** (which belong to **events**) to **users**.
|
||||
An **item** is a abstract thing, popular examples being an event ticket or a piece of
|
||||
merchandise, like 'T-shirt'. An **item** can have multiple **variations**. For example,
|
||||
the **item** 'T-Shirt' could have the **variations** S', 'M' and 'L'.
|
||||
the **item** 'T-Shirt' could have the **variations** 'S', 'M' and 'L'.
|
||||
|
||||
Questions
|
||||
^^^^^^^^^
|
||||
|
||||
An item can be extended using **questions**. Questions enable items to be extended by
|
||||
additional information which can be entered by the user. Examples of possible questions
|
||||
include 'Name' or 'age'.
|
||||
include 'name' or 'age'.
|
||||
|
||||
Restriction by number
|
||||
"""""""""""""""""""""
|
||||
@@ -59,17 +58,16 @@ special care in the implementation to never sell more tickets than allowed, even
|
||||
* Every time a user places a item in the cart, a **cart position** is created, reducing the number of
|
||||
available items in the pool by one. The position is valid for a fixed time (e.g. 30 minutes), but not
|
||||
instantly deleted after those 30 minutes (we'll get to that).
|
||||
* Every time a user places a binding order, the position object is replaced by an **order position** which behaves
|
||||
much the same as the cart position. It reduces the number of available item and is valid for a fixed time, this
|
||||
time for the configured payment term (e.g. 14 days).
|
||||
* Every time a user places a binding order, the **cart position** object is replaced by an **order position**
|
||||
object which behaves much the same as the cart position. It reduces the number of available items and is valid
|
||||
for a fixed time, this time for the configured payment term (e.g. 14 days).
|
||||
* If the order is being paid, the **order** becomes permanent.
|
||||
* Once there are no available tickets left and user A wants to buy a ticket, he can do so, as long as
|
||||
there are *expired* cart position in the system. In this case, user A gets a new cart position, so that there
|
||||
are more cart position than available tickets and therefore have to remove one of the expired cart positions.
|
||||
However, we do not choose one by random, but keep the surplus in a way that leads to the deletion
|
||||
of the cart position f the user who tries *last* to use his cart position.
|
||||
* The same goes for orders which are not paid within the specified timeframe. This policy allows the organizer to
|
||||
sell as much items as possible. Moreover, it guarantees the users to get their items if they check out within the validity
|
||||
* Once there are no available tickets left and user A wants to buy a ticket, they can do so, as long as
|
||||
there are *expired* cart position in the system. In this case, user A gets a new cart position. Now there are
|
||||
more cart positions than available tickets and therefore we have to remove one of the expired cart positions.
|
||||
We will choose to delete the cart position of the user who tries *last* to use his cart position.
|
||||
* The same goes for orders which are not paid within the configured time frame. This policy allows the organizer to
|
||||
sell as many items as possible. Moreover, it guarantees the users to get their items if they check out within the validity
|
||||
period of their positions and pay within the validity period of their orders. It does not guarantee them anything
|
||||
any longer, but it tries to be *as tolerant as possible* to users who are paying after their payment
|
||||
period or click checkout after the expiry of their position.
|
||||
|
||||
@@ -20,11 +20,11 @@ on our `GitHub repository`_.
|
||||
|
||||
Before you do so, please `squash all your changes`_ into one single commit. Please
|
||||
use the test suite to check whether your changes break any existing features and run
|
||||
the code style checks to confirm you are consistent with pretix' coding style. You'll
|
||||
the code style checks to confirm you are consistent with pretix's coding style. You'll
|
||||
find instructions on this in the :ref:`checksandtests` section of the development setup guide.
|
||||
|
||||
We automatically run the tests and the code style check on every pull request on Travis CI and we won’t
|
||||
accept any pull requets without all tests passing. However, if you don't find out *why* they are not passing,
|
||||
accept any pull requests without all tests passing. However, if you don't find out *why* they are not passing,
|
||||
just send the pull request and tell us – we'll be glad to help.
|
||||
|
||||
If you add a new feature, please include appropriate documentation into your patch. If you fix a bug,
|
||||
@@ -34,4 +34,4 @@ Again: If you get stuck, do not hesitate to contact any of us, or Raphael person
|
||||
|
||||
.. _create a pull request: https://help.github.com/articles/creating-a-pull-request/
|
||||
.. _GitHub repository: https://github.com/pretix/pretix
|
||||
.. _squash all your changes: https://davidwalsh.name/squash-commits-git
|
||||
.. _squash all your changes: https://davidwalsh.name/squash-commits-git
|
||||
|
||||
@@ -6,7 +6,7 @@ Python code
|
||||
|
||||
* Basically: Follow `PEP 8`_.
|
||||
|
||||
Use `flake8`_ to check for conformance problems. The project includes a setup.cfg
|
||||
Use `flake8`_ to check for conformance problems. The project includes a setup.cfg file
|
||||
with a default configuration for flake8 that excludes migrations and other non-relevant
|
||||
code parts. It also silences a few checks, e.g. ``N802`` (function names should be lowercase)
|
||||
and increases the maximum line length to more than 79 characters. **However** you should
|
||||
|
||||
@@ -2,7 +2,7 @@ Background tasks
|
||||
================
|
||||
|
||||
pretix provides the ability to run all longer-running tasks like generating ticket files or sending emails
|
||||
in a background thread instead of the webserver process. We use the well-established `Celery`_ project to
|
||||
in a background thread instead of the web server process. We use the well-established `Celery`_ project to
|
||||
implement this. However, as celery requires running a task queue like RabbitMQ and a result storage such as
|
||||
Redis to work efficiently, we don't like to *depend* on celery being available to make small-scale installations
|
||||
of pretix more straightforward. For this reason, the "background" in "background task" is always optional.
|
||||
@@ -13,8 +13,8 @@ in the current installation.
|
||||
Implementing a task
|
||||
-------------------
|
||||
|
||||
A common pattern for implementing "optionally-asynchronous" tasks that can be seen a lot in ``pretix.base.services``
|
||||
looks like this::
|
||||
A common pattern for implementing "optionally-asynchronous" tasks can be seen a lot in ``pretix.base.services``
|
||||
and looks like this::
|
||||
|
||||
def my_task(argument1, argument2):
|
||||
# Important: All arguments and return values need to be serializable into JSON.
|
||||
@@ -45,7 +45,7 @@ A usage example taken directly from the code is::
|
||||
class OrderCancelDo(EventViewMixin, OrderDetailMixin, AsyncAction, View):
|
||||
"""
|
||||
A view that executes a task asynchronously. A POST request will kick of the
|
||||
task into the background or run it in the foreground, if celery is not installed.
|
||||
task into the background or run it in the foreground if celery is not installed.
|
||||
In the former case, subsequent GET calls can be used to determinine the current
|
||||
status of the task.
|
||||
"""
|
||||
@@ -93,4 +93,4 @@ AJAX sending of the form and display a loading indicator::
|
||||
...
|
||||
</form>
|
||||
|
||||
.. _Celery: http://www.celeryproject.org/
|
||||
.. _Celery: http://www.celeryproject.org/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Sending Email
|
||||
=============
|
||||
|
||||
As pretix allows event organizers to configure how they want to sent emails to their users in multiple ways.
|
||||
Therefore, all emails should be sent throught the following function:
|
||||
pretix allows event organizers to configure how they want to send emails to their users in multiple ways.
|
||||
Therefore, all emails should be sent through the following function:
|
||||
|
||||
.. autofunction:: pretix.base.services.mail.mail
|
||||
.. autofunction:: pretix.base.services.mail.mail
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
Internationalization
|
||||
====================
|
||||
|
||||
One of pretix' major selling points is it's multi-language capability. We make heavy use of Django's
|
||||
One of pretix's major selling points is its multi-language capability. We make heavy use of Django's
|
||||
`translation features`_ that are built upon `GNU gettext`_. However, Django does not provide a standard
|
||||
way to translate *user-generated content*. In our case, we need to translate strings like product names
|
||||
or event descriptions, so we need event organizers to be able to fill in all fields in multiple languages
|
||||
at the same time.
|
||||
or event descriptions, so we need event organizers to be able to fill in all fields in multiple languages.
|
||||
|
||||
.. note:: Implementing object-level translation in a relational database is a task that requires some difficult
|
||||
trade-off. We decided for a design that is not elegant on the database level (as it violates the `1NF`_) and
|
||||
@@ -21,8 +20,8 @@ pretix provides two custom model field types that allow you to work with localiz
|
||||
``I18nTextField``. Both of them are stored in the database as a ``TextField`` internally, they only differ in the
|
||||
default form widget that is used by ``ModelForm``.
|
||||
|
||||
Yes, we know that this has negative impact on performance when indexing or searching them, but as mentioned above,
|
||||
within pretix this is not used in places that need to be searched. Lookups are currently not even implemented on these
|
||||
As pretix does not use these fields in places that need to be searched, the negative performance impact when searching
|
||||
and indexing these fields in negligible, as mentioned above. Lookups are currently not even implemented on these
|
||||
fields. In the database, the strings will be stored as a JSON-encoded mapping of language codes to strings.
|
||||
|
||||
Whenever you interact with those fields, you will either provide or receive an instance of the following class:
|
||||
@@ -58,8 +57,8 @@ The ``i18n`` module contains a few more useful utilities, starting with simple l
|
||||
numbers and dates, ``LazyDate`` and ``LazyNumber``. There also is a ``LazyLocaleException`` base class that provides
|
||||
exceptions with gettext-localized exception messages.
|
||||
|
||||
Last, but definitely not least, we have the ``language`` context manager that allows you to execute a piece of code with
|
||||
a different locale::
|
||||
Last, but definitely not least, we have the ``language`` context manager (``pretix.base.i18n.language``) that allows
|
||||
you to execute a piece of code with a different locale::
|
||||
|
||||
with language('de'):
|
||||
render_mail_template()
|
||||
@@ -69,4 +68,4 @@ action that causes the mail to be sent.
|
||||
|
||||
.. _translation features: https://docs.djangoproject.com/en/1.9/topics/i18n/translation/
|
||||
.. _GNU gettext: https://www.gnu.org/software/gettext/
|
||||
.. _1NF: https://en.wikipedia.org/wiki/First_normal_form
|
||||
.. _1NF: https://en.wikipedia.org/wiki/First_normal_form
|
||||
|
||||
@@ -2,7 +2,7 @@ Implementation and Utilities
|
||||
============================
|
||||
|
||||
This chapter describes the various inner workings that power pretix, most of them living in ``pretix.base``.
|
||||
If you want to develop around pretix' core or advanced plugins, this aims to describe everything you absolutely
|
||||
If you want to develop around pretix's core or advanced plugins, this aims to describe everything you absolutely
|
||||
need to know.
|
||||
|
||||
Contents:
|
||||
|
||||
@@ -3,7 +3,8 @@ Settings storage
|
||||
|
||||
pretix is highly configurable and therefore needs to store a lot of per-event and per-organizer settings.
|
||||
Those settings are stored in the database and accessed through a ``SettingsProxy`` instance. You can obtain
|
||||
such an instance from any event or organizer model instance by just accessing ``event.settings``.
|
||||
such an instance from any event or organizer model instance by just accessing ``event.settings`` or
|
||||
``organizer.settings``, respectively.
|
||||
|
||||
Any setting consists of a key and a value. By default, all settings are strings, but the settings system
|
||||
includes serializers for serializing the following types:
|
||||
|
||||
@@ -4,8 +4,8 @@ Working with URLs
|
||||
=================
|
||||
|
||||
As soon as you write a plugin that provides a new view to the user (or if you want to
|
||||
contribute to pretix itself), you need to understand how URLs work in pretix as it slightly
|
||||
differs from the standard Django system.
|
||||
contribute to pretix itself), you need to understand how URLs work in pretix as it differs
|
||||
slightly from the standard Django system.
|
||||
|
||||
The reason for the complicated URL handling is that pretix supports custom subdomains for
|
||||
single organizers. In this example we will use an event organizer with the slug ``bigorg``
|
||||
@@ -24,10 +24,10 @@ URL routing
|
||||
The hard part about implementing this URL routing in Django is that
|
||||
``https://pretix.eu/bigorg/awesomecon/`` contains two parameters of nearly arbitrary content
|
||||
and ``https://tickets.bigorg.com/awesomecon/`` contains only one. The only robust way to do
|
||||
this is by having *seperate* URL configuration for those two cases. In pretix, we call the
|
||||
former our ``maindomain`` config and the latter our ``subdomain`` config. For pretix' core
|
||||
this is by having *separate* URL configuration for those two cases. In pretix, we call the
|
||||
former our ``maindomain`` config and the latter our ``subdomain`` config. For pretix's core
|
||||
modules we do some magic to avoid duplicate configuration, but for a fairly simple plugin with
|
||||
only a handful of routes, we recommend just configuring the two URL sets seperately.
|
||||
only a handful of routes, we recommend just configuring the two URL sets separately.
|
||||
|
||||
The file ``maindomain_urls.py`` inside your plugin package will be loaded and scanned for
|
||||
URL configuration automatically and should be provided by any plugin that provides any view.
|
||||
@@ -63,12 +63,12 @@ If you only provide URLs in the admin area, you do not need to provide a ``subdo
|
||||
URL reversal
|
||||
------------
|
||||
|
||||
pretix uses Django's URL namespacing feature. The URLs of pretix' core are available in the ``control``
|
||||
pretix uses Django's URL namespacing feature. The URLs of pretix's core are available in the ``control``
|
||||
and ``presale`` namespaces, there are only very few URLs in the root namespace. Your plugin's URLs will
|
||||
be available in the ``plugins:<applabel>`` namespace, e.g. the form of the email sending plugin is
|
||||
available as ``plugins:sendmail:send``.
|
||||
|
||||
Generating an URL for the frontend is a complicated task, because you need to know whether the event's
|
||||
Generating a URL for the frontend is a complicated task, because you need to know whether the event's
|
||||
organizer uses a custom URL or not and then generate the URL with a different domain and different
|
||||
arguments based on this information. pretix provides some helpers to make this easier. The first helper
|
||||
is a python method that emulates a behaviour similar to ``reverse``:
|
||||
@@ -87,4 +87,4 @@ Implementation details
|
||||
|
||||
There are some other caveats when using a design like this, e.g. you have to care about cookie domains
|
||||
and referer verification yourself. If you want to see how we built this, look into the ``pretix/multidomain/``
|
||||
sub-tree.
|
||||
sub-tree.
|
||||
|
||||
@@ -22,8 +22,8 @@ Your local python environment
|
||||
-----------------------------
|
||||
|
||||
Please execute ``python -V`` or ``python3 -V`` to make sure you have Python 3.4
|
||||
installed. Also make sure you have pip for Python 3 installed, you can execute
|
||||
``pip3 -V`` to check. Then use Python 3.4's internal tools to create a virtual
|
||||
(or newer) installed. Also make sure you have pip for Python 3 installed, you can
|
||||
execute ``pip3 -V`` to check. Then use Python's internal tools to create a virtual
|
||||
environment and activate it for your current session::
|
||||
|
||||
pyvenv env
|
||||
|
||||
@@ -10,14 +10,14 @@ pretix/
|
||||
This directory contains nearly all source code.
|
||||
|
||||
base/
|
||||
This is the django app containing all the models and methods which are
|
||||
This is the Django app containing all the models and methods which are
|
||||
essential to all of pretix's features.
|
||||
|
||||
control/
|
||||
This is the django app containing the front end for organizers.
|
||||
This is the Django app containing the front end for organizers.
|
||||
|
||||
presale/
|
||||
This is the django app containing the front end for users buying tickets.
|
||||
This is the Django app containing the front end for users buying tickets.
|
||||
|
||||
helpers/
|
||||
Helpers contain a very few modules providing workarounds for low-level flaws in
|
||||
|
||||
Reference in New Issue
Block a user