from django import forms from django.conf.urls import patterns, url from django.contrib import admin, messages from django.contrib.admin.util import unquote from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect 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, admin_link from orchestra.core import services from .filters import HasMainUserListFilter from .forms import AccountCreationForm, AccountChangeForm from .models import Account class AccountAdmin(ExtendedModelAdmin): list_display = ('name', 'user_link', 'type', 'is_active') list_filter = ( 'type', 'is_active', HasMainUserListFilter ) add_fieldsets = ( (_("User"), { 'fields': ('username', 'password1', 'password2',), }), (_("Account info"), { 'fields': (('type', 'language'), 'comments'), }), ) fieldsets = ( (_("User"), { 'fields': ('user_link', 'password',), }), (_("Account info"), { 'fields': (('type', 'language'), 'comments'), }), ) readonly_fields = ('user_link',) search_fields = ('users__username',) add_form = AccountCreationForm form = AccountChangeForm user_link = admin_link('user', order='user__username') def name(self, account): return account.name name.admin_order_field = 'user__username' def formfield_for_dbfield(self, db_field, **kwargs): """ Make value input widget bigger """ if db_field.name == 'comments': kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4}) return super(AccountAdmin, self).formfield_for_dbfield(db_field, **kwargs) def change_view(self, request, object_id, form_url='', extra_context=None): if request.method == 'GET': account = self.get_object(request, unquote(object_id)) if not account.is_active: messages.warning(request, 'This account is disabled.') context = { 'services': sorted( [ model._meta for model in services.get() ], key=lambda i: i.verbose_name_plural.lower() ) } context.update(extra_context or {}) return super(AccountAdmin, self).change_view(request, object_id, form_url=form_url, extra_context=context) def save_model(self, request, obj, form, change): """ Save user and account, they are interdependent """ if change: return super(AccountAdmin, self).save_model(request, obj, form, change) obj.user.save() obj.user_id = obj.user.pk obj.save() obj.user.account = obj obj.user.save() def get_queryset(self, request): """ Select related for performance """ # TODO move invoicecontact to contacts related = ('user', 'invoicecontact') return super(AccountAdmin, self).get_queryset(request).select_related(*related) admin.site.register(Account, AccountAdmin) class AccountListAdmin(AccountAdmin): """ Account list to allow account selection when creating new services """ list_display = ('select_account', 'type', 'user') actions = None search_fields = ['user__username',] ordering = ('user__username',) def select_account(self, instance): context = { 'url': '../?account=' + str(instance.pk), 'name': instance.name } return '%(name)s' % context select_account.short_description = _("account") select_account.allow_tags = True select_account.order_admin_field = 'user__username' def changelist_view(self, request, extra_context=None): opts = self.model._meta original_app_label = request.META['PATH_INFO'].split('/')[-5] original_model = request.META['PATH_INFO'].split('/')[-4] context = { 'title': _("Select account for adding a new %s") % (original_model), 'original_app_label': original_app_label, 'original_model': original_model, } context.update(extra_context or {}) return super(AccountListAdmin, self).changelist_view(request, extra_context=context) class AccountAdminMixin(object): """ Provide basic account support to ModelAdmin and AdminInline classes """ readonly_fields = ('account_link',) filter_by_account_fields = [] def account_link(self, instance): account = instance.account if instance.pk else self.account url = reverse('admin:accounts_account_change', args=(account.pk,)) return '%s' % (url, account.name) account_link.short_description = _("account") account_link.allow_tags = True account_link.admin_order_field = 'account__user__username' def get_queryset(self, request): """ Select related for performance """ qs = super(AccountAdminMixin, self).get_queryset(request) return qs.select_related('account__user') def formfield_for_dbfield(self, db_field, **kwargs): """ Improve performance of account field and filter by account """ if db_field.name == 'account': qs = kwargs.get('queryset', db_field.rel.to.objects) kwargs['queryset'] = qs.select_related('user') formfield = super(AccountAdminMixin, self).formfield_for_dbfield(db_field, **kwargs) if db_field.name in self.filter_by_account_fields: if hasattr(self, 'account'): # Hack widget render in order to append ?account=id to the add url old_render = formfield.widget.render def render(*args, **kwargs): output = old_render(*args, **kwargs) output = output.replace('/add/"', '/add/?account=%s"' % self.account.pk) return mark_safe(output) formfield.widget.render = render # Filter related object by account formfield.queryset = formfield.queryset.filter(account=self.account) return formfield class SelectAccountAdminMixin(AccountAdminMixin): """ Provides support for accounts on ModelAdmin """ def get_readonly_fields(self, request, obj=None): if obj: self.account = obj.account return super(AccountAdminMixin, self).get_readonly_fields(request, obj=obj) def get_inline_instances(self, request, obj=None): inlines = super(AccountAdminMixin, self).get_inline_instances(request, obj=obj) if hasattr(self, 'account'): account = self.account else: account = Account.objects.get(pk=request.GET['account']) [ setattr(inline, 'account', account) for inline in inlines ] return inlines def get_urls(self): """ Hooks select account url """ urls = super(AccountAdminMixin, self).get_urls() admin_site = self.admin_site opts = self.model._meta info = opts.app_label, opts.model_name account_list = AccountListAdmin(Account, admin_site).changelist_view select_urls = patterns("", url("/select-account/$", wrap_admin_view(self, account_list), name='%s_%s_select_account' % info), ) return select_urls + urls def add_view(self, request, form_url='', extra_context=None): """ Redirects to select account view if required """ if request.user.is_superuser: if 'account' in request.GET or Account.objects.count() == 1: kwargs = {} if 'account' in request.GET: kwargs = dict(pk=request.GET['account']) self.account = Account.objects.get(**kwargs) opts = self.model._meta context = { 'title': _("Add %s for %s") % (opts.verbose_name, self.account.name) } context.update(extra_context or {}) return super(AccountAdminMixin, self).add_view(request, form_url=form_url, extra_context=context) return HttpResponseRedirect('./select-account/') def save_model(self, request, obj, form, change): """ Given a model instance save it to the database. """ if not change: obj.account_id = self.account.pk obj.save()