diff --git a/orchestra/admin/utils.py b/orchestra/admin/utils.py
index 2e7a00b7..d378968d 100644
--- a/orchestra/admin/utils.py
+++ b/orchestra/admin/utils.py
@@ -62,15 +62,15 @@ def wrap_admin_view(modeladmin, view):
def set_default_filter(queryarg, request, value):
""" set default filters for changelist_view """
if queryarg not in request.GET:
- q = request.GET.copy()
+ request_copy = request.GET.copy()
if callable(value):
value = value(request)
- q[queryarg] = value
- request.GET = q
+ request_copy[queryarg] = value
+ request.GET = request_copy
request.META['QUERY_STRING'] = request.GET.urlencode()
-def link(*args, **kwargs):
+def admin_link(*args, **kwargs):
""" utility function for creating admin links """
field = args[0] if args else ''
order = kwargs.pop('order', field)
@@ -88,7 +88,7 @@ def link(*args, **kwargs):
extra = 'onclick="return showAddAnotherPopup(this);"'
return '%s' % (url, extra, obj)
display_link.allow_tags = True
- display_link.short_description = _(field)
+ display_link.short_description = _(field.replace('_', ' '))
display_link.admin_order_field = order
return display_link
diff --git a/orchestra/api/options.py b/orchestra/api/options.py
index a56c3565..c56b8e33 100644
--- a/orchestra/api/options.py
+++ b/orchestra/api/options.py
@@ -94,6 +94,8 @@ class LinkHeaderRouter(DefaultRouter):
for _prefix, viewset, __ in self.registry:
if _prefix == prefix_or_model or viewset.model == prefix_or_model:
return viewset
+ msg = "%s does not have a regiestered viewset" % prefix_or_model
+ raise KeyError(msg)
def insert(self, prefix_or_model, name, field, **kwargs):
""" Dynamically add new fields to an existing serializer """
diff --git a/orchestra/apps/accounts/admin.py b/orchestra/apps/accounts/admin.py
index 18645882..2f8f7024 100644
--- a/orchestra/apps/accounts/admin.py
+++ b/orchestra/apps/accounts/admin.py
@@ -8,7 +8,7 @@ from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
-from orchestra.admin.utils import wrap_admin_view, link
+from orchestra.admin.utils import wrap_admin_view, admin_link
from orchestra.core import services
from .filters import HasMainUserListFilter
@@ -42,7 +42,7 @@ class AccountAdmin(ExtendedModelAdmin):
add_form = AccountCreationForm
form = AccountChangeForm
- user_link = link('user', order='user__username')
+ user_link = admin_link('user', order='user__username')
def name(self, account):
return account.name
diff --git a/orchestra/apps/databases/admin.py b/orchestra/apps/databases/admin.py
index b3771b59..30c45af4 100644
--- a/orchestra/apps/databases/admin.py
+++ b/orchestra/apps/databases/admin.py
@@ -7,7 +7,7 @@ from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
-from orchestra.admin.utils import link
+from orchestra.admin.utils import admin_link
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
from .forms import (DatabaseUserChangeForm, DatabaseUserCreationForm,
@@ -21,7 +21,7 @@ class UserInline(admin.TabularInline):
readonly_fields = ('user_link',)
extra = 0
- user_link = link('user')
+ user_link = admin_link('user')
def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """
@@ -38,7 +38,7 @@ class PermissionInline(AccountAdminMixin, admin.TabularInline):
extra = 0
filter_by_account_fields = ['database']
- database_link = link('database', popup=True)
+ database_link = admin_link('database', popup=True)
def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """
diff --git a/orchestra/apps/domains/admin.py b/orchestra/apps/domains/admin.py
index e49af15b..233250c1 100644
--- a/orchestra/apps/domains/admin.py
+++ b/orchestra/apps/domains/admin.py
@@ -8,7 +8,7 @@ from django.template.response import TemplateResponse
from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ChangeListDefaultFilter, ExtendedModelAdmin
-from orchestra.admin.utils import wrap_admin_view, link
+from orchestra.admin.utils import wrap_admin_view, admin_link
from orchestra.apps.accounts.admin import AccountAdminMixin
from orchestra.utils import apps
@@ -41,7 +41,7 @@ class DomainInline(admin.TabularInline):
extra = 0
verbose_name_plural = _("Subdomains")
- domain_link = link()
+ domain_link = admin_link()
domain_link.short_description = _("Name")
def has_add_permission(self, *args, **kwargs):
diff --git a/orchestra/apps/issues/admin.py b/orchestra/apps/issues/admin.py
index db5c496c..5e0ef9ff 100644
--- a/orchestra/apps/issues/admin.py
+++ b/orchestra/apps/issues/admin.py
@@ -12,7 +12,8 @@ from django.utils.translation import ugettext_lazy as _
from markdown import markdown
from orchestra.admin import ChangeListDefaultFilter, ExtendedModelAdmin#, ChangeViewActions
-from orchestra.admin.utils import (link, colored, wrap_admin_view, display_timesince)
+from orchestra.admin.utils import (admin_link, colored, wrap_admin_view,
+ display_timesince)
from orchestra.apps.contacts import settings as contacts_settings
from .actions import (reject_tickets, resolve_tickets, take_tickets, close_tickets,
@@ -107,8 +108,8 @@ class TicketInline(admin.TabularInline):
extra = 0
max_num = 0
- creator_link = link('creator')
- owner_link = link('owner')
+ creator_link = admin_link('creator')
+ owner_link = admin_link('owner')
def ticket_id(self, instance):
return '%s' % link()(self, instance)
@@ -198,9 +199,9 @@ class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin): #TODO ChangeView
'issues/js/ticket-admin.js',
)
- display_creator = link('creator')
- display_queue = link('queue')
- display_owner = link('owner')
+ display_creator = admin_link('creator')
+ display_queue = admin_link('queue')
+ display_owner = admin_link('owner')
def display_summary(self, ticket):
context = {
@@ -212,7 +213,7 @@ class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin): #TODO ChangeView
if msg:
context.update({
'updated': display_timesince(msg.created_on),
- 'updater': link('author')(self, msg) if msg.author else msg.author_name,
+ 'updater': admin_link('author')(self, msg) if msg.author else msg.author_name,
})
context['updated'] = '. Updated by %(updater)s about %(updated)s' % context
return '
Added by %(creator)s about %(created)s%(updated)s
' % context
diff --git a/orchestra/apps/issues/filters.py b/orchestra/apps/issues/filters.py
index 8782574d..982ac7b1 100644
--- a/orchestra/apps/issues/filters.py
+++ b/orchestra/apps/issues/filters.py
@@ -1,4 +1,5 @@
from django.contrib.admin import SimpleListFilter
+from django.utils.translation import ugettext_lazy as _
from .models import Ticket
@@ -10,13 +11,19 @@ class MyTicketsListFilter(SimpleListFilter):
def lookups(self, request, model_admin):
return (
- ('True', 'My Tickets'),
- ('False', 'All'),
+ ('True', _("My Tickets")),
+ ('False', _("All")),
)
def queryset(self, request, queryset):
if self.value() == 'True':
return queryset.involved_by(request.user)
+
+ def choices(self, cl):
+ """ Remove default All """
+ choices = iter(super(MyTicketsListFilter, self).choices(cl))
+ choices.next()
+ return choices
class TicketStateListFilter(SimpleListFilter):
@@ -25,14 +32,14 @@ class TicketStateListFilter(SimpleListFilter):
def lookups(self, request, model_admin):
return (
- ('OPEN', "Open"),
- (Ticket.NEW, "New"),
- (Ticket.IN_PROGRESS, "In Progress"),
- (Ticket.RESOLVED, "Resolved"),
- (Ticket.FEEDBACK, "Feedback"),
- (Ticket.REJECTED, "Rejected"),
- (Ticket.CLOSED, "Closed"),
- ('False', 'All'),
+ ('OPEN', _("Open")),
+ (Ticket.NEW, _("New")),
+ (Ticket.IN_PROGRESS, _("In Progress")),
+ (Ticket.RESOLVED, _("Resolved")),
+ (Ticket.FEEDBACK, _("Feedback")),
+ (Ticket.REJECTED, _("Rejected")),
+ (Ticket.CLOSED, _("Closed")),
+ ('False', _("All")),
)
def queryset(self, request, queryset):
@@ -41,3 +48,10 @@ class TicketStateListFilter(SimpleListFilter):
elif self.value() == 'False':
return queryset
return queryset.filter(state=self.value())
+
+ def choices(self, cl):
+ """ Remove default All """
+ choices = iter(super(TicketStateListFilter, self).choices(cl))
+ choices.next()
+ return choices
+
diff --git a/orchestra/apps/lists/admin.py b/orchestra/apps/lists/admin.py
index dfcf2e54..11ed445e 100644
--- a/orchestra/apps/lists/admin.py
+++ b/orchestra/apps/lists/admin.py
@@ -4,7 +4,7 @@ from django.contrib.auth.admin import UserAdmin
from django.utils.translation import ugettext, ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
-from orchestra.admin.utils import link
+from orchestra.admin.utils import admin_link
from orchestra.apps.accounts.admin import SelectAccountAdminMixin
from .forms import ListCreationForm, ListChangeForm
@@ -47,7 +47,7 @@ class ListAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
add_form = ListCreationForm
filter_by_account_fields = ['address_domain']
- address_domain_link = link('address_domain', order='address_domain__name')
+ address_domain_link = admin_link('address_domain', order='address_domain__name')
def get_urls(self):
useradmin = UserAdmin(List, self.admin_site)
diff --git a/orchestra/apps/orchestration/admin.py b/orchestra/apps/orchestration/admin.py
index cf5e39ad..fa06ebe9 100644
--- a/orchestra/apps/orchestration/admin.py
+++ b/orchestra/apps/orchestration/admin.py
@@ -5,7 +5,7 @@ from django.utils.translation import ugettext_lazy as _
from djcelery.humanize import naturaldate
from orchestra.admin.html import monospace_format
-from orchestra.admin.utils import link
+from orchestra.admin.utils import admin_link
from .models import Server, Route, BackendLog, BackendOperation
@@ -60,7 +60,7 @@ class BackendOperationInline(admin.TabularInline):
def instance_link(self, operation):
try:
- return link('instance')(self, operation)
+ return admin_link('instance')(self, operation)
except:
return _("deleted {0} {1}").format(
escape(operation.content_type), escape(operation.object_id)
@@ -88,11 +88,7 @@ class BackendLogAdmin(admin.ModelAdmin):
]
readonly_fields = fields
- def server_link(self, log):
- url = reverse('admin:orchestration_server_change', args=(log.server.pk,))
- return '%s' % (url, log.server.name)
- server_link.short_description = _("server")
- server_link.allow_tags = True
+ server_link = admin_link('server')
def display_state(self, log):
color = STATE_COLORS.get(log.state, 'grey')
diff --git a/orchestra/apps/orders/admin.py b/orchestra/apps/orders/admin.py
index b51a91c6..d949dd19 100644
--- a/orchestra/apps/orders/admin.py
+++ b/orchestra/apps/orders/admin.py
@@ -1,18 +1,29 @@
from django import forms
+from django.db import models
from django.contrib import admin
+from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
+from orchestra.admin import ChangeListDefaultFilter
+from orchestra.admin.filters import UsedContentTypeFilter
+from orchestra.admin.utils import admin_link
from orchestra.apps.accounts.admin import AccountAdminMixin
from orchestra.core import services
+from .filters import ActiveOrderListFilter
from .models import Service, Order, MetricStorage
class ServiceAdmin(admin.ModelAdmin):
+ list_display = (
+ 'description', 'content_type', 'handler_type', 'num_orders', 'is_active'
+ )
+ list_filter = ('is_active', 'handler_type', UsedContentTypeFilter)
fieldsets = (
(None, {
'classes': ('wide',),
- 'fields': ('description', 'content_type', 'match', 'handler', 'is_active')
+ 'fields': ('description', 'content_type', 'match', 'handler_type',
+ 'is_active')
}),
(_("Billing options"), {
'classes': ('wide',),
@@ -36,12 +47,32 @@ class ServiceAdmin(admin.ModelAdmin):
if db_field.name in ['match', 'metric']:
kwargs['widget'] = forms.TextInput(attrs={'size':'160'})
return super(ServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs)
+
+ def num_orders(self, service):
+ num = service.orders.count()
+ url = reverse('admin:orders_order_changelist')
+ url += '?service=%i' % service.pk
+ return '%d' % (url, num)
+ num_orders.short_description = _("Orders")
+ num_orders.admin_order_field = 'orders__count'
+ num_orders.allow_tags = True
+
+ def get_queryset(self, request):
+ qs = super(ServiceAdmin, self).get_queryset(request)
+ qs = qs.annotate(models.Count('orders'))
+ return qs
-class OrderAdmin(AccountAdminMixin, admin.ModelAdmin):
- list_display = ('id', 'service', 'account_link', 'cancelled_on')
- list_filter = ('service',)
-
+class OrderAdmin(AccountAdminMixin, ChangeListDefaultFilter, admin.ModelAdmin):
+ list_display = (
+ 'id', 'service', 'account_link', 'content_object_link', 'cancelled_on'
+ )
+ list_filter = (ActiveOrderListFilter, 'service',)
+ default_changelist_filters = (
+ ('is_active', 'True'),
+ )
+
+ content_object_link = admin_link('content_object')
class MetricStorageAdmin(admin.ModelAdmin):
list_display = ('order', 'value', 'created_on', 'updated_on')
diff --git a/orchestra/apps/orders/filters.py b/orchestra/apps/orders/filters.py
new file mode 100644
index 00000000..f82e54a6
--- /dev/null
+++ b/orchestra/apps/orders/filters.py
@@ -0,0 +1,28 @@
+from django.contrib.admin import SimpleListFilter
+from django.utils.translation import ugettext_lazy as _
+
+
+class ActiveOrderListFilter(SimpleListFilter):
+ """ Filter tickets by created_by according to request.user """
+ title = 'Orders'
+ parameter_name = 'is_active'
+
+ def lookups(self, request, model_admin):
+ return (
+ ('True', _("Active")),
+ ('False', _("Inactive")),
+ ('None', _("All")),
+ )
+
+ def queryset(self, request, queryset):
+ if self.value() == 'True':
+ return queryset.active()
+ elif self.value() == 'False':
+ return queryset.inactive()
+ return queryset
+
+ def choices(self, cl):
+ """ Remove default All """
+ choices = iter(super(ActiveOrderListFilter, self).choices(cl))
+ choices.next()
+ return choices
diff --git a/orchestra/apps/orders/handlers.py b/orchestra/apps/orders/handlers.py
index 2de9cbb8..76597327 100644
--- a/orchestra/apps/orders/handlers.py
+++ b/orchestra/apps/orders/handlers.py
@@ -12,13 +12,19 @@ class ServiceHandler(plugins.Plugin):
def __init__(self, service):
self.service = service
+ def __getattr__(self, attr):
+ return getattr(self.service, attr)
+
@classmethod
def get_plugin_choices(cls):
choices = super(ServiceHandler, cls).get_plugin_choices()
return [('', _("Default"))] + choices
- def __getattr__(self, attr):
- return getattr(self.service, attr)
+ def get_content_type(self):
+ if not self.model:
+ return self.content_type
+ app_label, model = self.model.split('.')
+ return ContentType.objects.get_by_natural_key(app_label, model.lower())
def matches(self, instance):
safe_locals = {
@@ -27,13 +33,8 @@ class ServiceHandler(plugins.Plugin):
return eval(self.match, safe_locals)
def get_metric(self, instance):
- safe_locals = {
- instance._meta.model_name: instance
- }
- return eval(self.metric, safe_locals)
-
- def get_content_type(self):
- if not self.model:
- return self.content_type
- app_label, model = self.model.split('.')
- return ContentType.objects.get_by_natural_key(app_label, model.lower())
+ if self.metric:
+ safe_locals = {
+ instance._meta.model_name: instance
+ }
+ return eval(self.metric, safe_locals)
diff --git a/orchestra/apps/orders/helpers.py b/orchestra/apps/orders/helpers.py
index 746fdfa4..087c1c4e 100644
--- a/orchestra/apps/orders/helpers.py
+++ b/orchestra/apps/orders/helpers.py
@@ -31,7 +31,7 @@ def search_for_related(origin, max_depth=2):
if hasattr(node, 'account') or isinstance(node, Account):
return node
for related in related_iterator(node):
- if related not in models:
+ if related and related not in models:
new_models = list(models)
new_models.append(related)
queue.append(new_models)
diff --git a/orchestra/apps/orders/models.py b/orchestra/apps/orders/models.py
index 2b8a014b..a24d24ba 100644
--- a/orchestra/apps/orders/models.py
+++ b/orchestra/apps/orders/models.py
@@ -45,8 +45,10 @@ class Service(models.Model):
description = models.CharField(_("description"), max_length=256, unique=True)
content_type = models.ForeignKey(ContentType, verbose_name=_("content type"))
match = models.CharField(_("match"), max_length=256, blank=True)
- handler = models.CharField(_("handler"), max_length=256, blank=True,
- help_text=_("Handler used to process this Service."),
+ handler_type = models.CharField(_("handler"), max_length=256, blank=True,
+ help_text=_("Handler used for processing this Service. A handler "
+ "enables customized behaviour far beyond what options "
+ "here allow to."),
choices=ServiceHandler.get_plugin_choices())
is_active = models.BooleanField(_("is active"), default=True)
# Billing
@@ -172,14 +174,16 @@ class Service(models.Model):
cache.set(ct, services)
return services
+ # FIXME some times caching is nasty, do we really have to? make get_plugin more efficient?
@cached_property
- def proxy(self):
- if self.handler:
- return ServiceHandler.get_plugin(self.handler)(self)
+ def handler(self):
+ """ Accessor of this service handler instance """
+ if self.handler_type:
+ return ServiceHandler.get_plugin(self.handler_type)(self)
return ServiceHandler(self)
def clean(self):
- content_type = self.proxy.get_content_type()
+ content_type = self.handler.get_content_type()
if self.content_type != content_type:
msg =_("Content type must be equal to '%s'." % str(content_type))
raise ValidationError(msg)
@@ -191,22 +195,30 @@ class Service(models.Model):
except IndexError:
pass
else:
- try:
- self.proxy.matches(obj)
- except Exception as e:
- raise ValidationError(_(str(e)))
+ for attr in ['matches', 'get_metric']:
+ try:
+ getattr(self.handler, attr)(obj)
+ except Exception as exception:
+ name = type(exception).__name__
+ message = exception.message
+ msg = "{0} {1}: {2}".format(attr, name, message)
+ raise ValidationError(msg)
class OrderQuerySet(models.QuerySet):
- def by_object(self, obj, *args, **kwargs):
+ def by_object(self, obj, **kwargs):
ct = ContentType.objects.get_for_model(obj)
- return self.filter(object_id=obj.pk, content_type=ct)
+ return self.filter(object_id=obj.pk, content_type=ct, **kwargs)
- def active(self, *args, **kwargs):
+ def active(self, **kwargs):
""" return active orders """
return self.filter(
Q(cancelled_on__isnull=True) | Q(cancelled_on__gt=timezone.now())
- ).filter(*args, **kwargs)
+ ).filter(**kwargs)
+
+ def inactive(self, **kwargs):
+ """ return inactive orders """
+ return self.filter(cancelled_on__lt=timezone.now(), **kwargs)
class Order(models.Model):
@@ -234,10 +246,12 @@ class Order(models.Model):
def update(self):
instance = self.content_object
- if self.service.metric:
- metric = self.service.get_metric(instance)
- MetricStorage.store(self, metric)
- description = "{}: {}".format(self.service.description, str(instance))
+ handler = self.service.handler
+ if handler.metric:
+ metric = handler.get_metric(instance)
+ if metric is not None:
+ MetricStorage.store(self, metric)
+ description = "{}: {}".format(handler.description, str(instance))
if self.description != description:
self.description = description
self.save()
@@ -246,7 +260,7 @@ class Order(models.Model):
def update_orders(cls, instance):
for service in Service.get_services(instance):
orders = Order.objects.by_object(instance, service=service).active()
- if service.matches(instance):
+ if service.handler.matches(instance):
if not orders:
account_id = getattr(instance, 'account_id', instance.pk)
order = cls.objects.create(content_object=instance,
@@ -289,23 +303,20 @@ class MetricStorage(models.Model):
@receiver(pre_delete, dispatch_uid="orders.cancel_orders")
def cancel_orders(sender, **kwargs):
- if (not sender in [MetricStorage, LogEntry, Order, Service] and
- not Service in sender.__mro__):
- instance = kwargs['instance']
- for order in Order.objects.by_object(instance).active():
- order.cancel()
+ if sender not in [MetricStorage, LogEntry, Order, Service]:
+ instance = kwargs['instance']
+ for order in Order.objects.by_object(instance).active():
+ order.cancel()
@receiver(post_save, dispatch_uid="orders.update_orders")
@receiver(post_delete, dispatch_uid="orders.update_orders")
def update_orders(sender, **kwargs):
- if (not sender in [MetricStorage, LogEntry, Order, Service] and
- not Service in sender.__mro__):
- instance = kwargs['instance']
- print kwargs
- if instance.pk:
- # post_save
- Order.update_orders(instance)
- related = search_for_related(instance)
- if related:
- Order.update_orders(related)
+ if sender not in [MetricStorage, LogEntry, Order, Service]:
+ instance = kwargs['instance']
+ if instance.pk:
+ # post_save
+ Order.update_orders(instance)
+ related = search_for_related(instance)
+ if related:
+ Order.update_orders(related)
diff --git a/orchestra/apps/resources/admin.py b/orchestra/apps/resources/admin.py
index 3f5fba38..7bbb55f7 100644
--- a/orchestra/apps/resources/admin.py
+++ b/orchestra/apps/resources/admin.py
@@ -7,7 +7,7 @@ from djcelery.humanize import naturaldate
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.filters import UsedContentTypeFilter
-from orchestra.admin.utils import insertattr, get_modeladmin, link
+from orchestra.admin.utils import insertattr, get_modeladmin, admin_link
from orchestra.core import services
from orchestra.utils import running_syncdb
@@ -71,10 +71,7 @@ class ResourceDataAdmin(admin.ModelAdmin):
list_filter = ('resource',)
readonly_fields = ('content_object_link',)
- def content_object_link(self, data):
- return link('content_object')(self, data)
- content_object_link.allow_tags = True
- content_object_link.short_description = _("Content object")
+ content_object_link = admin_link('content_object')
class MonitorDataAdmin(admin.ModelAdmin):
@@ -82,10 +79,7 @@ class MonitorDataAdmin(admin.ModelAdmin):
list_filter = ('monitor',)
readonly_fields = ('content_object_link',)
- def content_object_link(self, data):
- return link('content_object')(self, data)
- content_object_link.allow_tags = True
- content_object_link.short_description = _("Content object")
+ content_object_link = admin_link('content_object')
admin.site.register(Resource, ResourceAdmin)
diff --git a/orchestra/apps/resources/models.py b/orchestra/apps/resources/models.py
index 1940df8d..14a54027 100644
--- a/orchestra/apps/resources/models.py
+++ b/orchestra/apps/resources/models.py
@@ -164,7 +164,15 @@ def create_resource_relation():
class ResourceHandler(object):
""" account.resources.web """
def __getattr__(self, attr):
- return self.obj.resource_set.get(resource__name=attr)
+ """ get or create ResourceData """
+ try:
+ return self.obj.resource_set.get(resource__name=attr)
+ except ResourceData.DoesNotExist:
+ model = self.obj._meta.model_name
+ resource = Resource.objects.get(content_type__model=model,
+ name=attr, is_active=True)
+ return ResourceData.objects.create(content_object=self.obj,
+ resource=resource)
def __get__(self, obj, cls):
self.obj = obj
diff --git a/orchestra/apps/resources/serializers.py b/orchestra/apps/resources/serializers.py
index 1f7808bb..7be4393c 100644
--- a/orchestra/apps/resources/serializers.py
+++ b/orchestra/apps/resources/serializers.py
@@ -27,7 +27,10 @@ if not running_syncdb():
# TODO why this is even loaded during syncdb?
for resources in Resource.group_by_content_type():
model = resources[0].content_type.model_class()
- router.insert(model, 'resources', ResourceSerializer, required=False, many=True)
+ try:
+ router.insert(model, 'resources', ResourceSerializer, required=False, many=True)
+ except KeyError:
+ continue
def validate_resources(self, attrs, source, _resources=resources):
""" Creates missing resources """
diff --git a/orchestra/apps/users/roles/mail/admin.py b/orchestra/apps/users/roles/mail/admin.py
index 0124b242..72bfdc71 100644
--- a/orchestra/apps/users/roles/mail/admin.py
+++ b/orchestra/apps/users/roles/mail/admin.py
@@ -7,7 +7,7 @@ from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
-from orchestra.admin.utils import insertattr, link
+from orchestra.admin.utils import insertattr, admin_link
from orchestra.apps.accounts.admin import SelectAccountAdminMixin
from orchestra.apps.domains.forms import DomainIterator
from orchestra.apps.users.roles.admin import RoleAdmin
@@ -79,7 +79,7 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
filter_by_account_fields = ['domain']
filter_horizontal = ['mailboxes']
- domain_link = link('domain', order='domain__name')
+ domain_link = admin_link('domain', order='domain__name')
def email_link(self, address):
link = self.domain_link(address)
diff --git a/orchestra/apps/websites/admin.py b/orchestra/apps/websites/admin.py
index 741f863a..b67ac4ad 100644
--- a/orchestra/apps/websites/admin.py
+++ b/orchestra/apps/websites/admin.py
@@ -5,7 +5,7 @@ from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
-from orchestra.admin.utils import link
+from orchestra.admin.utils import admin_link
from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
from orchestra.apps.accounts.widgets import account_related_field_widget_factory
@@ -35,7 +35,7 @@ class ContentInline(AccountAdminMixin, admin.TabularInline):
readonly_fields = ('webapp_link', 'webapp_type')
filter_by_account_fields = ['webapp']
- webapp_link = link('webapp', popup=True)
+ webapp_link = admin_link('webapp', popup=True)
webapp_link.short_description = _("Web App")
def webapp_type(self, content):