django-admin2/djadmin2/views.py

368 lines
12 KiB
Python
Raw Normal View History

2013-07-07 14:13:48 +00:00
# -*- coding: utf-8 -*-
from __future__ import division, absolute_import, unicode_literals
2013-07-06 16:26:29 +00:00
import operator
from django.contrib.auth.forms import (PasswordChangeForm,
AdminPasswordChangeForm)
2013-05-24 04:06:31 +00:00
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
2013-07-06 10:13:36 +00:00
from django.utils.translation import ugettext_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
2013-07-07 13:32:44 +00:00
from django.db.models.fields import FieldDoesNotExist
2013-05-18 08:47:53 +00:00
2013-05-19 09:21:25 +00:00
import extra_views
from . import permissions, utils
from .forms import AdminAuthenticationForm
2013-07-18 20:59:08 +00:00
from .models import LogEntry
2013-05-31 01:21:43 +00:00
from .viewmixins import Admin2Mixin, AdminModel2Mixin, Admin2ModelFormMixin
from .filters import build_list_filter
2013-05-21 21:50:50 +00:00
class IndexView(Admin2Mixin, generic.TemplateView):
2013-07-06 07:37:25 +00:00
"""Context Variables
:apps: A dictionary of apps, each app being a dictionary with keys
being models and the value being djadmin2.types.ModelAdmin2
objects.
2013-07-04 12:07:32 +00:00
"""
2013-05-18 14:40:18 +00:00
default_template_name = "index.html"
registry = None
2013-05-19 11:03:25 +00:00
apps = None
2013-05-17 22:15:45 +00:00
def get_context_data(self, **kwargs):
data = super(IndexView, self).get_context_data(**kwargs)
data.update({
2013-05-19 11:03:25 +00:00
'apps': self.apps,
})
return data
2013-05-17 22:15:45 +00:00
2013-05-19 07:47:42 +00:00
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):
2013-07-06 12:40:49 +00:00
"""Context Variables
:is_paginated: If the page is paginated (page has a next button)
:model: Type of object you are editing
:model_name: Name of the object you are editing
:app_label: Name of your app
"""
2013-05-18 14:40:18 +00:00
default_template_name = "model_list.html"
permission_classes = (
permissions.IsStaffPermission,
permissions.ModelViewPermission)
2013-05-17 22:15:45 +00:00
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)
2013-07-18 20:04:47 +00:00
# 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:
2013-07-18 20:04:47 +00:00
queryset, search_use_distinct = self.get_search_results(
queryset, search_term)
if self.model_admin.list_filter:
queryset = self.build_list_filter(queryset).qs
2013-07-07 13:32:44 +00:00
queryset = self._modify_queryset_for_sort(queryset)
if search_use_distinct:
return queryset.distinct()
else:
return queryset
2013-07-07 13:32:44 +00:00
def _modify_queryset_for_sort(self, queryset):
# If we are sorting AND the field exists on the model
sort_by = self.request.GET.get('sort', None)
if sort_by:
# Special case when we are not explicityly displaying fields
if sort_by == '-__str__':
queryset = queryset[::-1]
try:
# If we sort on '-' remove it before looking for that field
field_exists = sort_by
if field_exists[0] == '-':
field_exists = field_exists[1:]
options = utils.model_options(self.model)
options.get_field(field_exists)
queryset = queryset.order_by(sort_by)
except FieldDoesNotExist:
# If the field does not exist then we dont sort on it
pass
return queryset
def build_list_filter(self, queryset=None):
if not hasattr(self, '_list_filter'):
if queryset is None:
queryset = self.get_queryset()
self._list_filter = build_list_filter(
self.request,
self.model_admin,
queryset,
)
return self._list_filter
2013-05-18 15:52:53 +00:00
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', '')
context['list_filter'] = self.build_list_filter()
2013-07-07 13:32:44 +00:00
context['sort_term'] = self.request.GET.get('sort', '')
2013-05-18 15:52:53 +00:00
return context
2013-05-19 08:09:12 +00:00
def get_success_url(self):
2013-07-18 20:04:47 +00:00
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
2013-05-17 22:15:45 +00:00
class ModelDetailView(AdminModel2Mixin, generic.DetailView):
2013-07-06 12:40:49 +00:00
"""Context Variables
:model: Type of object you are editing
:model_name: Name of the object you are editing
:app_label: Name of your app
"""
2013-05-18 14:40:18 +00:00
default_template_name = "model_detail.html"
permission_classes = (
permissions.IsStaffPermission,
permissions.ModelViewPermission)
2013-05-18 12:36:14 +00:00
2013-07-18 20:04:47 +00:00
class ModelEditFormView(AdminModel2Mixin, Admin2ModelFormMixin,
extra_views.UpdateWithInlinesView):
2013-07-06 12:40:49 +00:00
"""Context Variables
:model: Type of object you are editing
:model_name: Name of the object you are editing
:app_label: Name of your app
"""
2013-05-18 14:40:18 +00:00
form_class = None
default_template_name = "model_update_form.html"
permission_classes = (
permissions.IsStaffPermission,
permissions.ModelChangePermission)
2013-05-18 07:06:40 +00:00
def get_context_data(self, **kwargs):
context = super(ModelEditFormView, self).get_context_data(**kwargs)
context['model'] = self.get_model()
context['action'] = "Change"
context['action_name'] = ugettext_lazy("Change")
return context
2013-07-18 20:59:08 +00:00
def forms_valid(self, form, inlines):
response = super(ModelEditFormView, self).forms_valid(form, inlines)
2013-07-18 22:58:31 +00:00
LogEntry.objects.log_action(
self.request.user.id, self.object, LogEntry.CHANGE)
2013-07-18 20:59:08 +00:00
return response
2013-05-18 12:36:14 +00:00
2013-07-18 20:04:47 +00:00
class ModelAddFormView(AdminModel2Mixin, Admin2ModelFormMixin,
extra_views.CreateWithInlinesView):
2013-07-06 12:40:49 +00:00
"""Context Variables
:model: Type of object you are editing
:model_name: Name of the object you are editing
:app_label: Name of your app
"""
2013-05-18 12:36:14 +00:00
form_class = None
default_template_name = "model_update_form.html"
permission_classes = (
permissions.IsStaffPermission,
permissions.ModelAddPermission)
2013-05-18 12:36:14 +00:00
2013-05-20 13:06:28 +00:00
def get_context_data(self, **kwargs):
context = super(ModelAddFormView, self).get_context_data(**kwargs)
context['model'] = self.get_model()
context['action'] = "Add"
context['action_name'] = ugettext_lazy("Add")
2013-05-20 13:06:28 +00:00
return context
2013-07-18 20:59:08 +00:00
def forms_valid(self, form, inlines):
response = super(ModelAddFormView, self).forms_valid(form, inlines)
2013-07-18 22:58:31 +00:00
LogEntry.objects.log_action(
self.request.user.id, self.object, LogEntry.ADDITION)
2013-07-18 20:59:08 +00:00
return response
2013-05-17 22:15:45 +00:00
class ModelDeleteView(AdminModel2Mixin, generic.DeleteView):
2013-07-06 12:40:49 +00:00
"""Context Variables
:model: Type of object you are editing
:model_name: Name of the object you are editing
:app_label: Name of your app
:deletable_objects: Objects to delete
"""
success_url = "../../" # TODO - fix this!
2013-05-19 08:01:50 +00:00
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
2013-07-18 20:59:08 +00:00
def delete(self, request, *args, **kwargs):
2013-07-18 22:58:31 +00:00
LogEntry.objects.log_action(
request.user.id, self.get_object(), LogEntry.DELETION)
2013-07-18 20:59:08 +00:00
return super(ModelDeleteView, self).delete(request, *args, **kwargs)
2013-07-18 23:38:00 +00:00
class ModelHistoryView(Admin2Mixin, generic.ListView):
2013-07-18 23:36:31 +00:00
def get_context_data(self, **kwargs):
context = super(ModelHistoryView, self).get_context_data(**kwargs)
context['model'] = self.get_model()
return context
2013-05-31 00:39:58 +00:00
class PasswordChangeView(Admin2Mixin, generic.UpdateView):
default_template_name = 'auth/password_change_form.html'
form_class = AdminPasswordChangeForm
admin_form_class = PasswordChangeForm
model = get_user_model()
2013-05-31 01:25:45 +00:00
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()
2013-05-31 00:39:58 +00:00
class PasswordChangeDoneView(Admin2Mixin, generic.TemplateView):
default_template_name = 'auth/password_change_done.html'
2013-05-24 04:06:31 +00:00
class LoginView(Admin2Mixin, generic.TemplateView):
2013-07-06 12:40:49 +00:00
"""Context Variables
:site_name: Name of the site
"""
2013-05-24 04:06:31 +00:00
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)
2013-05-31 00:39:58 +00:00
class LogoutView(Admin2Mixin, generic.TemplateView):
2013-07-06 12:40:49 +00:00
"""Context Variables
:site_name: Name of the site
"""
default_template_name = 'auth/logout.html'
def get(self, request, *args, **kwargs):
2013-05-24 04:06:31 +00:00
return auth_logout(request, template_name=self.get_template_names(),
*args, **kwargs)