from django.contrib.auth.forms import (PasswordChangeForm, AdminPasswordChangeForm) from django.contrib.auth.views import (logout as auth_logout, login as auth_login) from django.contrib.auth import get_user_model from django.core.urlresolvers import reverse, reverse_lazy from django.db import models from django.http import HttpResponseRedirect from django.utils.encoding import force_text from django.utils.text import capfirst from django.views import generic import extra_views import operator from . import permissions, utils from .forms import AdminAuthenticationForm from .viewmixins import Admin2Mixin, AdminModel2Mixin, Admin2ModelFormMixin class IndexView(Admin2Mixin, generic.TemplateView): """Context Variables :apps: A dictionary of apps, each app being a dictionary with keys being models and the value being djadmin2.types.ModelAdmin2 objects. :request.user: The user object representing the current user. """ default_template_name = "index.html" registry = None apps = None def get_context_data(self, **kwargs): data = super(IndexView, self).get_context_data(**kwargs) data.update({ 'apps': self.apps, }) return data class AppIndexView(Admin2Mixin, generic.TemplateView): default_template_name = "app_index.html" registry = None apps = None def get_context_data(self, **kwargs): data = super(AppIndexView, self).get_context_data(**kwargs) app_label = self.kwargs['app_label'] registry = self.apps[app_label] data.update({ 'app_label': app_label, 'registry': registry, }) return data class ModelListView(AdminModel2Mixin, generic.ListView): default_template_name = "model_list.html" permission_classes = ( permissions.IsStaffPermission, permissions.ModelViewPermission) def post(self, request): action_name = request.POST['action'] action_callable = self.get_actions()[action_name]['action_callable'] selected_model_pks = request.POST.getlist('selected_model_pk') queryset = self.model.objects.filter(pk__in=selected_model_pks) # If action_callable is a class subclassing from actions.BaseListAction # then we generate the callable object. if hasattr(action_callable, "process_queryset"): response = action_callable.as_view(queryset=queryset)(request) else: # generate the reponse if a function. response = action_callable(request, queryset) if response is None: return HttpResponseRedirect(self.get_success_url()) else: return response def get_search_results(self, queryset, search_term): # Lifted from django.contrib.admin def construct_search(field_name): if field_name.startswith('^'): return "%s__istartswith" % field_name[1:] elif field_name.startswith('='): return "%s__iexact" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] else: return "%s__icontains" % field_name use_distinct = False orm_lookups = [construct_search(str(search_field)) for search_field in self.model_admin.search_fields] for bit in search_term.split(): or_queries = [models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups] queryset = queryset.filter(reduce(operator.or_, or_queries)) if not use_distinct: for search_spec in orm_lookups: opts = utils.model_options(self.get_model()) if utils.lookup_needs_distinct(opts, search_spec): use_distinct = True break return queryset, use_distinct def get_queryset(self): queryset = super(ModelListView, self).get_queryset() search_term = self.request.GET.get('q', None) search_use_distinct = False if self.model_admin.search_fields and search_term: queryset, search_use_distinct = self.get_search_results(queryset, search_term) if search_use_distinct: return queryset.distinct() else: return queryset def get_context_data(self, **kwargs): context = super(ModelListView, self).get_context_data(**kwargs) context['model'] = self.get_model() context['actions'] = self.get_actions().values() context['search_fields'] = self.get_search_fields() context['search_term'] = self.request.GET.get('q', '') return context def get_success_url(self): view_name = 'admin2:{}_{}_index'.format(self.app_label, self.model_name) return reverse(view_name) def get_actions(self): return self.model_admin.get_list_actions() def get_search_fields(self): return self.model_admin.search_fields class ModelDetailView(AdminModel2Mixin, generic.DetailView): default_template_name = "model_detail.html" permission_classes = ( permissions.IsStaffPermission, permissions.ModelViewPermission) class ModelEditFormView(AdminModel2Mixin, Admin2ModelFormMixin, extra_views.UpdateWithInlinesView): form_class = None default_template_name = "model_update_form.html" permission_classes = ( permissions.IsStaffPermission, permissions.ModelChangePermission) def get_context_data(self, **kwargs): context = super(ModelEditFormView, self).get_context_data(**kwargs) context['model'] = self.get_model() context['action'] = "Change" return context class ModelAddFormView(AdminModel2Mixin, Admin2ModelFormMixin, extra_views.CreateWithInlinesView): form_class = None default_template_name = "model_update_form.html" permission_classes = ( permissions.IsStaffPermission, permissions.ModelAddPermission) def get_context_data(self, **kwargs): context = super(ModelAddFormView, self).get_context_data(**kwargs) context['model'] = self.get_model() context['action'] = "Add" return context class ModelDeleteView(AdminModel2Mixin, generic.DeleteView): success_url = "../../" # TODO - fix this! default_template_name = "model_confirm_delete.html" permission_classes = ( permissions.IsStaffPermission, permissions.ModelDeletePermission) def get_context_data(self, **kwargs): context = super(ModelDeleteView, self).get_context_data(**kwargs) def _format_callback(obj): opts = utils.model_options(obj) return '%s: %s' % (force_text(capfirst(opts.verbose_name)), force_text(obj)) collector = utils.NestedObjects(using=None) collector.collect([self.get_object()]) context.update({ 'deletable_objects': collector.nested(_format_callback) }) return context class PasswordChangeView(Admin2Mixin, generic.UpdateView): default_template_name = 'auth/password_change_form.html' form_class = AdminPasswordChangeForm admin_form_class = PasswordChangeForm model = get_user_model() success_url = reverse_lazy('admin2:password_change_done') def get_form_kwargs(self, **kwargs): data = {'user': self.get_object()} if self.request.method in ('POST', 'PUT'): data.update({ 'data': self.request.POST }) return data def get_form_class(self): if self.request.user == self.get_object(): return self.admin_form_class return super(PasswordChangeView, self).get_form_class() class PasswordChangeDoneView(Admin2Mixin, generic.TemplateView): default_template_name = 'auth/password_change_done.html' class LoginView(Admin2Mixin, generic.TemplateView): default_template_name = 'auth/login.html' authentication_form = AdminAuthenticationForm def dispatch(self, request, *args, **kwargs): return auth_login(request, authentication_form=self.authentication_form, template_name=self.get_template_names(), *args, **kwargs) class LogoutView(Admin2Mixin, generic.TemplateView): default_template_name = 'auth/logout.html' def get(self, request, *args, **kwargs): return auth_logout(request, template_name=self.get_template_names(), *args, **kwargs)