Improved admin performance

This commit is contained in:
Marc Aymerich 2016-02-23 11:49:10 +00:00
parent e388346f9a
commit 201608c49c
9 changed files with 81 additions and 31 deletions

12
TODO.md
View file

@ -435,7 +435,7 @@ mkhomedir_helper or create ssh homes with bash.rc and such
# DOmain show implicit records # DOmain show implicit records
# if not database_ready(): schedule a retry in 60 seconds, otherwise resources and other dynamic content gets fucked, maybe attach some 'signal' when first query goes trough # if not database_ready(): schedule a retry in 60 seconds, otherwise resources and other dynamic content gets fucked, maybe attach some 'signal' when first query goes trough
with database_ready: with database_ready:
shit_happend, otherwise schedule for first query shit_happend, otherwise schedule for first query
# Entry.objects.filter()[:1].first() (LIMIT 1) # Entry.objects.filter()[:1].first() (LIMIT 1)
@ -443,24 +443,14 @@ mkhomedir_helper or create ssh homes with bash.rc and such
# put "Coordinate Apache restart" inside a bash function for clarity # put "Coordinate Apache restart" inside a bash function for clarity
# mailscanner phishing, spam, whitelist choices
# show base and total desglosed # show base and total desglosed
# Reverse lOgHistory order by date (lastest first) # Reverse lOgHistory order by date (lastest first)
* setuppostgres use porject_name for db name and user instead of orchestra * setuppostgres use porject_name for db name and user instead of orchestra
# POSTFIX web traffic monitor '": uid=" from=<%(user)s>' # POSTFIX web traffic monitor '": uid=" from=<%(user)s>'
# Automatically re-run backends until success? only timedout executions? # Automatically re-run backends until success? only timedout executions?
# TODO save serialized versions ob backendoperation.instance in order to allow backend reexecution of deleted objects # TODO save serialized versions ob backendoperation.instance in order to allow backend reexecution of deleted objects
# INDEXES for most used queries: account FK

View file

