diff --git a/TODO.md b/TODO.md
index d35791e1..48edb8ae 100644
--- a/TODO.md
+++ b/TODO.md
@@ -313,16 +313,15 @@ celery max-tasks-per-child
* webapp has_website list filter
-
-# FIXME account deletion generates a integrity error
-
-
-apt-get install python3 python3-pip
-cp /usr/local/lib/python2.7/dist-packages/orchestra.pth /usr/local/lib/python3.4/dist-packages/
glic3rinu's django-fluent-dashboard
* gevent is not ported to python3 :'(
* uwsgi python3
+https://github.com/django-nose/django-nose/archive/master.zip
+django_debug_toolbar-1.3.0-py2.py3-none-any.whl
+# FIXME account deletion generates a integrity error
# FIXME what to do when deleting accounts? set fk null and fill a username charfield? issues, invoices.. we whant all this to go away?
* implement delete All related services
+
+* address name change does not remove old one :P
diff --git a/orchestra/admin/filters.py b/orchestra/admin/filters.py
index e3c4bd34..27e4e276 100644
--- a/orchestra/admin/filters.py
+++ b/orchestra/admin/filters.py
@@ -9,7 +9,7 @@ class UsedContentTypeFilter(SimpleListFilter):
def lookups(self, request, model_admin):
qset = model_admin.model._default_manager.all().order_by()
result = ()
- for pk, name in qset.values_list('content_type', 'content_type__name').distinct():
+ for pk, name in qset.values_list('content_type', 'content_type__model').distinct():
result += ((str(pk), name.capitalize()),)
return result
diff --git a/orchestra/apps/bills/admin.py b/orchestra/apps/bills/admin.py
index 79be1ef8..020e665c 100644
--- a/orchestra/apps/bills/admin.py
+++ b/orchestra/apps/bills/admin.py
@@ -165,9 +165,9 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
transactions = bill.transactions.all()
if len(transactions) == 1:
args = (transactions[0].pk,)
- url = reverse('admin:%s_%s_change' % (t_opts.app_label, t_opts.module_name), args=args)
+ url = reverse('admin:%s_%s_change' % (t_opts.app_label, t_opts.model_name), args=args)
else:
- url = reverse('admin:%s_%s_changelist' % (t_opts.app_label, t_opts.module_name))
+ url = reverse('admin:%s_%s_changelist' % (t_opts.app_label, t_opts.model_name))
url += '?bill=%i' % bill.pk
state = bill.get_payment_state_display().upper()
color = PAYMENT_STATE_COLORS.get(bill.payment_state, 'grey')
diff --git a/orchestra/apps/databases/forms.py b/orchestra/apps/databases/forms.py
index b8717bf8..a3440e2d 100644
--- a/orchestra/apps/databases/forms.py
+++ b/orchestra/apps/databases/forms.py
@@ -92,7 +92,7 @@ class ReadOnlySQLPasswordHashField(ReadOnlyPasswordHashField):
summary = mark_safe("%s" % _("No password set."))
else:
size = len(value)
- summary = value[:size/2] + '*'*(size-size/2)
+ summary = value[:int(size/2)] + '*'*int(size-size/2)
summary = "hash: %s" % summary
if value.startswith('*'):
summary = "algorithm: sha1_bin_hex %s" % summary
diff --git a/orchestra/apps/domains/admin.py b/orchestra/apps/domains/admin.py
index e0ab1e01..0769ebb5 100644
--- a/orchestra/apps/domains/admin.py
+++ b/orchestra/apps/domains/admin.py
@@ -60,7 +60,7 @@ class DomainInline(admin.TabularInline):
class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
list_display = (
- 'structured_name', 'display_is_top', 'websites', 'account_link'
+ 'structured_name', 'display_is_top', 'display_websites', 'account_link'
)
add_fields = ('name', 'account')
fields = ('name', 'account_link')
@@ -85,7 +85,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
display_is_top.boolean = True
display_is_top.admin_order_field = 'top'
- def websites(self, domain):
+ def display_websites(self, domain):
if apps.isinstalled('orchestra.apps.websites'):
webs = domain.websites.all()
if webs:
@@ -95,9 +95,9 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
links.append('%s' % (url, web.name))
return '
'.join(links)
return _("No website")
- websites.admin_order_field = 'websites__name'
- websites.short_description = _("Websites")
- websites.allow_tags = True
+ display_websites.admin_order_field = 'websites__name'
+ display_websites.short_description = _("Websites")
+ display_websites.allow_tags = True
def get_queryset(self, request):
""" Order by structured name and imporve performance """
diff --git a/orchestra/apps/orchestration/middlewares.py b/orchestra/apps/orchestration/middlewares.py
index 062bee33..a9b08746 100644
--- a/orchestra/apps/orchestration/middlewares.py
+++ b/orchestra/apps/orchestration/middlewares.py
@@ -38,10 +38,7 @@ class OperationsMiddleware(object):
Stores all the operations derived from save and delete signals and executes them
at the end of the request/response cycle
- It also works as a transaction middleware. Each view function will be run
- with commit_on_response activated - that way a save() doesn't do a direct
- commit, the commit is done when a successful response is created. If an
- exception happens, the database is rolled back.
+ It also works as a transaction middleware, making requets to run within an atomic block.
"""
# Thread local is used because request object is not available on model signals
thread_locals = local()
@@ -77,55 +74,38 @@ class OperationsMiddleware(object):
instance = kwargs.pop('instance')
manager.collect(instance, action, **kwargs)
- def commit_transaction(self):
- if not transaction.get_autocommit():
- if transaction.is_dirty():
- # Note: it is possible that the commit fails. If the reason is
- # closed connection or some similar reason, then there is
- # little hope to proceed nicely. However, in some cases (
- # deferred foreign key checks for exampl) it is still possible
- # to rollback().
- try:
- transaction.commit()
- except Exception:
- # If the rollback fails, the transaction state will be
- # messed up. It doesn't matter, the connection will be set
- # to clean state after the request finishes. And, we can't
- # clean the state here properly even if we wanted to, the
- # connection is in transaction but we can't rollback...
- transaction.rollback()
- transaction.leave_transaction_management()
- raise
- transaction.leave_transaction_management()
+ def enter_transaction_management(self):
+ type(self).thread_locals.transaction = transaction.atomic()
+ type(self).thread_locals.transaction.__enter__()
+
+ def leave_transaction_management(self, exception=None):
+ type(self).thread_locals.transaction.__exit__(exception, None, None)
def process_request(self, request):
""" Store request on a thread local variable """
type(self).thread_locals.request = request
- # Enters transaction management
- transaction.enter_transaction_management()
+ self.enter_transaction_management()
def process_exception(self, request, exception):
"""Rolls back the database and leaves transaction management"""
- if transaction.is_dirty():
- # This rollback might fail because of network failure for example.
- # If rollback isn't possible it is impossible to clean the
- # connection's state. So leave the connection in dirty state and
- # let request_finished signal deal with cleaning the connection.
- transaction.rollback()
- transaction.leave_transaction_management()
+ self.leave_transaction_management(exception)
def process_response(self, request, response):
""" Processes pending backend operations """
if not isinstance(response, HttpResponseServerError):
operations = type(self).get_pending_operations()
if operations:
- scripts, block = manager.generate(operations)
+ try:
+ scripts, block = manager.generate(operations)
+ except Exception as exception:
+ self.leave_transaction_management(exception)
+ raise
# We commit transaction just before executing operations
# because here is when IntegrityError show up
- self.commit_transaction()
+ self.leave_transaction_management()
logs = manager.execute(scripts, block=block)
if logs and resolve(request.path).app_name == 'admin':
message_user(request, logs)
return response
- self.commit_transaction()
+ self.leave_transaction_management()
return response
diff --git a/orchestra/apps/payments/admin.py b/orchestra/apps/payments/admin.py
index 4496eb85..701cff41 100644
--- a/orchestra/apps/payments/admin.py
+++ b/orchestra/apps/payments/admin.py
@@ -94,7 +94,7 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
filter_by_account_fields = ('bill', 'source')
change_readonly_fields = ('amount', 'currency')
readonly_fields = ('bill_link', 'display_state', 'process_link', 'account_link', 'source_link')
- list_select_related = ('account', 'source', 'bill__account')
+ list_select_related = ('source', 'bill__account')
bill_link = admin_link('bill')
source_link = admin_link('source')
diff --git a/orchestra/bin/orchestra-admin b/orchestra/bin/orchestra-admin
index 889595e3..dd0076c4 100755
--- a/orchestra/bin/orchestra-admin
+++ b/orchestra/bin/orchestra-admin
@@ -135,7 +135,7 @@ function install_requirements () {
ca-certificates \
gettext"
- PIP="django==1.7.7 \
+ PIP="django==1.8 \
django-celery-email==1.0.4 \
django-fluent-dashboard==0.4 \
https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip \
@@ -174,8 +174,8 @@ function install_requirements () {
freezegun \
coverage \
orchestra-orm==dev \
- django-debug-toolbar==1.2.1 \
- django-nose==1.2 \
+ django-debug-toolbar==1.3.0 \
+ https://github.com/django-nose/django-nose/archive/master.zip \
sqlparse \
pyinotify \
--allow-external orchestra-orm --allow-unverified orchestra-orm"
diff --git a/orchestra/models/fields.py b/orchestra/models/fields.py
index fb1992ec..0a00d4fb 100644
--- a/orchestra/models/fields.py
+++ b/orchestra/models/fields.py
@@ -15,7 +15,7 @@ class MultiSelectField(models.CharField, metaclass=models.SubfieldBase):
'choices': self.choices
}
if self.has_default():
- defaults['initial'] = eval(self.get_default())
+ defaults['initial'] = self.get_default()
defaults.update(kwargs)
return MultiSelectFormField(**defaults)
@@ -27,13 +27,13 @@ class MultiSelectField(models.CharField, metaclass=models.SubfieldBase):
def to_python(self, value):
if value:
- if isinstance(value, list) and value[0].startswith('('):
- # Workaround unknown bug on default model values
- # [u"('SUPPORT'", u" 'ADMIN'", u" 'BILLING'", u" 'TECH'", u" 'ADDS'", u" 'EMERGENCY')"]
- value = list(eval(', '.join(value)))
- if isinstance(value, list):
- return value
- return value.split(',')
+# if isinstance(value, tuple) and value[0].startswith('('):
+# # Workaround unknown bug on default model values
+# # [u"('SUPPORT'", u" 'ADMIN'", u" 'BILLING'", u" 'TECH'", u" 'ADDS'", u" 'EMERGENCY')"]
+# value = list(eval(', '.join(value)))
+ if isinstance(value, str):
+ return value.split(',')
+ return value
return []
def contribute_to_class(self, cls, name):