From 85b72294f3ea7d038afcd3464d661324f16d8c15 Mon Sep 17 00:00:00 2001 From: Tuomas Suutari Date: Mon, 23 Sep 2019 10:08:23 +0300 Subject: [PATCH] Improve model admins Make the following improvements to the Django Admin parts in the parkings app: * Add PermitSeriesAdmin for browsing permit series * Tune ordering of the items * Add new fields to the list displays * Add date hierarchy to ParkingAdmin and PermitAdmin * Add area fields to the ParkingAreaAdmin, PaymentZoneAdmin, PermitAreaAdmin and RegionAdmin to display their area size. * Improve string presentation of Permit objects. * Add verbose name for PermitSeries model. --- parkings/admin.py | 51 +++++++++++++++++++++++------ parkings/admin_utils.py | 10 ++++++ parkings/migrations/0019_permits.py | 2 ++ parkings/models/permit.py | 8 ++++- 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/parkings/admin.py b/parkings/admin.py index ed04e411..2873bdf4 100644 --- a/parkings/admin.py +++ b/parkings/admin.py @@ -1,44 +1,53 @@ from django.contrib import admin from django.contrib.gis.admin import OSMGeoAdmin -from .admin_utils import ReadOnlyAdmin +from .admin_utils import ReadOnlyAdmin, WithAreaField from .models import ( Operator, Parking, ParkingArea, ParkingCheck, ParkingTerminal, PaymentZone, - Permit, PermitArea, PermitLookupItem, Region) + Permit, PermitArea, PermitLookupItem, PermitSeries, Region) @admin.register(Operator) class OperatorAdmin(admin.ModelAdmin): - pass + list_display = ['name', 'user'] @admin.register(PaymentZone) -class PaymentZoneAdmin(OSMGeoAdmin): +class PaymentZoneAdmin(WithAreaField, OSMGeoAdmin): + list_display = ['id', 'number', 'name', 'area'] ordering = ('number',) @admin.register(Parking) class ParkingAdmin(OSMGeoAdmin): + date_hierarchy = 'time_start' list_display = [ 'id', 'operator', 'zone', 'parking_area', 'terminal_number', 'time_start', 'time_end', 'registration_number', 'created_at', 'modified_at'] list_filter = ['operator', 'zone'] - ordering = ('-created_at',) + ordering = ('-time_start',) @admin.register(Region) -class RegionAdmin(OSMGeoAdmin): +class RegionAdmin(WithAreaField, OSMGeoAdmin): + list_display = ['id', 'name', 'capacity_estimate', 'area'] ordering = ('name',) @admin.register(ParkingArea) -class ParkingAreaAdmin(OSMGeoAdmin): +class ParkingAreaAdmin(WithAreaField, OSMGeoAdmin): + area_scale = 1 + list_display = ['id', 'origin_id', 'capacity_estimate', 'area'] ordering = ('origin_id',) @admin.register(ParkingCheck) class ParkingCheckAdmin(ReadOnlyAdmin, OSMGeoAdmin): + list_display = [ + 'id', 'time', 'registration_number', 'location', + 'allowed', 'result', 'performer', 'created_at'] + modifiable = False def get_readonly_fields(self, request, obj=None): @@ -61,14 +70,36 @@ class ParkingTerminalAdmin(OSMGeoAdmin): @admin.register(Permit) class PermitAdmin(admin.ModelAdmin): - pass + date_hierarchy = 'created_at' + list_display = ['id', 'series', 'external_id', 'created_at', 'modified_at'] + list_filter = ['series__active'] + ordering = ('-series', '-id') @admin.register(PermitArea) -class PermitAreaAdmin(OSMGeoAdmin): +class PermitAreaAdmin(WithAreaField, OSMGeoAdmin): + list_display = ['id', 'identifier', 'name', 'area'] ordering = ('identifier',) @admin.register(PermitLookupItem) class PermitLookupItemAdmin(ReadOnlyAdmin): - pass + list_display = [ + 'id', 'series', 'permit', + 'registration_number', 'area_identifier', + 'start_time', 'end_time'] + list_filter = ['permit__series__active'] + ordering = ('-permit__series', 'permit') + + def series(self, instance): + series = instance.permit.series + return '{id}{active}'.format( + id=series.id, active='*' if series.active else '') + + +@admin.register(PermitSeries) +class PermitSeriesAdmin(admin.ModelAdmin): + date_hierarchy = 'created_at' + list_display = ['id', 'active', 'created_at', 'modified_at'] + list_filter = ['active'] + ordering = ('-created_at', '-id') diff --git a/parkings/admin_utils.py b/parkings/admin_utils.py index 35245d2e..9f75356b 100644 --- a/parkings/admin_utils.py +++ b/parkings/admin_utils.py @@ -13,3 +13,13 @@ def has_change_permission(self, request, obj=None): def has_delete_permission(self, request, obj=None): return False + + +class WithAreaField: + area_scale = 1000000 + + def area(self, instance): + assert self.area_scale in [1000000, 1] + unit = 'km\u00b2' if self.area_scale == 1000000 else 'm\u00b2' + return '{area:.1f} {unit}'.format( + area=instance.geom.area / self.area_scale, unit=unit) diff --git a/parkings/migrations/0019_permits.py b/parkings/migrations/0019_permits.py index 77c5b07d..0f99ce85 100644 --- a/parkings/migrations/0019_permits.py +++ b/parkings/migrations/0019_permits.py @@ -62,6 +62,8 @@ class Migration(migrations.Migration): ], options={ 'ordering': ('created_at', 'id'), + 'verbose_name': 'permit series', + 'verbose_name_plural': 'permit series', }, ), migrations.AddField( diff --git a/parkings/models/permit.py b/parkings/models/permit.py index 4114fa36..d32ec69e 100644 --- a/parkings/models/permit.py +++ b/parkings/models/permit.py @@ -46,6 +46,8 @@ class PermitSeries(TimestampedModelMixin, models.Model): class Meta: ordering = ('created_at', 'id') + verbose_name = _("permit series") + verbose_name_plural = _("permit series") def __str__(self): return str(self.id) @@ -103,7 +105,11 @@ class Meta: ordering = ('series', 'id') def __str__(self): - return ('Permit {}'.format(self.id)) + return 'Permit {id} ({series}{active} / {external_id})'.format( + id=self.id, + series=self.series, + active='*' if self.series.active else '', + external_id=self.external_id) def save(self, using=None, *args, **kwargs): self.full_clean()