Updated to DJango 1.8

This commit is contained in:
Marc Aymerich 2015-04-03 13:03:08 +00:00
parent 4a8a77715c
commit 751dda7126
9 changed files with 42 additions and 63 deletions

11
TODO.md
View File

@ -313,16 +313,15 @@ celery max-tasks-per-child
* webapp has_website list filter * 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 glic3rinu's django-fluent-dashboard
* gevent is not ported to python3 :'( * gevent is not ported to python3 :'(
* uwsgi 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? # 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 * implement delete All related services
* address name change does not remove old one :P

View File

@ -9,7 +9,7 @@ class UsedContentTypeFilter(SimpleListFilter):
def lookups(self, request, model_admin): def lookups(self, request, model_admin):
qset = model_admin.model._default_manager.all().order_by() qset = model_admin.model._default_manager.all().order_by()
result = () 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()),) result += ((str(pk), name.capitalize()),)
return result return result

View File

@ -165,9 +165,9 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
transactions = bill.transactions.all() transactions = bill.transactions.all()
if len(transactions) == 1: if len(transactions) == 1:
args = (transactions[0].pk,) 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: 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 url += '?bill=%i' % bill.pk
state = bill.get_payment_state_display().upper() state = bill.get_payment_state_display().upper()
color = PAYMENT_STATE_COLORS.get(bill.payment_state, 'grey') color = PAYMENT_STATE_COLORS.get(bill.payment_state, 'grey')

View File

@ -92,7 +92,7 @@ class ReadOnlySQLPasswordHashField(ReadOnlyPasswordHashField):
summary = mark_safe("<strong>%s</strong>" % _("No password set.")) summary = mark_safe("<strong>%s</strong>" % _("No password set."))
else: else:
size = len(value) size = len(value)
summary = value[:size/2] + '*'*(size-size/2) summary = value[:int(size/2)] + '*'*int(size-size/2)
summary = "<strong>hash</strong>: %s" % summary summary = "<strong>hash</strong>: %s" % summary
if value.startswith('*'): if value.startswith('*'):
summary = "<strong>algorithm</strong>: sha1_bin_hex %s" % summary summary = "<strong>algorithm</strong>: sha1_bin_hex %s" % summary

View File

@ -60,7 +60,7 @@ class DomainInline(admin.TabularInline):
class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin): class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'structured_name', 'display_is_top', 'websites', 'account_link' 'structured_name', 'display_is_top', 'display_websites', 'account_link'
) )
add_fields = ('name', 'account') add_fields = ('name', 'account')
fields = ('name', 'account_link') fields = ('name', 'account_link')
@ -85,7 +85,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
display_is_top.boolean = True display_is_top.boolean = True
display_is_top.admin_order_field = 'top' display_is_top.admin_order_field = 'top'
def websites(self, domain): def display_websites(self, domain):
if apps.isinstalled('orchestra.apps.websites'): if apps.isinstalled('orchestra.apps.websites'):
webs = domain.websites.all() webs = domain.websites.all()
if webs: if webs:
@ -95,9 +95,9 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
links.append('<a href="%s">%s</a>' % (url, web.name)) links.append('<a href="%s">%s</a>' % (url, web.name))
return '<br>'.join(links) return '<br>'.join(links)
return _("No website") return _("No website")
websites.admin_order_field = 'websites__name' display_websites.admin_order_field = 'websites__name'
websites.short_description = _("Websites") display_websites.short_description = _("Websites")
websites.allow_tags = True display_websites.allow_tags = True
def get_queryset(self, request): def get_queryset(self, request):
""" Order by structured name and imporve performance """ """ Order by structured name and imporve performance """

View File

