Skip to content

Commit

Permalink
Merge pull request #364 from ImperialCollegeLondon/edit_view
Browse files Browse the repository at this point in the history
Adds edit, create and delete views
  • Loading branch information
dalonsoa authored Oct 4, 2024
2 parents 8516780 + 14f3123 commit 46041f8
Show file tree
Hide file tree
Showing 14 changed files with 491 additions and 99 deletions.
4 changes: 4 additions & 0 deletions importing/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ def clean(self) -> None:
if not tz:
raise ValidationError("Station must have a timezone set.")

# If the file has changed, we reprocess the data
if self.pk and self.rawfile != self.__class__.objects.get(pk=self.pk).rawfile:
self.reprocess = True

if self.reprocess:
self.status = "N"
self.reprocess = False
11 changes: 10 additions & 1 deletion importing/urls.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
from django.urls import path

from .views import DataImportDetailView, DataImportListView
from .views import (
DataImportCreateView,
DataImportDeleteView,
DataImportDetailView,
DataImportEditView,
DataImportListView,
)

app_name = "importing"
urlpatterns = [
path("", DataImportListView.as_view(), name="dataimport_list"),
path("<int:pk>/", DataImportDetailView.as_view(), name="dataimport_detail"),
path("edit/<int:pk>", DataImportEditView.as_view(), name="dataimport_edit"),
path("create/", DataImportCreateView.as_view(), name="dataimport_create"),
path("delete/<int:pk>", DataImportDeleteView.as_view(), name="dataimport_delete"),
]
34 changes: 31 additions & 3 deletions importing/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
from management.views import CustomDetailView, CustomTableView
from management.views import (
CustomCreateView,
CustomDeleteView,
CustomDetailView,
CustomEditView,
CustomTableView,
)

from .filters import DataImportFilter
from .models import DataImport
Expand All @@ -9,11 +15,33 @@ class DataImportDetailView(CustomDetailView):
"""View to view a data import."""

model = DataImport
show_list_btn = True


class DataImportListView(CustomTableView):
"""View to list all data imports."""

model = DataImport
table_class = DataImportTable
filterset_class = DataImportFilter
show_refresh_btn = True


class DataImportEditView(CustomEditView):
"""View to edit a data import."""

model = DataImport
fields = ["visibility", "station", "format", "rawfile", "reprocess", "observations"]
foreign_key_fields = ["station", "format"]


class DataImportCreateView(CustomCreateView):
"""View to create a data import."""

model = DataImport
fields = ["visibility", "station", "format", "rawfile", "reprocess", "observations"]
foreign_key_fields = ["station", "format"]


class DataImportDeleteView(CustomDeleteView):
"""View to delete a data import."""

model = DataImport
22 changes: 2 additions & 20 deletions management/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from guardian.shortcuts import get_objects_for_user

from .models import User
from .permissions import get_queryset

# Set global preferences for the Django admin site
admin.site.site_header = "Paricia Administration"
Expand Down Expand Up @@ -65,7 +66,7 @@ def get_queryset(self, request):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
"""Limit the queryset for foreign key fields."""
if db_field.name in self.foreign_key_fields:
kwargs["queryset"] = _get_queryset(db_field, request.user)
kwargs["queryset"] = get_queryset(db_field, request.user)
if db_field.name == "owner" and not request.user.is_superuser:
kwargs["initial"] = request.user.id
kwargs["disabled"] = True
Expand All @@ -80,25 +81,6 @@ def formfield_for_choice_field(self, db_field, request, **kwargs):
return super().formfield_for_choice_field(db_field, request, **kwargs)


def _get_queryset(db_field, user):
"""Return a queryset based on the permissions of the user.
Returns queryset of public objects and objects that the user has change permisions
for. For the case of `Station` objects, having the `change` permission is
necessary to include the object in the queryset - being `Public` is not enough.
"""
app_name = db_field.related_model._meta.app_label
model_name = db_field.related_model._meta.model_name
user_objects = get_objects_for_user(user, f"{app_name}.change_{model_name}")
public_objects = (
db_field.related_model.objects.none()
if model_name == "station"
else db_field.related_model.objects.filter(visibility="public")
)
return user_objects | public_objects


class CustomUserAdmin(UserAdmin):
"""A slightly more restrictive user admin page."""

Expand Down
27 changes: 27 additions & 0 deletions management/permissions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Customised permissions."""

from django.db import models as model
from guardian.shortcuts import get_objects_for_user
from rest_framework.permissions import DjangoModelPermissions


Expand All @@ -17,3 +19,28 @@ class CustomDjangoModelPermissions(DjangoModelPermissions):
"PATCH": ["%(app_label)s.change_%(model_name)s"],
"DELETE": ["%(app_label)s.delete_%(model_name)s"],
}


def get_queryset(db_field: model.Field, user: model.Model) -> model.QuerySet:
"""Return a queryset based on the permissions of the user.
Returns queryset of public objects and objects that the user has change permisions
for. For the case of `Station` objects, having the `change` permission is
necessary to include the object in the queryset - being `Public` is not enough.
Args:
db_field (model.Field): Field to filter.
user (model.Model): User to check permissions for.
Returns:
model.QuerySet: Queryset of objects that the user has permissions for.
"""
app_name = db_field.related_model._meta.app_label
model_name = db_field.related_model._meta.model_name
user_objects = get_objects_for_user(user, f"{app_name}.change_{model_name}")
public_objects = (
db_field.related_model.objects.none()
if model_name == "station"
else db_field.related_model.objects.filter(visibility="public")
)
return user_objects | public_objects
41 changes: 41 additions & 0 deletions management/tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from django.contrib.admin.utils import NestedObjects
from django.db import models
from django.utils.encoding import force_str
from django.utils.text import capfirst


def get_deleted_objects(
objs: list[models.Model],
) -> tuple[list[str], dict[str, int], list[str]]:
"""Return information about related objects to be deleted.
How to do this has been taken from https://stackoverflow.com/a/39533619/3778792
Args:
objs (list[models.Model]): List of objects to be deleted.
Returns:
tuple[list[str], dict[str, int], list[str]]: Tuple containing the following:
- List of strings representing the objects to be deleted.
- Dictionary containing the count of objects to be deleted for each model.
- List of strings representing the objects that are protected from deletion
"""
collector = NestedObjects(using="default")
collector.collect(objs)

def format_callback(obj):
opts = obj._meta
no_edit_link = f"{capfirst(opts.verbose_name)}: {force_str(obj)}"
return no_edit_link

to_delete = collector.nested(format_callback)
protected = [format_callback(obj) for obj in collector.protected]
model_count = {
model._meta.verbose_name_plural: len(objs)
for model, objs in collector.model_objs.items()
}
if len(to_delete) == 0:
to_delete.append("None")
if len(protected) == 0:
protected.append("None")
return to_delete, model_count, protected
Loading

0 comments on commit 46041f8

Please sign in to comment.