Skip to content

Commit 123782e

Browse files
committed
Add RasterFile model and use it as Foreing Key to RasterDataset
1 parent e422232 commit 123782e

File tree

9 files changed

+148
-42
lines changed

9 files changed

+148
-42
lines changed

media/raster/cog.tif

781 KB
Binary file not shown.

vbos/datasets/admin.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from .models import (
1212
RasterDataset,
13+
RasterFile,
1314
TabularDataset,
1415
TabularItem,
1516
VectorDataset,
@@ -18,9 +19,14 @@
1819
from .forms import CSVUploadForm, GeoJSONUploadForm
1920

2021

22+
@admin.register(RasterFile)
23+
class RasterFileAdmin(admin.ModelAdmin):
24+
list_display = ["id", "name", "created", "file"]
25+
26+
2127
@admin.register(RasterDataset)
2228
class RasterDatasetAdmin(admin.ModelAdmin):
23-
list_display = ["id", "name", "created", "updated", "file_path"]
29+
list_display = ["id", "name", "created", "updated", "file"]
2430

2531

2632
@admin.register(VectorDataset)

vbos/datasets/migrations/0003_rasterdataset.py

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Generated by Django 5.2.5 on 2025-09-03 16:50
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("datasets", "0002_tabulardataset_tabularitem"),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name="RasterFile",
16+
fields=[
17+
(
18+
"id",
19+
models.AutoField(
20+
auto_created=True,
21+
primary_key=True,
22+
serialize=False,
23+
verbose_name="ID",
24+
),
25+
),
26+
("name", models.CharField(max_length=155)),
27+
("created", models.DateTimeField(auto_now_add=True)),
28+
("file", models.FileField(upload_to="raster/")),
29+
],
30+
options={
31+
"ordering": ["id"],
32+
},
33+
),
34+
migrations.CreateModel(
35+
name="RasterDataset",
36+
fields=[
37+
(
38+
"id",
39+
models.AutoField(
40+
auto_created=True,
41+
primary_key=True,
42+
serialize=False,
43+
verbose_name="ID",
44+
),
45+
),
46+
("name", models.CharField(max_length=155)),
47+
("created", models.DateTimeField(auto_now_add=True)),
48+
("updated", models.DateTimeField(auto_now=True)),
49+
(
50+
"file",
51+
models.ForeignKey(
52+
on_delete=django.db.models.deletion.PROTECT,
53+
to="datasets.rasterfile",
54+
),
55+
),
56+
],
57+
options={
58+
"ordering": ["id"],
59+
},
60+
),
61+
]

vbos/datasets/models.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,37 @@
11
from django.contrib.gis.db import models
2+
from django.db.models.fields.files import default_storage
3+
from django.db.models.signals import pre_delete
4+
from django.dispatch import receiver
5+
6+
7+
class RasterFile(models.Model):
8+
name = models.CharField(max_length=155)
9+
created = models.DateTimeField(auto_now_add=True)
10+
file = models.FileField(upload_to="raster/")
11+
12+
def __str__(self):
13+
return self.name
14+
15+
class Meta:
16+
ordering = ["id"]
17+
18+
19+
@receiver(pre_delete, sender=RasterFile)
20+
def delete_raster_file(sender, instance, **kwargs):
21+
"""
22+
Delete the file from storage when a RasterFile instance is deleted
23+
"""
24+
if instance.file:
25+
# Using default_storage for better compatibility with different storage backends
26+
if default_storage.exists(instance.file.name):
27+
default_storage.delete(instance.file.name)
228

329

430
class RasterDataset(models.Model):
531
name = models.CharField(max_length=155)
632
created = models.DateTimeField(auto_now_add=True)
733
updated = models.DateTimeField(auto_now=True)
8-
file_path = models.CharField(max_length=2000, null=False, blank=False)
34+
file = models.ForeignKey(RasterFile, on_delete=models.PROTECT)
935

1036
def __str__(self):
1137
return self.name

vbos/datasets/serializers.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111

1212

1313
class RasterDatasetSerializer(serializers.ModelSerializer):
14+
file = serializers.ReadOnlyField(source="file.file.url")
15+
1416
class Meta:
1517
model = RasterDataset
16-
fields = "__all__"
18+
fields = ["id", "name", "created", "updated", "file"]
1719

1820

1921
class VectorDatasetSerializer(serializers.ModelSerializer):
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{% extends "admin/change_list.html" %}
2+
{% load admin_urls %}
3+
{% block object-tools-items %}
4+
{{ block.super }}
5+
<li>
6+
<a href="{% url opts|admin_urlname:'import_file' %}" class="addlink">
7+
Import File
8+
</a>
9+
</li>
10+
{% endblock %}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from django.db.models.deletion import ProtectedError
2+
from django.test import TestCase
3+
4+
from vbos.datasets.models import RasterDataset, RasterFile
5+
6+
7+
class TestRasterModels(TestCase):
8+
def setUp(self):
9+
self.r_1 = RasterFile.objects.create(
10+
name="Rainfall COG", file="raster/rainfall.tiff"
11+
)
12+
self.r_2 = RasterFile.objects.create(
13+
name="Coastline COG", file="raster/coastline.tiff"
14+
)
15+
self.dataset = RasterDataset.objects.create(name="Rainfall", file=self.r_1)
16+
17+
def test_deletion_restriction(self):
18+
with self.assertRaises(ProtectedError):
19+
self.r_1.delete()
20+
self.r_2.delete()
21+
self.assertEqual(RasterFile.objects.count(), 1)
22+
23+
def test_deletion(self):
24+
self.dataset.file = self.r_2
25+
self.dataset.save()
26+
self.r_1.delete()
27+
self.assertEqual(RasterFile.objects.count(), 1)
28+
self.dataset.delete()
29+
self.assertEqual(RasterDataset.objects.count(), 0)
30+
self.r_2.delete()
31+
self.assertEqual(RasterFile.objects.count(), 0)

vbos/datasets/test/test_raster_views.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22
from rest_framework.test import APITestCase
33
from django.urls import reverse
44

5-
from ..models import RasterDataset
5+
from ..models import RasterDataset, RasterFile
66

77

88
class TestRasterDatasetListDetailViews(APITestCase):
99
def setUp(self):
10-
self.dataset_1 = RasterDataset.objects.create(
11-
name="Rainfall", file_path="cogs/rainfall.tiff"
10+
self.r_1 = RasterFile.objects.create(
11+
name="Rainfall COG", file="raster/rainfall.tiff"
1212
)
13+
self.r_2 = RasterFile.objects.create(
14+
name="Coastline COG", file="raster/coastline.tiff"
15+
)
16+
self.dataset_1 = RasterDataset.objects.create(name="Rainfall", file=self.r_1)
1317
self.dataset_2 = RasterDataset.objects.create(
14-
name="Coastline changes", file_path="cogs/coastlines.tiff"
18+
name="Coastline changes", file=self.r_2
1519
)
1620
self.url = reverse("datasets:raster-list")
1721

@@ -27,6 +31,6 @@ def test_raster_datasets_detail(self):
2731
req = self.client.get(url)
2832
assert req.status_code == status.HTTP_200_OK
2933
assert req.data.get("name") == "Rainfall"
30-
assert req.data.get("file_path") == "cogs/rainfall.tiff"
34+
assert req.data.get("file") == "/media/raster/rainfall.tiff"
3135
assert req.data.get("created")
3236
assert req.data.get("updated")

0 commit comments

Comments
 (0)