@ -64,10 +64,10 @@ class BillLineInline(admin.TabularInline):
kwargs['widget'] = forms.TextInput(attrs={'size':'50'}) kwargs['widget'] = forms.TextInput(attrs={'size':'50'})
elif db_field.name not in ('start_on', 'end_on'): elif db_field.name not in ('start_on', 'end_on'):
kwargs['widget'] = forms.TextInput(attrs={'size':'6'}) kwargs['widget'] = forms.TextInput(attrs={'size':'6'})
return super(BillLineInline, self).formfield_for_dbfield(db_field, **kwargs) return super().formfield_for_dbfield(db_field, **kwargs)
def get_queryset(self, request): def get_queryset(self, request):
qs = super(BillLineInline, self).get_queryset(request) qs = super().get_queryset(request)
return qs.prefetch_related('sublines').select_related('order') return qs.prefetch_related('sublines').select_related('order')
@ -141,7 +141,7 @@ class BillLineAdmin(admin.ModelAdmin):
display_total.admin_order_field = 'computed_total' display_total.admin_order_field = 'computed_total'
def get_queryset(self, request): def get_queryset(self, request):
qs = super(BillLineAdmin, self).get_queryset(request) qs = super().get_queryset(request)
qs = qs.annotate( qs = qs.annotate(
subline_total=Sum('sublines__total'), subline_total=Sum('sublines__total'),
computed_total=(F('subtotal') + Sum(Coalesce('sublines__total', 0))) * (1+F('tax')/100), computed_total=(F('subtotal') + Sum(Coalesce('sublines__total', 0))) * (1+F('tax')/100),
@ -151,7 +151,7 @@ class BillLineAdmin(admin.ModelAdmin):
class BillLineManagerAdmin(BillLineAdmin): class BillLineManagerAdmin(BillLineAdmin):
def get_queryset(self, request): def get_queryset(self, request):
qset = super(BillLineManagerAdmin, self).get_queryset(request) qset = super().get_queryset(request)
if self.bill_ids: if self.bill_ids:
return qset.filter(bill_id__in=self.bill_ids) return qset.filter(bill_id__in=self.bill_ids)
return qset return qset
@ -182,7 +182,7 @@ class BillLineManagerAdmin(BillLineAdmin):
'title': title, 'title': title,
} }
context.update(extra_context or {}) context.update(extra_context or {})
return super(BillLineManagerAdmin, self).changelist_view(request, context) return super().changelist_view(request, context)
class BillAdmin(AccountAdminMixin, ExtendedModelAdmin): class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
@ -288,7 +288,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
def get_urls(self): def get_urls(self):
""" Hook bill lines management URLs on bill admin """ """ Hook bill lines management URLs on bill admin """
urls = super(BillAdmin, self).get_urls() urls = super().get_urls()
admin_site = self.admin_site admin_site = self.admin_site
extra_urls = [ extra_urls = [
url("^manage-lines/$", url("^manage-lines/$",
@ -298,13 +298,13 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
return extra_urls + urls return extra_urls + urls
def get_readonly_fields(self, request, obj=None): def get_readonly_fields(self, request, obj=None):
fields = super(BillAdmin, self).get_readonly_fields(request, obj) fields = super().get_readonly_fields(request, obj)
if obj and not obj.is_open: if obj and not obj.is_open:
fields += self.add_fields fields += self.add_fields
return fields return fields
def get_fieldsets(self, request, obj=None): def get_fieldsets(self, request, obj=None):
fieldsets = super(BillAdmin, self).get_fieldsets(request, obj) fieldsets = super().get_fieldsets(request, obj)
if obj: if obj:
# Switches between amend_of_link and amend_links fields # Switches between amend_of_link and amend_links fields
if obj.amend_of_id: if obj.amend_of_id:
@ -316,7 +316,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
return fieldsets return fieldsets
def get_change_view_actions(self, obj=None): def get_change_view_actions(self, obj=None):
actions = super(BillAdmin, self).get_change_view_actions(obj) actions = super().get_change_view_actions(obj)
exclude = [] exclude = []
if obj: if obj:
if not obj.is_open: if not obj.is_open:
@ -326,7 +326,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
return [action for action in actions if action.__name__ not in exclude] return [action for action in actions if action.__name__ not in exclude]
def get_inline_instances(self, request, obj=None): def get_inline_instances(self, request, obj=None):
inlines = super(BillAdmin, self).get_inline_instances(request, obj) inlines = super().get_inline_instances(request, obj)
if obj and not obj.is_open: if obj and not obj.is_open:
return [inline for inline in inlines if type(inline) != BillLineInline] return [inline for inline in inlines if type(inline) != BillLineInline]
return [inline for inline in inlines if type(inline) != ClosedBillLineInline] return [inline for inline in inlines if type(inline) != ClosedBillLineInline]
@ -337,13 +337,13 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4}) kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4})
elif db_field.name == 'html': elif db_field.name == 'html':
kwargs['widget'] = forms.Textarea(attrs={'cols': 150, 'rows': 20}) kwargs['widget'] = forms.Textarea(attrs={'cols': 150, 'rows': 20})
formfield = super(BillAdmin, self).formfield_for_dbfield(db_field, **kwargs) formfield = super().formfield_for_dbfield(db_field, **kwargs)
if db_field.name == 'amend_of': if db_field.name == 'amend_of':
formfield.queryset = formfield.queryset.filter(is_open=False) formfield.queryset = formfield.queryset.filter(is_open=False)
return formfield return formfield
def get_queryset(self, request): def get_queryset(self, request):
qs = super(BillAdmin, self).get_queryset(request) qs = super().get_queryset(request)
qs = qs.annotate( qs = qs.annotate(
models.Count('lines'), models.Count('lines'),
# FIXME https://code.djangoproject.com/ticket/10060 # FIXME https://code.djangoproject.com/ticket/10060
@ -360,7 +360,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
# TODO raise404, here and everywhere # TODO raise404, here and everywhere
bill = self.get_object(request, unquote(object_id)) bill = self.get_object(request, unquote(object_id))
actions.validate_contact(request, bill, error=False) actions.validate_contact(request, bill, error=False)
return super(BillAdmin, self).change_view(request, object_id, **kwargs) return super().change_view(request, object_id, **kwargs)
admin.site.register(Bill, BillAdmin) admin.site.register(Bill, BillAdmin)
@ -384,7 +384,7 @@ class BillContactInline(admin.StackedInline):
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2}) kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
if db_field.name == 'email_usage': if db_field.name == 'email_usage':
kwargs['widget'] = paddingCheckboxSelectMultiple(45) kwargs['widget'] = paddingCheckboxSelectMultiple(45)
return super(BillContactInline, self).formfield_for_dbfield(db_field, **kwargs) return super().formfield_for_dbfield(db_field, **kwargs)
def has_bill_contact(account): def has_bill_contact(account):

