register and sign in pages
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Atoll, Island
|
||||
|
||||
|
||||
class IslandInline(admin.TabularInline):
|
||||
model = Island
|
||||
extra = 0
|
||||
|
||||
|
||||
@admin.register(Atoll)
|
||||
class AtollAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "code", "is_active"]
|
||||
search_fields = ["name", "code"]
|
||||
inlines = [IslandInline]
|
||||
|
||||
|
||||
@admin.register(Island)
|
||||
class IslandAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "atoll", "is_active"]
|
||||
list_filter = ["atoll", "is_active"]
|
||||
search_fields = ["name"]
|
||||
@@ -0,0 +1,17 @@
|
||||
from rest_framework.generics import ListAPIView
|
||||
from rest_framework.permissions import AllowAny
|
||||
|
||||
from .models import Atoll
|
||||
from .serializers import AtollSerializer
|
||||
|
||||
|
||||
class AtollListView(ListAPIView):
|
||||
"""Public: the registration form needs this before anyone has a token."""
|
||||
|
||||
authentication_classes = []
|
||||
permission_classes = [AllowAny]
|
||||
serializer_class = AtollSerializer
|
||||
pagination_class = None
|
||||
|
||||
def get_queryset(self):
|
||||
return Atoll.objects.filter(is_active=True).prefetch_related("islands")
|
||||
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class LocationsConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "locations"
|
||||
@@ -0,0 +1,16 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from locations.models import Atoll, Island
|
||||
from locations.seed import seed
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Create the atolls and islands SAR Link serves (idempotent)."
|
||||
|
||||
def handle(self, *args, **options):
|
||||
atolls, islands = seed(Atoll, Island)
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f"Locations seeded: {atolls} atoll(s), {islands} island(s) created."
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,40 @@
|
||||
# Generated by Django 5.2.7 on 2026-09-21 19:05
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Atoll',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100, unique=True)),
|
||||
('code', models.CharField(blank=True, max_length=8)),
|
||||
('is_active', models.BooleanField(default=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Island',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('is_active', models.BooleanField(default=True)),
|
||||
('atoll', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='islands', to='locations.atoll')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
'constraints': [models.UniqueConstraint(fields=('atoll', 'name'), name='unique_island_name_per_atoll')],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
from django.db import migrations
|
||||
|
||||
from locations.seed import seed
|
||||
|
||||
|
||||
def seed_locations(apps, schema_editor):
|
||||
seed(apps.get_model("locations", "Atoll"), apps.get_model("locations", "Island"))
|
||||
|
||||
|
||||
def unseed_locations(apps, schema_editor):
|
||||
"""Only removes rows nothing references."""
|
||||
Atoll = apps.get_model("locations", "Atoll")
|
||||
Island = apps.get_model("locations", "Island")
|
||||
from locations.seed import ATOLLS
|
||||
|
||||
for entry in ATOLLS:
|
||||
Island.objects.filter(
|
||||
atoll__name=entry["name"], name__in=entry["islands"], users__isnull=True
|
||||
).delete()
|
||||
Atoll.objects.filter(
|
||||
name=entry["name"], islands__isnull=True, users__isnull=True
|
||||
).delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("locations", "0001_initial"),
|
||||
# Islands/atolls are referenced by users; keep the tables in step.
|
||||
("users", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [migrations.RunPython(seed_locations, unseed_locations)]
|
||||
@@ -0,0 +1,31 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Atoll(models.Model):
|
||||
name = models.CharField(max_length=100, unique=True)
|
||||
# Maldivian administrative code, e.g. "F" for Faafu.
|
||||
code = models.CharField(max_length=8, blank=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ["name"]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Island(models.Model):
|
||||
atoll = models.ForeignKey(Atoll, on_delete=models.PROTECT, related_name="islands")
|
||||
name = models.CharField(max_length=100)
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ["name"]
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["atoll", "name"], name="unique_island_name_per_atoll"
|
||||
)
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name}, {self.atoll.name}"
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Seed data for atolls and islands.
|
||||
|
||||
Only the areas SAR Link actually serves are listed. Add more here (or in the
|
||||
admin) as coverage grows; `seed()` is idempotent, so re-running is safe.
|
||||
"""
|
||||
|
||||
ATOLLS = [
|
||||
{
|
||||
"name": "Faafu",
|
||||
"code": "F",
|
||||
"islands": ["Dharanboodhoo"],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def seed(atoll_model, island_model) -> tuple[int, int]:
|
||||
"""Create any missing atolls/islands. Returns (atolls, islands) created.
|
||||
|
||||
Takes the models as arguments so both the management command and the data
|
||||
migration can call it, the latter with historical models.
|
||||
"""
|
||||
atolls_created = 0
|
||||
islands_created = 0
|
||||
|
||||
for entry in ATOLLS:
|
||||
atoll, created = atoll_model.objects.get_or_create(
|
||||
name=entry["name"], defaults={"code": entry.get("code", "")}
|
||||
)
|
||||
atolls_created += int(created)
|
||||
|
||||
for island_name in entry["islands"]:
|
||||
_, created = island_model.objects.get_or_create(
|
||||
atoll=atoll, name=island_name
|
||||
)
|
||||
islands_created += int(created)
|
||||
|
||||
return atolls_created, islands_created
|
||||
@@ -0,0 +1,23 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import Atoll, Island
|
||||
|
||||
|
||||
class IslandSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Island
|
||||
fields = ["id", "name", "atoll"]
|
||||
|
||||
|
||||
class AtollSerializer(serializers.ModelSerializer):
|
||||
"""Atolls with their islands nested - the registration form needs both."""
|
||||
|
||||
islands = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Atoll
|
||||
fields = ["id", "name", "code", "islands"]
|
||||
|
||||
def get_islands(self, obj) -> list[dict]:
|
||||
islands = [island for island in obj.islands.all() if island.is_active]
|
||||
return IslandSerializer(islands, many=True).data
|
||||
@@ -0,0 +1,7 @@
|
||||
from django.urls import path
|
||||
|
||||
from .api import AtollListView
|
||||
|
||||
urlpatterns = [
|
||||
path("atolls/", AtollListView.as_view(), name="atoll-list"),
|
||||
]
|
||||
Reference in New Issue
Block a user