diff --git a/TODO.md b/TODO.md
index ea5a5e24..7ad9bd01 100644
--- a/TODO.md
+++ b/TODO.md
@@ -461,6 +461,6 @@ mkhomedir_helper or create ssh homes with bash.rc and such
# TODO save serialized versions ob backendoperation.instance in order to allow backend reexecution of deleted objects
-
+# INDEXES for most used queries: account FK
diff --git a/orchestra/admin/utils.py b/orchestra/admin/utils.py
index 7b23fa03..a48e94c1 100644
--- a/orchestra/admin/utils.py
+++ b/orchestra/admin/utils.py
@@ -112,7 +112,7 @@ def admin_link(*args, **kwargs):
return '---'
display = kwargs.get('display')
if display:
- display = getattr(obj, display, None)
+ display = getattr(obj, display, display)
else:
display = obj
try:
@@ -123,7 +123,8 @@ def admin_link(*args, **kwargs):
extra = ''
if kwargs['popup']:
extra = 'onclick="return showAddAnotherPopup(this);"'
- return mark_safe('%s' % (url, extra, display))
+ title = "Change %s" % obj._meta.verbose_name
+ return mark_safe('%s' % (url, title, extra, display))
@admin_field
diff --git a/orchestra/contrib/accounts/admin.py b/orchestra/contrib/accounts/admin.py
index db58194b..aa20c8ba 100644
--- a/orchestra/contrib/accounts/admin.py
+++ b/orchestra/contrib/accounts/admin.py
@@ -16,7 +16,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import SendEmail
-from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query, change_url
+from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query
from orchestra.core import services, accounts
from orchestra.forms import UserChangeForm
@@ -189,8 +189,7 @@ class AccountAdminMixin(object):
def account_link(self, instance):
account = instance.account if instance.pk else self.account
- url = change_url(account)
- return '%s' % (url, account)
+ return admin_link()(account)
account_link.short_description = _("account")
account_link.allow_tags = True
account_link.admin_order_field = 'account__username'
diff --git a/orchestra/contrib/domains/admin.py b/orchestra/contrib/domains/admin.py
index e2beb8bf..09d61c57 100644
--- a/orchestra/contrib/domains/admin.py
+++ b/orchestra/contrib/domains/admin.py
@@ -85,7 +85,9 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
for website in websites:
site_link = get_on_site_link(website.get_absolute_url())
admin_url = change_url(website)
- link = '%s %s' % (admin_url, website.name, site_link)
+ title = _("Edit website")
+ link = '%s %s' % (
+ admin_url, title, website.name, site_link)
links.append(link)
return '
'.join(links)
site_link = get_on_site_link('http://%s' % domain.name)
@@ -127,7 +129,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
structured_name=Concat('top__name', 'name')
).order_by('-structured_id', 'structured_name')
if apps.isinstalled('orchestra.contrib.websites'):
- qs = qs.prefetch_related('websites')
+ qs = qs.prefetch_related('websites__domains')
return qs
def save_model(self, request, obj, form, change):
diff --git a/orchestra/contrib/domains/migrations/0005_auto_20160219_1034.py b/orchestra/contrib/domains/migrations/0005_auto_20160219_1034.py
new file mode 100644
index 00000000..3d8a3c5c
--- /dev/null
+++ b/orchestra/contrib/domains/migrations/0005_auto_20160219_1034.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import orchestra.contrib.domains.validators
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('domains', '0004_auto_20150720_1121'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='domain',
+ name='name',
+ field=models.CharField(max_length=256, validators=[orchestra.contrib.domains.validators.validate_domain_name, orchestra.contrib.domains.validators.validate_allowed_domain], db_index=True, verbose_name='name', unique=True, help_text='Domain or subdomain name.'),
+ ),
+ migrations.AlterField(
+ model_name='domain',
+ name='top',
+ field=models.ForeignKey(editable=False, verbose_name='top domain', related_name='subdomain_set', to='domains.Domain', null=True),
+ ),
+ migrations.AlterField(
+ model_name='record',
+ name='type',
+ field=models.CharField(max_length=32, verbose_name='type', choices=[('MX', 'MX'), ('NS', 'NS'), ('CNAME', 'CNAME'), ('A', 'A (IPv4 address)'), ('AAAA', 'AAAA (IPv6 address)'), ('SRV', 'SRV'), ('TXT', 'TXT'), ('SPF', 'SPF'), ('SOA', 'SOA')]),
+ ),
+ ]
diff --git a/orchestra/contrib/domains/models.py b/orchestra/contrib/domains/models.py
index 1cd893a5..05aac373 100644
--- a/orchestra/contrib/domains/models.py
+++ b/orchestra/contrib/domains/models.py
@@ -24,7 +24,7 @@ class DomainQuerySet(models.QuerySet):
class Domain(models.Model):
- name = models.CharField(_("name"), max_length=256, unique=True,
+ name = models.CharField(_("name"), max_length=256, unique=True, db_index=True,
help_text=_("Domain or subdomain name."),
validators=[
validators.validate_domain_name,
diff --git a/orchestra/contrib/mailboxes/migrations/0002_auto_20160219_1032.py b/orchestra/contrib/mailboxes/migrations/0002_auto_20160219_1032.py
new file mode 100644
index 00000000..99b89e53
--- /dev/null
+++ b/orchestra/contrib/mailboxes/migrations/0002_auto_20160219_1032.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.core.validators
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('mailboxes', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='mailbox',
+ name='filtering',
+ field=models.CharField(max_length=16, choices=[('CUSTOM', 'Custom filtering'), ('DISABLE', 'Disable'), ('REDIRECT', 'Archive spam (Score≥8)'), ('REDIRECT5', 'Archive spam (Score≥5)'), ('REJECT', 'Reject spam (Score≥8)'), ('REJECT5', 'Reject spam (Score≥5)')], default='REDIRECT'),
+ ),
+ migrations.AlterField(
+ model_name='mailbox',
+ name='name',
+ field=models.CharField(max_length=64, db_index=True, unique=True, help_text='Required. 32 characters or fewer. Letters, digits and ./-/_ only.', validators=[django.core.validators.RegexValidator('^[\\w.-]+$', 'Enter a valid mailbox name.')], verbose_name='name'),
+ ),
+ ]
diff --git a/orchestra/contrib/mailboxes/models.py b/orchestra/contrib/mailboxes/models.py
index 5c9694fb..aae48ef4 100644
--- a/orchestra/contrib/mailboxes/models.py
+++ b/orchestra/contrib/mailboxes/models.py
@@ -14,7 +14,7 @@ from . import validators, settings
class Mailbox(models.Model):
CUSTOM = 'CUSTOM'
- name = models.CharField(_("name"), max_length=64, unique=True,
+ name = models.CharField(_("name"), max_length=64, unique=True, db_index=True,
help_text=_("Required. %s characters or fewer. Letters, digits and ./-/_ only.") %
settings.MAILBOXES_NAME_MAX_LENGTH,
validators=[
diff --git a/orchestra/contrib/mailer/migrations/0005_auto_20160219_1056.py b/orchestra/contrib/mailer/migrations/0005_auto_20160219_1056.py
new file mode 100644
index 00000000..8b3eca4b
--- /dev/null
+++ b/orchestra/contrib/mailer/migrations/0005_auto_20160219_1056.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('mailer', '0004_auto_20150805_1328'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='message',
+ name='last_try',
+ field=models.DateTimeField(null=True, db_index=True, verbose_name='last try'),
+ ),
+ migrations.AlterField(
+ model_name='message',
+ name='priority',
+ field=models.PositiveIntegerField(default=2, choices=[(0, 'Critical (not queued)'), (1, 'High'), (2, 'Normal'), (3, 'Low')], db_index=True, verbose_name='Priority'),
+ ),
+ migrations.AlterField(
+ model_name='message',
+ name='retries',
+ field=models.PositiveIntegerField(default=0, db_index=True, verbose_name='retries'),
+ ),
+ migrations.AlterField(
+ model_name='message',
+ name='state',
+ field=models.CharField(default='QUEUED', choices=[('QUEUED', 'Queued'), ('SENT', 'Sent'), ('DEFERRED', 'Deferred'), ('FAILED', 'Failed')], db_index=True, max_length=16, verbose_name='State'),
+ ),
+ ]
diff --git a/orchestra/contrib/mailer/models.py b/orchestra/contrib/mailer/models.py
index 519ecfa5..5b43e756 100644
--- a/orchestra/contrib/mailer/models.py
+++ b/orchestra/contrib/mailer/models.py
@@ -27,15 +27,17 @@ class Message(models.Model):
(LOW, _("Low")),
)
- state = models.CharField(_("State"), max_length=16, choices=STATES, default=QUEUED)
- priority = models.PositiveIntegerField(_("Priority"), choices=PRIORITIES, default=NORMAL)
+ state = models.CharField(_("State"), max_length=16, choices=STATES, default=QUEUED,
+ db_index=True)
+ priority = models.PositiveIntegerField(_("Priority"), choices=PRIORITIES, default=NORMAL,
+ db_index=True)
to_address = models.CharField(max_length=256)
from_address = models.CharField(max_length=256)
subject = models.TextField(_("subject"))
content = models.TextField(_("content"))
created_at = models.DateTimeField(_("created at"), auto_now_add=True)
- retries = models.PositiveIntegerField(_("retries"), default=0)
- last_try = models.DateTimeField(_("last try"), null=True)
+ retries = models.PositiveIntegerField(_("retries"), default=0, db_index=True)
+ last_try = models.DateTimeField(_("last try"), null=True, db_index=True)
def __str__(self):
return '%s to %s' % (self.subject, self.to_address)
diff --git a/orchestra/contrib/orchestration/migrations/0006_auto_20160219_1110.py b/orchestra/contrib/orchestration/migrations/0006_auto_20160219_1110.py
new file mode 100644
index 00000000..4a9045ca
--- /dev/null
+++ b/orchestra/contrib/orchestration/migrations/0006_auto_20160219_1110.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import orchestra.core.validators
+import orchestra.models.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('orchestration', '0005_auto_20150709_1016'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='route',
+ name='backend',
+ field=models.CharField(choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('OpenVZTraffic', '[M] OpenVZTraffic'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic'), ('Apache2Backend', '[S] Apache 2'), ('BSCWBackend', '[S] BSCW SaaS'), ('Bind9MasterDomainBackend', '[S] Bind9 master domain'), ('Bind9SlaveDomainBackend', '[S] Bind9 slave domain'), ('DokuWikiMuBackend', '[S] DokuWiki multisite'), ('DrupalMuBackend', '[S] Drupal multisite'), ('GitLabSaaSBackend', '[S] GitLab SaaS'), ('AutoresponseBackend', '[S] Mail autoresponse'), ('MailScannerSpamRuleBackend', '[S] MailScanner ruleset'), ('MailmanBackend', '[S] Mailman'), ('MailmanVirtualDomainBackend', '[S] Mailman virtdomain-only'), ('MoodleBackend', '[S] Moodle'), ('MoodleWWWRootBackend', '[S] Moodle WWWRoot (required)'), ('MoodleMuBackend', '[S] Moodle multisite'), ('MySQLBackend', '[S] MySQL database'), ('MySQLUserBackend', '[S] MySQL user'), ('PHPBackend', '[S] PHP FPM/FCGID'), ('PangeaProxmoxOVZ', '[S] PangeaProxmoxOVZ'), ('PostfixAddressBackend', '[S] Postfix address'), ('PostfixAddressVirtualDomainBackend', '[S] Postfix address virtdomain-only'), ('PostfixRecipientAccessBackend', '[S] Postfix recipient access'), ('ProxmoxOVZ', '[S] ProxmoxOVZ'), ('uWSGIPythonBackend', '[S] Python uWSGI'), ('StaticBackend', '[S] Static'), ('SymbolicLinkBackend', '[S] Symbolic link webapp'), ('SyncBind9MasterDomainBackend', '[S] Sync Bind9 master domain'), ('SyncBind9SlaveDomainBackend', '[S] Sync Bind9 slave domain'), ('UNIXUserMaildirBackend', '[S] UNIX maildir user'), ('UNIXUserBackend', '[S] UNIX user'), ('WebalizerAppBackend', '[S] Webalizer App'), ('WebalizerBackend', '[S] Webalizer Content'), ('WordPressURLBackend', '[S] WordPress URL'), ('WordPressBackend', '[S] Wordpress'), ('WordpressMuBackend', '[S] Wordpress multisite'), ('OwnCloudBackend', '[S] ownCloud SaaS'), ('PhpListSaaSBackend', '[S] phpList SaaS')], max_length=256, verbose_name='backend'),
+ ),
+ migrations.AlterField(
+ model_name='server',
+ name='address',
+ field=orchestra.models.fields.NullableCharField(help_text='Optional IP address or domain name. If blank, name field will be used for address resolution.
If the IP address never changes you can set this field and save DNS requests.', verbose_name='address', validators=[orchestra.core.validators.OrValidator(orchestra.core.validators.validate_ip_address, orchestra.core.validators.validate_hostname)], blank=True, max_length=256, unique=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='server',
+ name='name',
+ field=models.CharField(help_text='Verbose name or hostname of this server.', max_length=256, verbose_name='name', unique=True),
+ ),
+ migrations.AlterIndexTogether(
+ name='backendoperation',
+ index_together=set([('content_type', 'object_id')]),
+ ),
+ ]
diff --git a/orchestra/contrib/orchestration/models.py b/orchestra/contrib/orchestration/models.py
index c99cbd52..887db725 100644
--- a/orchestra/contrib/orchestration/models.py
+++ b/orchestra/contrib/orchestration/models.py
@@ -148,6 +148,9 @@ class BackendOperation(models.Model):
class Meta:
verbose_name = _("Operation")
verbose_name_plural = _("Operations")
+ index_together = (
+ ('content_type', 'object_id'),
+ )
def __str__(self):
return '%s.%s(%s)' % (self.backend, self.action, self.instance or self.instance_repr)
diff --git a/orchestra/contrib/orders/migrations/0005_auto_20160219_1107.py b/orchestra/contrib/orders/migrations/0005_auto_20160219_1107.py
new file mode 100644
index 00000000..da9de840
--- /dev/null
+++ b/orchestra/contrib/orders/migrations/0005_auto_20160219_1107.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('orders', '0004_auto_20150729_0945'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='order',
+ name='billed_metric',
+ field=models.DecimalField(decimal_places=2, null=True, verbose_name='billed metric', blank=True, max_digits=16),
+ ),
+ migrations.AlterField(
+ model_name='order',
+ name='content_object_repr',
+ field=models.CharField(max_length=256, verbose_name='content object representation', help_text='Used for searches.', editable=False),
+ ),
+ migrations.AlterIndexTogether(
+ name='order',
+ index_together=set([('content_type', 'object_id')]),
+ ),
+ ]
diff --git a/orchestra/contrib/orders/models.py b/orchestra/contrib/orders/models.py
index cb2614b6..d2c42613 100644
--- a/orchestra/contrib/orders/models.py
+++ b/orchestra/contrib/orders/models.py
@@ -172,6 +172,9 @@ class Order(models.Model):
class Meta:
get_latest_by = 'id'
+ index_together = (
+ ('content_type', 'object_id'),
+ )
def __str__(self):
return str(self.service)
diff --git a/orchestra/contrib/payments/admin.py b/orchestra/contrib/payments/admin.py
index 07231a67..14f06d47 100644
--- a/orchestra/contrib/payments/admin.py
+++ b/orchestra/contrib/payments/admin.py
@@ -134,6 +134,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
list_display = ('id', 'file_url', 'display_transactions', 'created_at')
fields = ('data', 'file_url', 'created_at')
readonly_fields = ('data', 'file_url', 'display_transactions', 'created_at')
+ list_prefetch_related = ('transactions',)
inlines = [TransactionInline]
change_view_actions = (
actions.mark_process_as_executed, actions.abort, actions.commit, actions.report
@@ -150,8 +151,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
ids = []
lines = []
counter = 0
- # Because of values_list this query doesn't benefit from prefetch_related
- for trans in process.transactions.only('id', 'state'):
+ for trans in process.transactions.all():
color = STATE_COLORS.get(trans.state, 'black')
state = trans.get_state_display()
ids.append('%i' % (color, state, trans.id))
@@ -163,7 +163,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
lines.append(','.join(ids))
transactions = '
%s' % (url, transactions)
display_transactions.short_description = _("Transactions")
display_transactions.allow_tags = True
@@ -172,7 +172,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
return False
def get_change_view_actions(self, obj=None):
- actions = super(TransactionProcessAdmin, self).get_change_view_actions()
+ actions = super().get_change_view_actions()
exclude = []
if obj:
if obj.state == TransactionProcess.EXECUTED:
@@ -186,13 +186,11 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
def delete_view(self, request, object_id, extra_context=None):
queryset = self.model.objects.filter(id=object_id)
related_transactions = helpers.pre_delete_processes(self, request, queryset)
- response = super(TransactionProcessAdmin, self).delete_view(
- request, object_id, extra_context)
+ response = super().delete_view(request, object_id, extra_context)
if isinstance(response, HttpResponseRedirect):
helpers.post_delete_processes(self, request, related_transactions)
return response
-
admin.site.register(PaymentSource, PaymentSourceAdmin)
admin.site.register(Transaction, TransactionAdmin)
admin.site.register(TransactionProcess, TransactionProcessAdmin)
diff --git a/orchestra/contrib/resources/migrations/0010_auto_20160219_1108.py b/orchestra/contrib/resources/migrations/0010_auto_20160219_1108.py
new file mode 100644
index 00000000..9d709ea0
--- /dev/null
+++ b/orchestra/contrib/resources/migrations/0010_auto_20160219_1108.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import orchestra.models.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('resources', '0009_auto_20150804_1450'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='monitordata',
+ name='monitor',
+ field=models.CharField(db_index=True, choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('OpenVZTraffic', '[M] OpenVZTraffic'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic')], verbose_name='monitor', max_length=256),
+ ),
+ migrations.AlterField(
+ model_name='monitordata',
+ name='object_id',
+ field=models.PositiveIntegerField(verbose_name='object id'),
+ ),
+ migrations.AlterField(
+ model_name='resource',
+ name='disable_trigger',
+ field=models.BooleanField(help_text='Disables monitors exeeded and recovery triggers', verbose_name='disable trigger', default=True),
+ ),
+ migrations.AlterField(
+ model_name='resource',
+ name='monitors',
+ field=orchestra.models.fields.MultiSelectField(help_text='Monitor backends used for monitoring this resource.', blank=True, verbose_name='monitors', max_length=256, choices=[('Apache2Traffic', '[M] Apache 2 Traffic'), ('ApacheTrafficByName', '[M] ApacheTrafficByName'), ('DokuWikiMuTraffic', '[M] DokuWiki MU Traffic'), ('DovecotMaildirDisk', '[M] Dovecot Maildir size'), ('Exim4Traffic', '[M] Exim4 traffic'), ('MailmanSubscribers', '[M] Mailman subscribers'), ('MailmanTraffic', '[M] Mailman traffic'), ('MysqlDisk', '[M] MySQL disk'), ('OpenVZTraffic', '[M] OpenVZTraffic'), ('PostfixMailscannerTraffic', '[M] Postfix-Mailscanner traffic'), ('UNIXUserDisk', '[M] UNIX user disk'), ('VsFTPdTraffic', '[M] VsFTPd traffic'), ('WordpressMuTraffic', '[M] Wordpress MU Traffic'), ('OwnCloudDiskQuota', '[M] ownCloud SaaS Disk Quota'), ('OwncloudTraffic', '[M] ownCloud SaaS Traffic'), ('PhpListTraffic', '[M] phpList SaaS Traffic')]),
+ ),
+ migrations.AlterField(
+ model_name='resourcedata',
+ name='object_id',
+ field=models.PositiveIntegerField(verbose_name='object id'),
+ ),
+ migrations.AlterIndexTogether(
+ name='monitordata',
+ index_together=set([('content_type', 'object_id')]),
+ ),
+ migrations.AlterIndexTogether(
+ name='resourcedata',
+ index_together=set([('content_type', 'object_id')]),
+ ),
+ ]
diff --git a/orchestra/contrib/resources/models.py b/orchestra/contrib/resources/models.py
index c68ac2c1..d2767ccc 100644
--- a/orchestra/contrib/resources/models.py
+++ b/orchestra/contrib/resources/models.py
@@ -179,8 +179,8 @@ class ResourceDataQuerySet(models.QuerySet):
class ResourceData(models.Model):
""" Stores computed resource usage and allocation """
resource = models.ForeignKey(Resource, related_name='dataset', verbose_name=_("resource"))
- content_type = models.ForeignKey(ContentType, verbose_name=_("content type"), db_index=True)
- object_id = models.PositiveIntegerField(_("object id"), db_index=True)
+ content_type = models.ForeignKey(ContentType, verbose_name=_("content type"))
+ object_id = models.PositiveIntegerField(_("object id"))
used = models.DecimalField(_("used"), max_digits=16, decimal_places=3, null=True,
editable=False)
updated_at = models.DateTimeField(_("updated"), null=True, editable=False)
@@ -194,6 +194,9 @@ class ResourceData(models.Model):
class Meta:
unique_together = ('resource', 'content_type', 'object_id')
verbose_name_plural = _("resource data")
+ index_together = (
+ ('content_type', 'object_id'),
+ )
def __str__(self):
return "%s: %s" % (self.resource, self.content_object)
@@ -264,8 +267,8 @@ class MonitorData(models.Model):
""" Stores monitored data """
monitor = models.CharField(_("monitor"), max_length=256, db_index=True,
choices=ServiceMonitor.get_choices())
- content_type = models.ForeignKey(ContentType, verbose_name=_("content type"), db_index=True)
- object_id = models.PositiveIntegerField(_("object id"), db_index=True)
+ content_type = models.ForeignKey(ContentType, verbose_name=_("content type"))
+ object_id = models.PositiveIntegerField(_("object id"))
created_at = models.DateTimeField(_("created"), default=timezone.now, db_index=True)
value = models.DecimalField(_("value"), max_digits=16, decimal_places=2)
state = models.DecimalField(_("state"), max_digits=16, decimal_places=2, null=True,
@@ -279,6 +282,9 @@ class MonitorData(models.Model):
class Meta:
get_latest_by = 'id'
verbose_name_plural = _("monitor data")
+ index_together = (
+ ('content_type', 'object_id'),
+ )
def __str__(self):
return str(self.monitor)
diff --git a/orchestra/contrib/webapps/admin.py b/orchestra/contrib/webapps/admin.py
index 40966a16..b3eaafe6 100644
--- a/orchestra/contrib/webapps/admin.py
+++ b/orchestra/contrib/webapps/admin.py
@@ -5,7 +5,7 @@ from django.utils.encoding import force_text
from django.utils.translation import ugettext, ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
-from orchestra.admin.utils import change_url, get_modeladmin
+from orchestra.admin.utils import admin_link, get_modeladmin
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin
from orchestra.forms.widgets import DynamicHelpTextSelect
@@ -72,9 +72,8 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
site_url = content.get_absolute_url()
site_link = get_on_site_link(site_url)
website = content.website
- admin_url = change_url(website)
- name = "%s on %s" % (website.name, content.path)
- link = '%s %s' % (admin_url, name, site_link)
+ name = "%s on %s %s" % (website.name, content.path, site_link)
+ link = admin_link(display=name)(website)
websites.append(link)
if not websites:
add_url = reverse('admin:websites_website_add')
diff --git a/orchestra/contrib/websites/admin.py b/orchestra/contrib/websites/admin.py
index 6bb2f246..de51de49 100644
--- a/orchestra/contrib/websites/admin.py
+++ b/orchestra/contrib/websites/admin.py
@@ -87,7 +87,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
for content in website.content_set.all():
site_link = get_on_site_link(content.get_absolute_url())
webapp = content.webapp
- detail = webapp.get_type_display()
+ detail = _("Edit Webapp") + ' ' + webapp.get_type_display()
try:
detail += ' ' + webapp.type_instance.get_detail()
except KeyError:
diff --git a/orchestra/contrib/websites/backends/apache.py b/orchestra/contrib/websites/backends/apache.py
index 9ea4f8fa..6214fd68 100644
--- a/orchestra/contrib/websites/backends/apache.py
+++ b/orchestra/contrib/websites/backends/apache.py
@@ -55,15 +55,18 @@ class Apache2Backend(ServiceController):
return '\n'.join([conf for location, conf in extra_conf])
def render_virtual_host(self, site, context, ssl=False):
- context['port'] = self.HTTPS_PORT if ssl else self.HTTP_PORT
- context['vhost_set_fcgid'] = False
+ context.update({
+ 'port': self.HTTPS_PORT if ssl else self.HTTP_PORT,
+ 'vhost_set_fcgid': False,
+ 'server_alias_lines': ' \\\n '.join(context['server_alias'])
+ })
context['extra_conf'] = self.get_extra_conf(site, context, ssl)
return Template(textwrap.dedent("""\
IncludeOptional /etc/apache2/site[s]-override/{{ site_unique_name }}.con[f]
ServerName {{ server_name }}\
{% if server_alias %}
- ServerAlias {{ server_alias|join:' ' }}{% endif %}\
+ ServerAlias {{ server_alias_lines }}{% endif %}\
{% if access_log %}
CustomLog {{ access_log }} common{% endif %}\
{% if error_log %}
diff --git a/orchestra/contrib/websites/migrations/0002_auto_20160219_1036.py b/orchestra/contrib/websites/migrations/0002_auto_20160219_1036.py
new file mode 100644
index 00000000..7b9af1e8
--- /dev/null
+++ b/orchestra/contrib/websites/migrations/0002_auto_20160219_1036.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('websites', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='website',
+ name='domains',
+ field=models.ManyToManyField(to='domains.Domain', related_name='websites', verbose_name='domains', blank=True),
+ ),
+ migrations.AlterField(
+ model_name='websitedirective',
+ name='name',
+ field=models.CharField(choices=[(None, '-------'), ('HTTPD', [('redirect', 'Redirection'), ('proxy', 'Proxy'), ('error-document', 'ErrorDocumentRoot')]), ('SaaS', [('wordpress-saas', 'WordPress SaaS'), ('dokuwiki-saas', 'DokuWiki SaaS'), ('drupal-saas', 'Drupdal SaaS'), ('moodle-saas', 'Moodle SaaS')]), ('ModSecurity', [('sec-rule-remove', 'SecRuleRemoveById'), ('sec-engine', 'SecRuleEngine Off')]), ('SSL', [('ssl-ca', 'SSL CA'), ('ssl-cert', 'SSL cert'), ('ssl-key', 'SSL key')])], db_index=True, verbose_name='name', max_length=128),
+ ),
+ ]
diff --git a/orchestra/contrib/websites/models.py b/orchestra/contrib/websites/models.py
index 85a82f14..ea73bdfa 100644
--- a/orchestra/contrib/websites/models.py
+++ b/orchestra/contrib/websites/models.py
@@ -115,7 +115,7 @@ class Website(models.Model):
class WebsiteDirective(models.Model):
website = models.ForeignKey(Website, verbose_name=_("web site"),
related_name='directives')
- name = models.CharField(_("name"), max_length=128,
+ name = models.CharField(_("name"), max_length=128, db_index=True,
choices=SiteDirective.get_choices())
value = models.CharField(_("value"), max_length=256)
diff --git a/orchestra/core/validators.py b/orchestra/core/validators.py
index c863cd6e..f9fc6c07 100644
--- a/orchestra/core/validators.py
+++ b/orchestra/core/validators.py
@@ -5,6 +5,7 @@ from ipaddress import ip_address
import phonenumbers
from django.core import validators
from django.core.exceptions import ValidationError
+from django.utils.deconstruct import deconstructible
from django.utils.translation import ugettext_lazy as _
from ..utils.python import import_class
@@ -37,6 +38,7 @@ def all_valid(*args):
raise ValidationError(errors)
+@deconstructible
class OrValidator(object):
"""
Run validators with an OR logic