View file

@ -404,7 +404,8 @@ class BillLine(models.Model):
start_on = models.DateField(_("start")) start_on = models.DateField(_("start"))
end_on = models.DateField(_("end"), null=True, blank=True) end_on = models.DateField(_("end"), null=True, blank=True)
order = models.ForeignKey(settings.BILLS_ORDER_MODEL, null=True, blank=True, order = models.ForeignKey(settings.BILLS_ORDER_MODEL, null=True, blank=True,
help_text=_("Informative link back to the order"), on_delete=models.SET_NULL) related_name='lines', on_delete=models.SET_NULL,
help_text=_("Informative link back to the order"))
order_billed_on = models.DateField(_("order billed"), null=True, blank=True) order_billed_on = models.DateField(_("order billed"), null=True, blank=True)
order_billed_until = models.DateField(_("order billed until"), null=True, blank=True) order_billed_until = models.DateField(_("order billed until"), null=True, blank=True)
created_on = models.DateField(_("created"), auto_now_add=True) created_on = models.DateField(_("created"), auto_now_add=True)

View file

@ -104,6 +104,9 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
fieldsets += ( fieldsets += (
(_("SOA"), { (_("SOA"), {
'classes': ('collapse',), 'classes': ('collapse',),
'description': _(
"SOA (Start of Authority) records are used to determine how your "
"zone propagates to the secondary nameservers."),
'fields': ('serial', 'refresh', 'retry', 'expire', 'min_ttl'), 'fields': ('serial', 'refresh', 'retry', 'expire', 'min_ttl'),
}), }),
) )

View file