@ -38,10 +38,7 @@ class OperationsMiddleware(object):
Stores all the operations derived from save and delete signals and executes them Stores all the operations derived from save and delete signals and executes them
at the end of the request/response cycle at the end of the request/response cycle
It also works as a transaction middleware. Each view function will be run It also works as a transaction middleware, making requets to run within an atomic block.
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.
""" """
# Thread local is used because request object is not available on model signals # Thread local is used because request object is not available on model signals
thread_locals = local() thread_locals = local()
@ -77,55 +74,38 @@ class OperationsMiddleware(object):
instance = kwargs.pop('instance') instance = kwargs.pop('instance')
manager.collect(instance, action, **kwargs) manager.collect(instance, action, **kwargs)
def commit_transaction(self): def enter_transaction_management(self):
if not transaction.get_autocommit(): type(self).thread_locals.transaction = transaction.atomic()
if transaction.is_dirty(): type(self).thread_locals.transaction.__enter__()
# Note: it is possible that the commit fails. If the reason is
# closed connection or some similar reason, then there is def leave_transaction_management(self, exception=None):
# little hope to proceed nicely. However, in some cases ( type(self).thread_locals.transaction.__exit__(exception, None, None)
# 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 process_request(self, request): def process_request(self, request):
""" Store request on a thread local variable """ """ Store request on a thread local variable """
type(self).thread_locals.request = request type(self).thread_locals.request = request
# Enters transaction management self.enter_transaction_management()
transaction.enter_transaction_management()
def process_exception(self, request, exception): def process_exception(self, request, exception):
"""Rolls back the database and leaves transaction management""" """Rolls back the database and leaves transaction management"""
if transaction.is_dirty(): self.leave_transaction_management(exception)
# 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()
def process_response(self, request, response): def process_response(self, request, response):
""" Processes pending backend operations """ """ Processes pending backend operations """
if not isinstance(response, HttpResponseServerError): if not isinstance(response, HttpResponseServerError):
operations = type(self).get_pending_operations() operations = type(self).get_pending_operations()
if 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 # We commit transaction just before executing operations
# because here is when IntegrityError show up # because here is when IntegrityError show up
self.commit_transaction() self.leave_transaction_management()
logs = manager.execute(scripts, block=block) logs = manager.execute(scripts, block=block)
if logs and resolve(request.path).app_name == 'admin': if logs and resolve(request.path).app_name == 'admin':
message_user(request, logs) message_user(request, logs)
return response return response
self.commit_transaction() self.leave_transaction_management()
return response return response

View File

@ -94,7 +94,7 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
filter_by_account_fields = ('bill', 'source') filter_by_account_fields = ('bill', 'source')
change_readonly_fields = ('amount', 'currency') change_readonly_fields = ('amount', 'currency')
readonly_fields = ('bill_link', 'display_state', 'process_link', 'account_link', 'source_link') 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') bill_link = admin_link('bill')
source_link = admin_link('source') source_link = admin_link('source')

View File

@ -135,7 +135,7 @@ function install_requirements () {
ca-certificates \ ca-certificates \
gettext" gettext"
PIP="django==1.7.7 \ PIP="django==1.8 \
django-celery-email==1.0.4 \ django-celery-email==1.0.4 \
django-fluent-dashboard==0.4 \ django-fluent-dashboard==0.4 \
https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip \ https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip \
@ -174,8 +174,8 @@ function install_requirements () {
freezegun \ freezegun \
coverage \ coverage \
orchestra-orm==dev \ orchestra-orm==dev \
django-debug-toolbar==1.2.1 \ django-debug-toolbar==1.3.0 \
django-nose==1.2 \ https://github.com/django-nose/django-nose/archive/master.zip \
sqlparse \ sqlparse \
pyinotify \ pyinotify \
--allow-external orchestra-orm --allow-unverified orchestra-orm" --allow-external orchestra-orm --allow-unverified orchestra-orm"

View File

@ -15,7 +15,7 @@ class MultiSelectField(models.CharField, metaclass=models.SubfieldBase):
'choices': self.choices 'choices': self.choices
} }
if self.has_default(): if self.has_default():
defaults['initial'] = eval(self.get_default()) defaults['initial'] = self.get_default()
defaults.update(kwargs) defaults.update(kwargs)
return MultiSelectFormField(**defaults) return MultiSelectFormField(**defaults)
@ -27,13 +27,13 @@ class MultiSelectField(models.CharField, metaclass=models.SubfieldBase):
def to_python(self, value): def to_python(self, value):
if value: if value:
if isinstance(value, list) and value[0].startswith('('): # if isinstance(value, tuple) and value[0].startswith('('):
# Workaround unknown bug on default model values # # Workaround unknown bug on default model values
# [u"('SUPPORT'", u" 'ADMIN'", u" 'BILLING'", u" 'TECH'", u" 'ADDS'", u" 'EMERGENCY')"] # # [u"('SUPPORT'", u" 'ADMIN'", u" 'BILLING'", u" 'TECH'", u" 'ADDS'", u" 'EMERGENCY')"]
value = list(eval(', '.join(value))) # value = list(eval(', '.join(value)))
if isinstance(value, list): if isinstance(value, str):
return value return value.split(',')
return value.split(',') return value
return [] return []
def contribute_to_class(self, cls, name): def contribute_to_class(self, cls, name):