forked from CGM_Public/pretix_original
Add a custom context manager for rollbacks (#282)
This commit is contained in:
@@ -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,45 +409,38 @@ 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(
|
order=order, event=event, invoice_no="PREVIEW",
|
||||||
order=order, event=event, invoice_no="PREVIEW",
|
date=date.today(), locale=locale
|
||||||
date=date.today(), locale=locale
|
)
|
||||||
)
|
invoice.invoice_from = event.settings.get('invoice_address_from')
|
||||||
invoice.invoice_from = event.settings.get('invoice_address_from')
|
|
||||||
|
|
||||||
introductory = event.settings.get('invoice_introductory_text', as_type=LazyI18nString)
|
introductory = event.settings.get('invoice_introductory_text', as_type=LazyI18nString)
|
||||||
additional = event.settings.get('invoice_additional_text', as_type=LazyI18nString)
|
additional = event.settings.get('invoice_additional_text', as_type=LazyI18nString)
|
||||||
footer = event.settings.get('invoice_footer_text', as_type=LazyI18nString)
|
footer = event.settings.get('invoice_footer_text', as_type=LazyI18nString)
|
||||||
payment = _("A payment provider specific text might appear here.")
|
payment = _("A payment provider specific text might appear here.")
|
||||||
|
|
||||||
invoice.introductory_text = str(introductory).replace('\n', '<br />')
|
invoice.introductory_text = str(introductory).replace('\n', '<br />')
|
||||||
invoice.additional_text = str(additional).replace('\n', '<br />')
|
invoice.additional_text = str(additional).replace('\n', '<br />')
|
||||||
invoice.footer_text = str(footer)
|
invoice.footer_text = str(footer)
|
||||||
invoice.payment_provider_text = str(payment).replace('\n', '<br />')
|
invoice.payment_provider_text = str(payment).replace('\n', '<br />')
|
||||||
invoice.invoice_to = _("John Doe\n214th Example Street\n012345 Somecity")
|
invoice.invoice_to = _("John Doe\n214th Example Street\n012345 Somecity")
|
||||||
invoice.file = None
|
invoice.file = None
|
||||||
invoice.save()
|
invoice.save()
|
||||||
invoice.lines.all().delete()
|
invoice.lines.all().delete()
|
||||||
|
|
||||||
InvoiceLine.objects.create(
|
InvoiceLine.objects.create(
|
||||||
invoice=invoice, description=_("Sample product A"),
|
invoice=invoice, description=_("Sample product A"),
|
||||||
gross_value=119, tax_value=19,
|
gross_value=119, tax_value=19,
|
||||||
tax_rate=19
|
tax_rate=19
|
||||||
)
|
)
|
||||||
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.')
|
|
||||||
|
|||||||
@@ -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,24 +42,17 @@ 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(),
|
||||||
expires=now(), code="PREVIEW1234", total=119)
|
expires=now(), code="PREVIEW1234", total=119)
|
||||||
|
|
||||||
order.positions.create(item=item, attendee_name=_("John Doe"), price=item.default_price)
|
order.positions.create(item=item, attendee_name=_("John Doe"), price=item.default_price)
|
||||||
|
|
||||||
responses = register_ticket_outputs.send(event)
|
responses = register_ticket_outputs.send(event)
|
||||||
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.')
|
|
||||||
|
|||||||
28
src/pretix/helpers/database.py
Normal file
28
src/pretix/helpers/database.py
Normal 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.')
|
||||||
Reference in New Issue
Block a user