@ -10,6 +10,7 @@ from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
from orchestra.contrib.accounts.filters import IsActiveListFilter from orchestra.contrib.accounts.filters import IsActiveListFilter
from . import settings
from .forms import ListCreationForm, ListChangeForm from .forms import ListCreationForm, ListChangeForm
from .models import List from .models import List
@ -39,6 +40,8 @@ class ListAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModel
}), }),
(_("Address"), { (_("Address"), {
'classes': ('wide',), 'classes': ('wide',),
'description': _("Additional address besides the default &lt;name&gt;@%s"
) % settings.LISTS_DEFAULT_DOMAIN,
'fields': (('address_name', 'address_domain'),) 'fields': (('address_name', 'address_domain'),)
}), }),
(_("Admin"), { (_("Admin"), {

View file

@ -49,6 +49,8 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
}), }),
(_("Custom filtering"), { (_("Custom filtering"), {
'classes': ('collapse',), 'classes': ('collapse',),
'description': _("Please remember to select <tt>custom filtering</tt> "
"if you want this filter to be applied."),
'fields': ('custom_filtering',), 'fields': ('custom_filtering',),
}), }),
(_("Addresses"), { (_("Addresses"), {

View file

@ -78,16 +78,16 @@ class Command(BaseCommand):
for instance in queryset: for instance in queryset:
manager.collect(instance, action, operations=operations, route_cache=route_cache) manager.collect(instance, action, operations=operations, route_cache=route_cache)
scripts, serialize = manager.generate(operations) scripts, serialize = manager.generate(operations)
servers = [] servers = set()
# Print scripts # Print scripts
for key, value in scripts.items(): for key, value in scripts.items():
route, __, __ = key route, __, __ = key
backend, operations = value backend, operations = value
servers.append(str(route.host)) servers.add(str(route.host))
self.stdout.write('# Execute %s on %s' % (backend.get_name(), route.host)) self.stdout.write('# Execute %s on %s' % (backend.get_name(), route.host))
for method, commands in backend.scripts: for method, commands in backend.scripts:
script = '\n'.join(commands) script = '\n'.join(commands)
self.stdout.write(script) self.stdout.write(script.encode('ascii', errors='replace').decode())
if interactive: if interactive:
context = { context = {
'servers': ', '.join(servers), 'servers': ', '.join(servers),

View file

@ -1,3 +1,4 @@
from django import forms
from django.contrib import admin from django.contrib import admin
from django.core.urlresolvers import reverse, NoReverseMatch from django.core.urlresolvers import reverse, NoReverseMatch
from django.db.models import Prefetch from django.db.models import Prefetch
@ -38,8 +39,9 @@ class MetricStorageInline(admin.TabularInline):
change_view = bool(self.parent_object and self.parent_object.pk) change_view = bool(self.parent_object and self.parent_object.pk)
if change_view: if change_view:
qs = qs.order_by('-id') qs = qs.order_by('-id')
parent_id = self.parent_object.pk
try: try:
tenth_id = qs.filter(order_id=self.parent_object.pk).values_list('id', flat=True)[9] tenth_id = qs.filter(order_id=parent_id).values_list('id', flat=True)[9]
except IndexError: except IndexError:
pass pass
else: else:
@ -72,7 +74,37 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
Prefetch('metrics', queryset=MetricStorage.objects.order_by('-id')), Prefetch('metrics', queryset=MetricStorage.objects.order_by('-id')),
) )
list_select_related = ('account', 'service') list_select_related = ('account', 'service')
readonly_fields = ('content_object_repr', 'content_object_link') add_fieldsets = (
(None, {
'fields': ('account', 'service')
}),
(_("Object"), {
'fields': ('content_type', 'object_id',),
}),
(_("State"), {
'fields': ('registered_on', 'cancelled_on', 'billed_on', 'billed_metric',
'billed_until' )
}),
(None, {
'fields': ('description', 'ignore',),
}),
)
fieldsets = (
(None, {
'fields': ('account_link', 'service_link', 'content_object_link'),
}),
(_("State"), {
'fields': ('registered_on', 'cancelled_on', 'billed_on', 'billed_metric',
'billed_until' )
}),
(None, {
'fields': ('description', 'ignore', 'bills_links'),
}),
)
readonly_fields = (
'content_object_repr', 'content_object_link', 'bills_links', 'account_link',
'service_link'
)
service_link = admin_link('service') service_link = admin_link('service')
display_registered_on = admin_date('registered_on') display_registered_on = admin_date('registered_on')
@ -99,6 +131,15 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
content_object_link.allow_tags = True content_object_link.allow_tags = True
content_object_link.admin_order_field = 'content_object_repr' content_object_link.admin_order_field = 'content_object_repr'
def bills_links(self, order):
bills = []
make_link = admin_link()
for line in order.lines.select_related('bill').distinct('bill'):
bills.append(make_link(line.bill))
return '<br'.join(bills)
bills_links.short_description = _("Bills")
bills_links.allow_tags = True
def display_billed_until(self, order): def display_billed_until(self, order):
billed_until = order.billed_until billed_until = order.billed_until
red = False red = False
@ -133,6 +174,12 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
return metric.value return metric.value
display_metric.short_description = _("Metric") display_metric.short_description = _("Metric")
def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """
if db_field.name == 'description':
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
return super().formfield_for_dbfield(db_field, **kwargs)
# def get_changelist(self, request, **kwargs): # def get_changelist(self, request, **kwargs):
# ChangeList = super(OrderAdmin, self).get_changelist(request, **kwargs) # ChangeList = super(OrderAdmin, self).get_changelist(request, **kwargs)
# class OrderFilterChangeList(ChangeList): # class OrderFilterChangeList(ChangeList):

View file

@ -54,6 +54,10 @@ class TransactionInline(admin.TabularInline):
def has_add_permission(self, *args, **kwargs): def has_add_permission(self, *args, **kwargs):
return False return False
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
return qs.select_related('source', 'bill')
class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (