2013-07-06 16:26:29 +00:00
|
|
|
|
import operator
|
2013-07-31 18:58:08 +00:00
|
|
|
|
from datetime import datetime
|
2016-05-07 23:31:16 +00:00
|
|
|
|
from functools import reduce
|
2013-07-06 16:26:29 +00:00
|
|
|
|
|
2016-05-07 23:31:16 +00:00
|
|
|
|
import extra_views
|
2020-09-26 21:21:43 +00:00
|
|
|
|
from django.core.exceptions import FieldDoesNotExist
|
2014-09-22 02:14:02 +00:00
|
|
|
|
from django.conf import settings
|
2020-09-26 21:21:43 +00:00
|
|
|
|
from django.contrib.auth import (logout as auth_logout,
|
|
|
|
|
|
update_session_auth_hash)
|
2013-05-31 00:19:38 +00:00
|
|
|
|
from django.contrib.auth.forms import (PasswordChangeForm,
|
|
|
|
|
|
AdminPasswordChangeForm)
|
2020-09-26 21:21:43 +00:00
|
|
|
|
from django.contrib.auth.views import LoginView as DjangoLoginView
|
2013-07-18 23:51:16 +00:00
|
|
|
|
from django.contrib.contenttypes.models import ContentType
|
2015-03-21 15:44:11 +00:00
|
|
|
|
from django.db import models, router
|
2013-05-22 10:09:01 +00:00
|
|
|
|
from django.http import HttpResponseRedirect
|
2013-07-18 23:51:16 +00:00
|
|
|
|
from django.shortcuts import get_object_or_404
|
2020-09-26 21:21:43 +00:00
|
|
|
|
from django.utils.encoding import force_str
|
2013-05-25 14:52:58 +00:00
|
|
|
|
from django.utils.text import capfirst
|
2020-09-26 21:21:43 +00:00
|
|
|
|
from django.utils.translation import gettext_lazy
|
2018-05-10 19:23:23 +00:00
|
|
|
|
from django.urls import reverse, reverse_lazy
|
|
|
|
|
|
|
2013-05-18 13:43:44 +00:00
|
|
|
|
from django.views import generic
|
2013-06-06 18:00:03 +00:00
|
|
|
|
|
2013-05-29 13:09:26 +00:00
|
|
|
|
from . import permissions, utils
|
2013-07-30 20:28:24 +00:00
|
|
|
|
from .filters import build_list_filter, build_date_filter
|
2016-05-07 23:31:16 +00:00
|
|
|
|
from .forms import AdminAuthenticationForm
|
2015-01-08 07:41:41 +00:00
|
|
|
|
from .models import LogEntry
|
2016-05-07 23:31:16 +00:00
|
|
|
|
from .viewmixins import Admin2Mixin, Admin2ModelMixin, Admin2ModelFormMixin
|
2013-05-21 21:50:50 +00:00
|
|
|
|
|
2016-05-07 20:59:15 +00:00
|
|
|
|
|
2021-10-17 08:51:27 +00:00
|
|
|
|
class AdminView:
|
2013-06-21 03:05:23 +00:00
|
|
|
|
|
2013-06-22 04:30:09 +00:00
|
|
|
|
def __init__(self, url, view, name=None):
|
2013-06-21 03:05:23 +00:00
|
|
|
|
self.url = url
|
|
|
|
|
|
self.view = view
|
2013-06-22 04:30:09 +00:00
|
|
|
|
self.name = name
|
2013-06-21 03:05:23 +00:00
|
|
|
|
|
2013-06-27 02:22:52 +00:00
|
|
|
|
def get_view_kwargs(self):
|
|
|
|
|
|
return {
|
|
|
|
|
|
'app_label': self.model_admin.app_label,
|
|
|
|
|
|
'model': self.model_admin.model,
|
|
|
|
|
|
'model_name': self.model_admin.model_name,
|
|
|
|
|
|
'model_admin': self.model_admin,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-06-21 03:05:23 +00:00
|
|
|
|
|
2013-05-18 15:20:49 +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-09-24 07:27:38 +00:00
|
|
|
|
:app_verbose_names: A dictionary containing the app verbose names,
|
|
|
|
|
|
each item has a key being the `app_label` and
|
|
|
|
|
|
the value being a string, (or even a lazy
|
|
|
|
|
|
translation object), with the custom app name.
|
2013-07-04 12:07:32 +00:00
|
|
|
|
"""
|
2013-05-18 14:40:18 +00:00
|
|
|
|
default_template_name = "index.html"
|
2013-05-18 15:20:49 +00:00
|
|
|
|
registry = None
|
2013-05-19 11:03:25 +00:00
|
|
|
|
apps = None
|
2013-08-05 07:35:03 +00:00
|
|
|
|
app_verbose_names = None
|
2013-05-17 22:15:45 +00:00
|
|
|
|
|
2013-05-18 15:20:49 +00:00
|
|
|
|
def get_context_data(self, **kwargs):
|
2021-10-17 08:51:27 +00:00
|
|
|
|
data = super().get_context_data(**kwargs)
|
2013-05-18 15:20:49 +00:00
|
|
|
|
data.update({
|
2013-05-19 11:03:25 +00:00
|
|
|
|
'apps': self.apps,
|
2013-08-05 07:35:03 +00:00
|
|
|
|
'app_verbose_names': self.app_verbose_names,
|
2013-05-18 15:20:49 +00:00
|
|
|
|
})
|
|
|
|
|
|
return data
|
2013-05-17 22:15:45 +00:00
|
|
|
|
|
2013-05-19 07:47:42 +00:00
|
|
|
|
|
2013-05-26 00:55:43 +00:00
|
|
|
|
class AppIndexView(Admin2Mixin, generic.TemplateView):
|
2013-09-24 07:27:38 +00:00
|
|
|
|
"""Context Variables
|
|
|
|
|
|
|
|
|
|
|
|
:app_label: Name of your app
|
|
|
|
|
|
:registry: A dictionary of registered models for a given app, each
|
|
|
|
|
|
item has a key being the model and the value being
|
|
|
|
|
|
djadmin2.types.ModelAdmin2 objects.
|
|
|
|
|
|
:app_verbose_names: A dictionary containing the app verbose name for
|
|
|
|
|
|
a given app, the item has a key being the
|
|
|
|
|
|
`app_label` and the value being a string, (or
|
|
|
|
|
|
even a lazy translation object), with the custom
|
|
|
|
|
|
app name.
|
|
|
|
|
|
"""
|
2013-05-26 00:55:43 +00:00
|
|
|
|
default_template_name = "app_index.html"
|
|
|
|
|
|
registry = None
|
|
|
|
|
|
apps = None
|
2013-08-05 07:35:03 +00:00
|
|
|
|
app_verbose_names = None
|
2013-05-26 00:55:43 +00:00
|
|
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
2021-10-17 08:51:27 +00:00
|
|
|
|
data = super().get_context_data(**kwargs)
|
2013-05-26 00:55:43 +00:00
|
|
|
|
app_label = self.kwargs['app_label']
|
|
|
|
|
|
registry = self.apps[app_label]
|
|
|
|
|
|
data.update({
|
|
|
|
|
|
'app_label': app_label,
|
|
|
|
|
|
'registry': registry,
|
2013-08-05 07:35:03 +00:00
|
|
|
|
'app_verbose_names': self.app_verbose_names,
|
2013-05-26 00:55:43 +00:00
|
|
|
|
})
|
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-05-07 22:25:12 +00:00
|
|
|
|
class ModelListView(Admin2ModelMixin, 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-09-24 07:27:38 +00:00
|
|
|
|
:app_verbose_names: A dictionary containing the app verbose name for
|
|
|
|
|
|
a given app, the item has a key being the
|
|
|
|
|
|
`app_label` and the value being a string, (or
|
|
|
|
|
|
even a lazy translation object), with the custom
|
|
|
|
|
|
app name.
|
2013-07-06 12:40:49 +00:00
|
|
|
|
"""
|
2013-05-18 14:40:18 +00:00
|
|
|
|
default_template_name = "model_list.html"
|
2018-09-17 07:58:30 +00:00
|
|
|
|
paginate_by = 10
|
2013-05-22 23:21:01 +00:00
|
|
|
|
permission_classes = (
|
|
|
|
|
|
permissions.IsStaffPermission,
|
|
|
|
|
|
permissions.ModelViewPermission)
|
2013-05-17 22:15:45 +00:00
|
|
|
|
|
2013-05-22 10:09:01 +00:00
|
|
|
|
def post(self, request):
|
|
|
|
|
|
action_name = request.POST['action']
|
2013-06-04 18:18:45 +00:00
|
|
|
|
action_callable = self.get_actions()[action_name]['action_callable']
|
2013-05-24 11:06:35 +00:00
|
|
|
|
selected_model_pks = request.POST.getlist('selected_model_pk')
|
2013-08-02 17:58:57 +00:00
|
|
|
|
if getattr(action_callable, "only_selected", True):
|
|
|
|
|
|
queryset = self.model.objects.filter(pk__in=selected_model_pks)
|
|
|
|
|
|
else:
|
|
|
|
|
|
queryset = self.model.objects.all()
|
2013-06-04 18:18:45 +00:00
|
|
|
|
|
2013-07-18 20:04:47 +00:00
|
|
|
|
# If action_callable is a class subclassing from
|
|
|
|
|
|
# actions.BaseListAction then we generate the callable object.
|
2013-06-14 13:58:27 +00:00
|
|
|
|
if hasattr(action_callable, "process_queryset"):
|
2013-08-05 07:35:03 +00:00
|
|
|
|
response = action_callable.as_view(queryset=queryset, model_admin=self.model_admin)(request)
|
2013-06-04 18:18:45 +00:00
|
|
|
|
else:
|
|
|
|
|
|
# generate the reponse if a function.
|
|
|
|
|
|
response = action_callable(request, queryset)
|
2013-06-14 13:58:27 +00:00
|
|
|
|
|
2013-05-22 10:09:01 +00:00
|
|
|
|
if response is None:
|
|
|
|
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
|
|
|
|
else:
|
|
|
|
|
|
return response
|
|
|
|
|
|
|
2013-06-06 18:00:03 +00:00
|
|
|
|
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):
|
2021-10-17 08:51:27 +00:00
|
|
|
|
queryset = super().get_queryset()
|
2013-06-06 18:00:03 +00:00
|
|
|
|
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)
|
2013-06-06 18:00:03 +00:00
|
|
|
|
|
2013-08-01 16:00:03 +00:00
|
|
|
|
queryset = self._modify_queryset_for_ordering(queryset)
|
|
|
|
|
|
|
2013-07-06 13:47:09 +00:00
|
|
|
|
if self.model_admin.list_filter:
|
|
|
|
|
|
queryset = self.build_list_filter(queryset).qs
|
|
|
|
|
|
|
2013-07-30 20:28:24 +00:00
|
|
|
|
if self.model_admin.date_hierarchy:
|
2016-05-07 19:09:58 +00:00
|
|
|
|
queryset = self.build_date_filter(queryset, self.model_admin.date_hierarchy).qs
|
2013-07-30 20:28:24 +00:00
|
|
|
|
|
2013-07-07 13:32:44 +00:00
|
|
|
|
queryset = self._modify_queryset_for_sort(queryset)
|
2013-06-06 18:00:03 +00:00
|
|
|
|
|
|
|
|
|
|
if search_use_distinct:
|
|
|
|
|
|
return queryset.distinct()
|
|
|
|
|
|
else:
|
|
|
|
|
|
return queryset
|
|
|
|
|
|
|
2013-08-01 16:00:03 +00:00
|
|
|
|
def _modify_queryset_for_ordering(self, queryset):
|
|
|
|
|
|
ordering = self.model_admin.get_ordering(self.request)
|
|
|
|
|
|
if ordering:
|
|
|
|
|
|
queryset = queryset.order_by(*ordering)
|
|
|
|
|
|
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
|
|
|
|
|
|
|
2013-07-06 13:47:09 +00:00
|
|
|
|
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
|
|
|
|
|
|
|
2016-05-07 19:09:58 +00:00
|
|
|
|
def build_date_filter(self, queryset=None, field_name=None):
|
2013-07-30 20:28:24 +00:00
|
|
|
|
if not hasattr(self, "_date_filter"):
|
|
|
|
|
|
if queryset is None:
|
|
|
|
|
|
queryset = self.get_queryset()
|
|
|
|
|
|
self._date_filter = build_date_filter(
|
|
|
|
|
|
self.request,
|
|
|
|
|
|
self.model_admin,
|
|
|
|
|
|
queryset,
|
2016-05-07 19:09:58 +00:00
|
|
|
|
field_name
|
2013-07-30 20:28:24 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
return self._date_filter
|
|
|
|
|
|
|
2013-05-18 15:52:53 +00:00
|
|
|
|
def get_context_data(self, **kwargs):
|
2021-10-17 08:51:27 +00:00
|
|
|
|
context = super().get_context_data(**kwargs)
|
2013-05-23 18:11:04 +00:00
|
|
|
|
context['model'] = self.get_model()
|
2013-05-22 10:09:01 +00:00
|
|
|
|
context['actions'] = self.get_actions().values()
|
2013-06-06 18:00:03 +00:00
|
|
|
|
context['search_fields'] = self.get_search_fields()
|
|
|
|
|
|
context['search_term'] = self.request.GET.get('q', '')
|
2013-07-06 13:47:09 +00:00
|
|
|
|
context['list_filter'] = self.build_list_filter()
|
2013-07-07 13:32:44 +00:00
|
|
|
|
context['sort_term'] = self.request.GET.get('sort', '')
|
2013-07-30 20:28:24 +00:00
|
|
|
|
|
|
|
|
|
|
if self.model_admin.date_hierarchy:
|
2013-07-31 16:57:48 +00:00
|
|
|
|
year = self.request.GET.get("year", False)
|
|
|
|
|
|
month = self.request.GET.get("month", False)
|
|
|
|
|
|
day = self.request.GET.get("day", False)
|
|
|
|
|
|
|
|
|
|
|
|
if year and month and day:
|
2013-07-31 18:58:08 +00:00
|
|
|
|
new_date = datetime.strptime(
|
|
|
|
|
|
"%s %s %s" % (month, day, year),
|
|
|
|
|
|
"%m %d %Y",
|
|
|
|
|
|
)
|
|
|
|
|
|
context["previous_date"] = {
|
|
|
|
|
|
"link": "?year=%s&month=%s" % (year, month),
|
|
|
|
|
|
"text": "‹ %s" % new_date.strftime("%B %Y")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
context["active_day"] = new_date.strftime("%B %d")
|
|
|
|
|
|
|
2016-05-07 19:07:51 +00:00
|
|
|
|
context["dates"] = self._format_days(self.get_queryset())
|
2013-07-31 16:57:48 +00:00
|
|
|
|
elif year and month:
|
2013-07-31 18:58:08 +00:00
|
|
|
|
context["previous_date"] = {
|
|
|
|
|
|
"link": "?year=%s" % (year),
|
|
|
|
|
|
"text": "‹ %s" % year,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-05-07 19:07:51 +00:00
|
|
|
|
context["dates"] = self._format_days(self.get_queryset())
|
2013-07-31 16:57:48 +00:00
|
|
|
|
elif year:
|
2013-07-31 18:58:08 +00:00
|
|
|
|
context["previous_date"] = {
|
|
|
|
|
|
"link": "?",
|
2020-09-26 21:21:43 +00:00
|
|
|
|
"text": gettext_lazy("‹ All dates"),
|
2013-07-31 18:58:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-05-07 19:07:51 +00:00
|
|
|
|
context["dates"] = self._format_months(self.get_queryset())
|
2013-07-31 16:57:48 +00:00
|
|
|
|
else:
|
2016-05-07 19:07:51 +00:00
|
|
|
|
context["dates"] = self._format_years(self.get_queryset())
|
2013-07-31 16:57:48 +00:00
|
|
|
|
|
2013-05-18 15:52:53 +00:00
|
|
|
|
return context
|
2013-05-19 08:09:12 +00:00
|
|
|
|
|
2016-05-07 19:07:51 +00:00
|
|
|
|
def _format_years(self, queryset):
|
|
|
|
|
|
years = self._qs_date_or_datetime(queryset, 'year')
|
2013-07-31 21:43:05 +00:00
|
|
|
|
if len(years) == 1:
|
2016-05-07 19:07:51 +00:00
|
|
|
|
return self._format_months(queryset)
|
2013-07-31 21:43:05 +00:00
|
|
|
|
else:
|
|
|
|
|
|
return [
|
|
|
|
|
|
(("?year=%s" % year.strftime("%Y")), year.strftime("%Y"))
|
|
|
|
|
|
for year in
|
2016-05-07 19:07:51 +00:00
|
|
|
|
self._qs_date_or_datetime(queryset, 'year')
|
2013-07-31 21:43:05 +00:00
|
|
|
|
]
|
2013-07-31 16:57:48 +00:00
|
|
|
|
|
2016-05-07 19:07:51 +00:00
|
|
|
|
def _format_months(self, queryset):
|
2013-07-31 16:57:48 +00:00
|
|
|
|
return [
|
|
|
|
|
|
(
|
|
|
|
|
|
"?year=%s&month=%s" % (
|
|
|
|
|
|
date.strftime("%Y"), date.strftime("%m")
|
|
|
|
|
|
),
|
|
|
|
|
|
date.strftime("%B %Y")
|
|
|
|
|
|
) for date in
|
2016-05-07 19:07:51 +00:00
|
|
|
|
self._qs_date_or_datetime(queryset, 'month')
|
2013-07-31 16:57:48 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
2016-05-07 19:07:51 +00:00
|
|
|
|
def _format_days(self, queryset):
|
2013-07-31 16:57:48 +00:00
|
|
|
|
return [
|
|
|
|
|
|
(
|
|
|
|
|
|
"?year=%s&month=%s&day=%s" % (
|
|
|
|
|
|
date.strftime("%Y"),
|
|
|
|
|
|
date.strftime("%m"),
|
|
|
|
|
|
date.strftime("%d"),
|
|
|
|
|
|
),
|
|
|
|
|
|
date.strftime("%B %d")
|
|
|
|
|
|
) for date in
|
2016-05-07 19:07:51 +00:00
|
|
|
|
self._qs_date_or_datetime(queryset, 'day')
|
2013-07-31 16:57:48 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
2014-09-22 02:14:02 +00:00
|
|
|
|
def _qs_date_or_datetime(self, object_list, type):
|
|
|
|
|
|
if isinstance(self.model._meta.get_field(self.model_admin.date_hierarchy), models.DateTimeField):
|
|
|
|
|
|
qs = object_list.datetimes(self.model_admin.date_hierarchy, type)
|
|
|
|
|
|
else:
|
|
|
|
|
|
qs = object_list.dates(self.model_admin.date_hierarchy, type)
|
|
|
|
|
|
return qs
|
|
|
|
|
|
|
2013-05-22 10:09:01 +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)
|
2013-05-22 10:09:01 +00:00
|
|
|
|
return reverse(view_name)
|
|
|
|
|
|
|
|
|
|
|
|
def get_actions(self):
|
2013-05-31 13:37:55 +00:00
|
|
|
|
return self.model_admin.get_list_actions()
|
2013-05-22 10:09:01 +00:00
|
|
|
|
|
2013-06-06 18:00:03 +00:00
|
|
|
|
def get_search_fields(self):
|
|
|
|
|
|
return self.model_admin.search_fields
|
|
|
|
|
|
|
2013-05-17 22:15:45 +00:00
|
|
|
|
|
2016-05-07 22:25:12 +00:00
|
|
|
|
class ModelDetailView(Admin2ModelMixin, 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-09-24 07:27:38 +00:00
|
|
|
|
:app_verbose_names: A dictionary containing the app verbose name for
|
|
|
|
|
|
a given app, the item has a key being the
|
|
|
|
|
|
`app_label` and the value being a string, (or
|
|
|
|
|
|
even a lazy translation object), with the custom
|
|
|
|
|
|
app name.
|
2013-07-06 12:40:49 +00:00
|
|
|
|
"""
|
2013-05-18 14:40:18 +00:00
|
|
|
|
default_template_name = "model_detail.html"
|
2013-05-22 23:21:01 +00:00
|
|
|
|
permission_classes = (
|
|
|
|
|
|
permissions.IsStaffPermission,
|
|
|
|
|
|
permissions.ModelViewPermission)
|
2013-05-18 13:43:44 +00:00
|
|
|
|
|
2013-05-18 12:36:14 +00:00
|
|
|
|
|
2016-05-07 22:25:12 +00:00
|
|
|
|
class ModelEditFormView(Admin2ModelMixin, Admin2ModelFormMixin,
|
2013-07-18 20:04:47 +00:00
|
|
|
|
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-09-24 07:27:38 +00:00
|
|
|
|
:app_verbose_names: A dictionary containing the app verbose name for
|
|
|
|
|
|
a given app, the item has a key being the
|
|
|
|
|
|
`app_label` and the value being a string, (or
|
|
|
|
|
|
even a lazy translation object), with the custom
|
|
|
|
|
|
app name.
|
2013-07-06 12:40:49 +00:00
|
|
|
|
"""
|
2013-05-18 14:40:18 +00:00
|
|
|
|
form_class = None
|
2013-05-21 22:45:08 +00:00
|
|
|
|
default_template_name = "model_update_form.html"
|
2013-05-22 23:21:01 +00:00
|
|
|
|
permission_classes = (
|
|
|
|
|
|
permissions.IsStaffPermission,
|
|
|
|
|
|
permissions.ModelChangePermission)
|
2013-05-18 07:06:40 +00:00
|
|
|
|
|
2013-05-21 22:45:08 +00:00
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
|
|
context = super(ModelEditFormView, self).get_context_data(**kwargs)
|
2013-05-23 18:11:04 +00:00
|
|
|
|
context['model'] = self.get_model()
|
2013-05-21 22:45:08 +00:00
|
|
|
|
context['action'] = "Change"
|
2020-09-26 21:21:43 +00:00
|
|
|
|
context['action_name'] = gettext_lazy("Change")
|
2013-05-21 22:45:08 +00:00
|
|
|
|
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(
|
2013-07-19 00:23:48 +00:00
|
|
|
|
self.request.user.id,
|
|
|
|
|
|
self.object,
|
|
|
|
|
|
LogEntry.CHANGE,
|
|
|
|
|
|
self.construct_change_message(self.request, form, inlines))
|
2013-07-18 20:59:08 +00:00
|
|
|
|
return response
|
|
|
|
|
|
|
2013-05-18 12:36:14 +00:00
|
|
|
|
|
2016-05-07 22:25:12 +00:00
|
|
|
|
class ModelAddFormView(Admin2ModelMixin, Admin2ModelFormMixin,
|
2013-07-18 20:04:47 +00:00
|
|
|
|
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-09-24 07:27:38 +00:00
|
|
|
|
:app_verbose_names: A dictionary containing the app verbose name for
|
|
|
|
|
|
a given app, the item has a key being the
|
|
|
|
|
|
`app_label` and the value being a string, (or
|
|
|
|
|
|
even a lazy translation object), with the custom
|
|
|
|
|
|
app name.
|
2013-07-06 12:40:49 +00:00
|
|
|
|
"""
|
2013-05-18 12:36:14 +00:00
|
|
|
|
form_class = None
|
2013-05-21 22:45:08 +00:00
|
|
|
|
default_template_name = "model_update_form.html"
|
2013-05-22 23:21:01 +00:00
|
|
|
|
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):
|
2021-10-17 08:51:27 +00:00
|
|
|
|
context = super().get_context_data(**kwargs)
|
2013-05-23 18:11:04 +00:00
|
|
|
|
context['model'] = self.get_model()
|
2013-05-21 22:45:08 +00:00
|
|
|
|
context['action'] = "Add"
|
2020-09-26 21:21:43 +00:00
|
|
|
|
context['action_name'] = gettext_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):
|
2021-10-17 08:51:27 +00:00
|
|
|
|
response = super().forms_valid(form, inlines)
|
2013-07-18 22:58:31 +00:00
|
|
|
|
LogEntry.objects.log_action(
|
2013-07-19 00:23:48 +00:00
|
|
|
|
self.request.user.id,
|
|
|
|
|
|
self.object,
|
|
|
|
|
|
LogEntry.ADDITION,
|
|
|
|
|
|
'Object created.')
|
2013-07-18 20:59:08 +00:00
|
|
|
|
return response
|
|
|
|
|
|
|
2013-05-17 22:15:45 +00:00
|
|
|
|
|
2016-05-07 22:25:12 +00:00
|
|
|
|
class ModelDeleteView(Admin2ModelMixin, 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
|
2013-09-24 07:27:38 +00:00
|
|
|
|
:app_verbose_names: A dictionary containing the app verbose name for
|
|
|
|
|
|
a given app, the item has a key being the
|
|
|
|
|
|
`app_label` and the value being a string, (or
|
|
|
|
|
|
even a lazy translation object), with the custom
|
|
|
|
|
|
app name.
|
2013-07-06 12:40:49 +00:00
|
|
|
|
"""
|
2013-05-23 18:11:04 +00:00
|
|
|
|
success_url = "../../" # TODO - fix this!
|
2013-05-19 08:01:50 +00:00
|
|
|
|
default_template_name = "model_confirm_delete.html"
|
2013-05-22 23:21:01 +00:00
|
|
|
|
permission_classes = (
|
|
|
|
|
|
permissions.IsStaffPermission,
|
|
|
|
|
|
permissions.ModelDeletePermission)
|
2013-05-25 14:52:58 +00:00
|
|
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
2021-10-17 08:51:27 +00:00
|
|
|
|
context = super().get_context_data(**kwargs)
|
2013-05-25 14:52:58 +00:00
|
|
|
|
|
|
|
|
|
|
def _format_callback(obj):
|
2013-05-29 13:09:26 +00:00
|
|
|
|
opts = utils.model_options(obj)
|
2020-09-26 21:21:43 +00:00
|
|
|
|
return '%s: %s' % (force_str(capfirst(opts.verbose_name)),
|
|
|
|
|
|
force_str(obj))
|
2016-05-07 20:59:15 +00:00
|
|
|
|
|
2015-03-21 15:44:11 +00:00
|
|
|
|
using = router.db_for_write(self.get_object()._meta.model)
|
|
|
|
|
|
collector = utils.NestedObjects(using=using)
|
2013-05-25 14:52:58 +00:00
|
|
|
|
collector.collect([self.get_object()])
|
|
|
|
|
|
context.update({
|
|
|
|
|
|
'deletable_objects': collector.nested(_format_callback)
|
|
|
|
|
|
})
|
|
|
|
|
|
return context
|
2013-05-22 10:10:22 +00:00
|
|
|
|
|
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(
|
2013-07-19 00:23:48 +00:00
|
|
|
|
request.user.id,
|
|
|
|
|
|
self.get_object(),
|
|
|
|
|
|
LogEntry.DELETION,
|
|
|
|
|
|
'Object deleted.')
|
2021-10-17 08:51:27 +00:00
|
|
|
|
return super().delete(request, *args, **kwargs)
|
2013-07-18 20:59:08 +00:00
|
|
|
|
|
2013-05-22 10:10:22 +00:00
|
|
|
|
|
2016-05-07 22:25:12 +00:00
|
|
|
|
class ModelHistoryView(Admin2ModelMixin, generic.ListView):
|
2013-09-24 07:27:38 +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
|
|
|
|
|
|
:app_verbose_names: A dictionary containing the app verbose name for
|
|
|
|
|
|
a given app, the item has a key being the
|
|
|
|
|
|
`app_label` and the value being a string, (or
|
|
|
|
|
|
even a lazy translation object), with the custom
|
|
|
|
|
|
app name.
|
|
|
|
|
|
"""
|
2013-07-18 23:51:16 +00:00
|
|
|
|
default_template_name = "model_history.html"
|
2018-09-17 07:58:30 +00:00
|
|
|
|
paginate_by = 10
|
2013-07-18 23:51:16 +00:00
|
|
|
|
permission_classes = (
|
|
|
|
|
|
permissions.IsStaffPermission,
|
|
|
|
|
|
permissions.ModelChangePermission
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2013-07-18 23:36:31 +00:00
|
|
|
|
def get_context_data(self, **kwargs):
|
2021-10-17 08:51:27 +00:00
|
|
|
|
context = super().get_context_data(**kwargs)
|
2013-07-18 23:36:31 +00:00
|
|
|
|
context['model'] = self.get_model()
|
2013-07-18 23:51:16 +00:00
|
|
|
|
context['object'] = self.get_object()
|
2013-07-18 23:36:31 +00:00
|
|
|
|
return context
|
|
|
|
|
|
|
2013-07-18 23:51:16 +00:00
|
|
|
|
def get_object(self):
|
|
|
|
|
|
return get_object_or_404(self.get_model(), pk=self.kwargs.get('pk'))
|
|
|
|
|
|
|
|
|
|
|
|
def get_queryset(self):
|
|
|
|
|
|
content_type = ContentType.objects.get_for_model(self.get_object())
|
|
|
|
|
|
return LogEntry.objects.filter(
|
|
|
|
|
|
content_type=content_type,
|
|
|
|
|
|
object_id=self.get_object().id
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2013-05-22 10:10:22 +00:00
|
|
|
|
|
2013-05-31 00:39:58 +00:00
|
|
|
|
class PasswordChangeView(Admin2Mixin, generic.UpdateView):
|
2013-05-22 10:10:22 +00:00
|
|
|
|
|
|
|
|
|
|
default_template_name = 'auth/password_change_form.html'
|
2013-05-31 00:19:38 +00:00
|
|
|
|
form_class = AdminPasswordChangeForm
|
|
|
|
|
|
admin_form_class = PasswordChangeForm
|
2014-09-22 02:14:02 +00:00
|
|
|
|
model = settings.AUTH_USER_MODEL
|
2013-05-31 01:25:45 +00:00
|
|
|
|
success_url = reverse_lazy('admin2:password_change_done')
|
2013-05-22 10:10:22 +00:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
2013-05-31 00:19:38 +00:00
|
|
|
|
def get_form_class(self):
|
|
|
|
|
|
if self.request.user == self.get_object():
|
|
|
|
|
|
return self.admin_form_class
|
2021-10-17 08:51:27 +00:00
|
|
|
|
return super().get_form_class()
|
2013-05-22 10:10:22 +00:00
|
|
|
|
|
2014-09-22 02:14:02 +00:00
|
|
|
|
def get_queryset(self):
|
|
|
|
|
|
from django.contrib.auth import get_user_model
|
|
|
|
|
|
return get_user_model()._default_manager.all()
|
2013-05-22 10:10:22 +00:00
|
|
|
|
|
2016-05-22 08:49:09 +00:00
|
|
|
|
def form_valid(self, form):
|
|
|
|
|
|
self.object = form.save()
|
2016-05-22 09:45:08 +00:00
|
|
|
|
if self.request.user == self.get_object():
|
|
|
|
|
|
update_session_auth_hash(self.request, form.user)
|
2016-05-22 08:49:09 +00:00
|
|
|
|
return HttpResponseRedirect(self.get_success_url())
|
2016-05-07 20:59:15 +00:00
|
|
|
|
|
2016-05-22 08:52:43 +00:00
|
|
|
|
|
2013-05-31 00:39:58 +00:00
|
|
|
|
class PasswordChangeDoneView(Admin2Mixin, generic.TemplateView):
|
2013-05-22 10:10:22 +00:00
|
|
|
|
|
|
|
|
|
|
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):
|
2020-09-26 21:21:43 +00:00
|
|
|
|
return DjangoLoginView.as_view(template_name=self.get_templates(), authentication_form=self.authentication_form, *args, **kwargs)(request)
|
2013-05-24 04:06:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
"""
|
2013-05-22 10:10:22 +00:00
|
|
|
|
|
|
|
|
|
|
default_template_name = 'auth/logout.html'
|
|
|
|
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
2020-09-26 21:21:43 +00:00
|
|
|
|
auth_logout(request)
|
|
|
|
|
|
context = self.get_context_data(**kwargs)
|
|
|
|
|
|
return self.render_to_response(context)
|