reservations: Make ReservationAdmin more useful for subclasses
This commit is contained in:
parent
927e2f4b90
commit
d792efc084
@ -1,5 +1,6 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.humanize.templatetags.humanize import naturaltime
|
from django.contrib.humanize.templatetags.humanize import naturaltime
|
||||||
|
from django.http import HttpRequest
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
|
||||||
from django_object_actions import (
|
from django_object_actions import (
|
||||||
@ -223,3 +224,9 @@ class EventAdmin(DjangoObjectActions, admin.ModelAdmin):
|
|||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(EventMeetingTime)
|
||||||
|
class EventMeetingTimeAdmin(admin.ModelAdmin):
|
||||||
|
def has_module_permission(self, request: HttpRequest) -> bool:
|
||||||
|
return False
|
||||||
|
@ -626,6 +626,9 @@ class EventMeetingTime(Reservation):
|
|||||||
EventExt, on_delete=models.CASCADE, related_name="meeting_times"
|
EventExt, on_delete=models.CASCADE, related_name="meeting_times"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_title(self) -> str:
|
||||||
|
return self.event.unescaped_title
|
||||||
|
|
||||||
# TODO: should probably do some validation in python to enforce
|
# TODO: should probably do some validation in python to enforce
|
||||||
# - uniqueness and non-overlapping (per event)
|
# - uniqueness and non-overlapping (per event)
|
||||||
# - min/max start/end time == event start end
|
# - min/max start/end time == event start end
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
from django.contrib import admin
|
from typing import Any
|
||||||
|
|
||||||
from .models import Reservation, Resource, UserReservation
|
from django.contrib import admin
|
||||||
|
from django.contrib.admin.views.main import ChangeList
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import QuerySet
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from .models import ExternalReservation, Reservation, Resource, UserReservation
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Resource)
|
@admin.register(Resource)
|
||||||
@ -11,18 +18,83 @@ class ResourceAdmin(admin.ModelAdmin):
|
|||||||
show_facets = admin.ShowFacets.ALWAYS
|
show_facets = admin.ShowFacets.ALWAYS
|
||||||
|
|
||||||
|
|
||||||
|
class ReservationTypeFilter(admin.SimpleListFilter):
|
||||||
|
"""Filter by subclass"""
|
||||||
|
|
||||||
|
title = "Reservation Type"
|
||||||
|
parameter_name = "type"
|
||||||
|
|
||||||
|
# TODO: this could be automatic
|
||||||
|
_subtypes = {
|
||||||
|
"eventmeetingtime": "Event Meeting Time",
|
||||||
|
"userreservation": "User Reservation",
|
||||||
|
"externalreservation": "External Reservation",
|
||||||
|
}
|
||||||
|
|
||||||
|
def lookups(
|
||||||
|
self, request: HttpRequest, model_admin: admin.ModelAdmin
|
||||||
|
) -> list[tuple[str, str]]:
|
||||||
|
return [
|
||||||
|
("generic", "Generic Reservation"),
|
||||||
|
] + list(self._subtypes.items())
|
||||||
|
|
||||||
|
def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet:
|
||||||
|
if self.value() in self._subtypes:
|
||||||
|
return queryset.filter(**{f"{self.value()}__isnull": False})
|
||||||
|
elif self.value() == "generic":
|
||||||
|
return queryset.filter(
|
||||||
|
**{f"{subtype}__isnull": True for subtype in self._subtypes}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class SubclassChangeList(ChangeList):
|
||||||
|
def url_for_result(self, result: models.Model) -> str:
|
||||||
|
opts = type(result)._meta
|
||||||
|
return reverse(
|
||||||
|
f"admin:{opts.app_label}_{opts.model_name}_change",
|
||||||
|
args=(result.pk,),
|
||||||
|
current_app=self.model_admin.admin_site.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Reservation)
|
@admin.register(Reservation)
|
||||||
class ReservationAdmin(admin.ModelAdmin):
|
class ReservationAdmin(admin.ModelAdmin):
|
||||||
list_display = ["_resources", "start", "end"]
|
list_display = ["_title", "_resources", "start", "end"]
|
||||||
readonly_fields = ["google_calendar_event_id"]
|
readonly_fields = ["google_calendar_event_id"]
|
||||||
list_filter = ["resources"]
|
list_filter = ["resources", ReservationTypeFilter]
|
||||||
show_facets = admin.ShowFacets.ALWAYS
|
show_facets = admin.ShowFacets.ALWAYS
|
||||||
|
date_hierarchy = "start"
|
||||||
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
return (
|
||||||
|
super()
|
||||||
|
.get_queryset(request)
|
||||||
|
.select_related("eventmeetingtime__event")
|
||||||
|
.prefetch_related("resources")
|
||||||
|
.select_subclasses()
|
||||||
|
)
|
||||||
|
|
||||||
|
@admin.display()
|
||||||
|
def _title(self, obj: Reservation):
|
||||||
|
return obj.get_title()
|
||||||
|
|
||||||
@admin.display()
|
@admin.display()
|
||||||
def _resources(self, obj: Reservation):
|
def _resources(self, obj: Reservation):
|
||||||
return list(obj.resources.all()) or None
|
return list(obj.resources.all()) or None
|
||||||
|
|
||||||
|
def get_changelist(self, request: HttpRequest, **kwargs: Any) -> type[ChangeList]:
|
||||||
|
return SubclassChangeList
|
||||||
|
|
||||||
|
|
||||||
@admin.register(UserReservation)
|
@admin.register(UserReservation)
|
||||||
class UserReservationAdmin(ReservationAdmin):
|
class UserReservationAdmin(admin.ModelAdmin):
|
||||||
list_display = ["_resources", "user", "start", "end"]
|
def has_module_permission(self, request: HttpRequest) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ExternalReservation)
|
||||||
|
class ExternalReservationAdmin(admin.ModelAdmin):
|
||||||
|
def has_module_permission(self, request: HttpRequest) -> bool:
|
||||||
|
return False
|
||||||
|
@ -98,6 +98,9 @@ class Reservation(models.Model):
|
|||||||
resources = ", ".join(str(resource) for resource in self.resources.all())
|
resources = ", ".join(str(resource) for resource in self.resources.all())
|
||||||
return f"{resources}: {self.start} - {self.end}"
|
return f"{resources}: {self.start} - {self.end}"
|
||||||
|
|
||||||
|
def get_title(self) -> str:
|
||||||
|
return "Unknown Reservation"
|
||||||
|
|
||||||
def make_google_calendar_event(self):
|
def make_google_calendar_event(self):
|
||||||
event = {
|
event = {
|
||||||
"summary": "CMSManage Reservation",
|
"summary": "CMSManage Reservation",
|
||||||
@ -128,6 +131,9 @@ class UserReservation(Reservation):
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.user} | {super().__str__()}"
|
return f"{self.user} | {super().__str__()}"
|
||||||
|
|
||||||
|
def get_title(self) -> str:
|
||||||
|
return str(self.user)
|
||||||
|
|
||||||
def make_google_calendar_event(self):
|
def make_google_calendar_event(self):
|
||||||
return super().make_google_calendar_event() | {
|
return super().make_google_calendar_event() | {
|
||||||
"summary": str(self.user),
|
"summary": str(self.user),
|
||||||
@ -142,6 +148,9 @@ class ExternalReservation(Reservation):
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f'External "{self.title}" | {super().__str__()}'
|
return f'External "{self.title}" | {super().__str__()}'
|
||||||
|
|
||||||
|
def get_title(self) -> str:
|
||||||
|
return str(self.title)
|
||||||
|
|
||||||
def make_google_calendar_event(self):
|
def make_google_calendar_event(self):
|
||||||
"""This should never be called, as these are reservations from Google Calendar and shouldn't be synced back"""
|
"""This should never be called, as these are reservations from Google Calendar and shouldn't be synced back"""
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
|
Loading…
Reference in New Issue
Block a user