Add a custom context manager for rollbacks (#282)

This commit is contained in:
Raphael Michel
2016-10-21 10:40:04 +02:00
committed by GitHub
parent 5923af37f3
commit 0dece9e62b
3 changed files with 69 additions and 53 deletions

View File

@@ -26,6 +26,7 @@ from pretix.base.models import Invoice, InvoiceAddress, InvoiceLine, Order
from pretix.base.services.async import TransactionAwareTask from pretix.base.services.async import TransactionAwareTask
from pretix.base.signals import register_payment_providers from pretix.base.signals import register_payment_providers
from pretix.celery import app from pretix.celery import app
from pretix.helpers.database import rolledback_transaction
@transaction.atomic @transaction.atomic
@@ -408,12 +409,10 @@ class DummyRollbackException(Exception):
def build_preview_invoice_pdf(event): def build_preview_invoice_pdf(event):
locale = event.settings.invoice_language locale = event.settings.invoice_language
pdf = None
if not locale or locale == '__user__': if not locale or locale == '__user__':
locale = event.settings.locale locale = event.settings.locale
try: with rolledback_transaction(), language(locale):
with transaction.atomic(), language(locale):
order = event.orders.create(status=Order.STATUS_PENDING, datetime=now(), order = event.orders.create(status=Order.STATUS_PENDING, datetime=now(),
expires=now(), code="PREVIEW", total=119) expires=now(), code="PREVIEW", total=119)
invoice = Invoice( invoice = Invoice(
@@ -444,9 +443,4 @@ def build_preview_invoice_pdf(event):
with tempfile.NamedTemporaryFile(suffix=".pdf") as f: with tempfile.NamedTemporaryFile(suffix=".pdf") as f:
_invoice_generate_german(invoice, f) _invoice_generate_german(invoice, f)
f.seek(0) f.seek(0)
pdf = f.read() return f.read()
raise DummyRollbackException()
except DummyRollbackException:
return pdf
else:
raise Exception('Invalid state, should have rolled back.')

View File

@@ -11,6 +11,7 @@ from pretix.base.models import (
) )
from pretix.base.signals import register_ticket_outputs from pretix.base.signals import register_ticket_outputs
from pretix.celery import app from pretix.celery import app
from pretix.helpers.database import rolledback_transaction
@app.task @app.task
@@ -41,10 +42,8 @@ class DummyRollbackException(Exception):
def preview(event: int, provider: str): def preview(event: int, provider: str):
event = Event.objects.get(id=event) event = Event.objects.get(id=event)
res = None
try: with rolledback_transaction(), language(event.settings.locale):
with transaction.atomic(), language(event.settings.locale):
item = event.items.create(name=_("Sample product"), default_price=42.23) item = event.items.create(name=_("Sample product"), default_price=42.23)
order = event.orders.create(status=Order.STATUS_PENDING, datetime=now(), order = event.orders.create(status=Order.STATUS_PENDING, datetime=now(),
@@ -56,9 +55,4 @@ def preview(event: int, provider: str):
for receiver, response in responses: for receiver, response in responses:
prov = response(event) prov = response(event)
if prov.identifier == provider: if prov.identifier == provider:
res = prov.generate(order) return prov.generate(order)
raise DummyRollbackException()
except DummyRollbackException:
return res
else:
raise Exception('Invalid state, should have rolled back.')

View File

@@ -0,0 +1,28 @@
import contextlib
from django.db import transaction
class DummyRollbackException(Exception):
pass
@contextlib.contextmanager
def rolledback_transaction():
"""
This context manager runs your code in a database transaction that will be rolled back in the end.
This can come in handy to simulate the effects of a database operation that you do not actually
want to perform.
Note that rollbacks are a very slow operation on most database backends. Also, long-running
transactions can slow down other operations currently running and you should not use this
in a place that is called frequently.
"""
try:
with transaction.atomic():
yield
raise DummyRollbackException()
except DummyRollbackException:
pass
else:
raise Exception('Invalid state, should have rolled back.')