diff --git a/apps/api/afb/urls/__init__.py b/apps/api/afb/urls/__init__.py index 4642bd8..f44f0af 100644 --- a/apps/api/afb/urls/__init__.py +++ b/apps/api/afb/urls/__init__.py @@ -4,6 +4,7 @@ """ from afbcore.views import ( + BranchViewSet, FoodRequestViewSet, ProfileViewSet, authtoken, @@ -28,6 +29,7 @@ # e.g. /api/v1/requests/abcdef1234/ router.register("requests", FoodRequestViewSet, basename="foodrequest") router.register("profiles", ProfileViewSet, basename="profile") +router.register("branches", BranchViewSet) urlpatterns = [ path("afbadmin/", afbcore_admin.site.urls, name="admin"), diff --git a/apps/api/afbcore/admin.py b/apps/api/afbcore/admin.py index b5ea7e6..09ef054 100644 --- a/apps/api/afbcore/admin.py +++ b/apps/api/afbcore/admin.py @@ -34,7 +34,7 @@ class AuthorAdmin(admin.ModelAdmin): # Register your models here. admin.site.register(User, UserAdmin) admin.site.register(Profile) -admin.site.register(Branch, BranchAdmin) +# admin.site.register(Branch, BranchAdmin) admin.site.register(FoodRequest) admin.site.register(Delivery) admin.site.register(DeliveryRegion) diff --git a/apps/api/afbcore/serializers/__init__.py b/apps/api/afbcore/serializers/__init__.py index bd1d4b4..1e33958 100644 --- a/apps/api/afbcore/serializers/__init__.py +++ b/apps/api/afbcore/serializers/__init__.py @@ -1,3 +1,4 @@ +from .branch_serializer import BranchSerializer # noqa: F401 from .delivery_region_serializer import DeliveryRegionSerializer # noqa: F401 from .profile.profile_serializer import ProfileSerializer # noqa: F401 from .request.food_request_serializer import ( diff --git a/apps/api/afbcore/serializers/branch_serializer.py b/apps/api/afbcore/serializers/branch_serializer.py new file mode 100644 index 0000000..d570b6d --- /dev/null +++ b/apps/api/afbcore/serializers/branch_serializer.py @@ -0,0 +1,67 @@ +from rest_framework import serializers + +from ..models import Branch +from .delivery_region_serializer import DeliveryRegionSerializer + + +class BranchSerializer(serializers.ModelSerializer): + delivery_regions = DeliveryRegionSerializer(many=True, read_only=True) + + class Meta: + model = Branch + fields = [ + "id", + "display_name", + "delivery_regions", + "pickup_locations", + "frequency_of_requests", + "spay_neuter_requirement", + "pets_per_household_max", + "delivery_deadline_days", + "delivery_type", + "delivery_pickup_details", + "blurb", + "blurb_image", + "latitude", + "longitude", + "delivery_radius", + # Include fields from PhysicalLocationMixin if needed + "street_address", + "city", + "province_state", + "country", + "postal_zip_code", + ] + read_only_fields = ["id"] + + def validate_delivery_radius(self, value): + if value is not None and value <= 0: + raise serializers.ValidationError( + "Delivery radius must be greater than 0." + ) + return value + + def validate_pets_per_household_max(self, value): + if value <= 0: + raise serializers.ValidationError( + "Maximum pets per household must be greater than 0." + ) + return value + + def validate_delivery_deadline_days(self, value): + if value <= 0: + raise serializers.ValidationError( + "Delivery deadline days must be greater than 0." + ) + return value + + def validate(self, data): + if data.get("latitude") is not None and data.get("longitude") is None: + raise serializers.ValidationError( + "Both latitude and longitude must be provided together." + ) + if data.get("latitude") is None and data.get("longitude") is not None: + raise serializers.ValidationError( + "Both latitude and longitude must be provided together." + ) + return data diff --git a/apps/api/afbcore/views/__init__.py b/apps/api/afbcore/views/__init__.py index 5f41917..cb75633 100644 --- a/apps/api/afbcore/views/__init__.py +++ b/apps/api/afbcore/views/__init__.py @@ -15,6 +15,7 @@ module. """ +from .branch_viewset import BranchViewSet # noqa: F401 from .profile import ProfileViewSet # noqa: F401 from .requests import FoodRequestViewSet # noqa: F401 from .users import CurrentUserAPIView # noqa: F401 diff --git a/apps/api/afbcore/views/admin/branch_admin.py b/apps/api/afbcore/views/admin/branch_admin.py index 42f5694..3673068 100644 --- a/apps/api/afbcore/views/admin/branch_admin.py +++ b/apps/api/afbcore/views/admin/branch_admin.py @@ -1,15 +1,110 @@ from django.contrib import admin +from django.utils.html import format_html -from .models import Branch +from ...models import Branch +@admin.register(Branch) class BranchAdmin(admin.ModelAdmin): - list_display = [ + list_display = ( "display_name", + "location_name", "city", - "province_state", - ] # Customize as needed - # No need to specify read_only_fields unless you want some fields to be read-only in the admin + "state_or_province", + "country", + "operational", + "hidden", + ) + list_filter = ( + "operational", + "hidden", + "spay_neuter_requirement", + "delivery_type", + ) + search_fields = ( + "display_name", + "location_name", + "city", + "state_or_province", + "country", + ) + readonly_fields = ("id",) + + fieldsets = ( + ( + "Basic Information", + { + "fields": ( + "id", + "display_name", + "blurb", + "blurb_image", + "operational", + "hidden", + ) + }, + ), + ( + "Location", + { + "fields": ( + "location_name", + "address_line1", + "address_line2", + "city", + "state_or_province", + "postal_code", + "country", + "ext_id", + "latitude", + "longitude", + ) + }, + ), + ( + "Delivery Information", + { + "fields": ( + "delivery_regions", + "pickup_locations", + "delivery_type", + "delivery_pickup_details", + "delivery_radius", + "delivery_deadline_days", + ) + }, + ), + ( + "Branch Policies", + { + "fields": ( + "frequency_of_requests", + "spay_neuter_requirement", + "pets_per_household_max", + ) + }, + ), + ) + + def blurb_image_preview(self, obj): + if obj.blurb_image: + return format_html( + '', obj.blurb_image.url + ) + return "No Image" + + blurb_image_preview.short_description = "Blurb Image Preview" + + def get_readonly_fields(self, request, obj=None): + if obj: # editing an existing object + return self.readonly_fields + ("id",) + return self.readonly_fields + filter_horizontal = ("delivery_regions",) -admin.site.register(Branch, BranchAdmin) + def formfield_for_manytomany(self, db_field, request, **kwargs): + if db_field.name == "delivery_regions": + kwargs["widget"] = admin.widgets.FilteredSelectMultiple( + "Delivery Regions", is_stacked=False + ) + return super().formfield_for_manytomany(db_field, request, **kwargs) diff --git a/apps/api/afbcore/views/branches.py b/apps/api/afbcore/views/branch_viewset.py similarity index 76% rename from apps/api/afbcore/views/branches.py rename to apps/api/afbcore/views/branch_viewset.py index 8d4a40c..7a50b78 100644 --- a/apps/api/afbcore/views/branches.py +++ b/apps/api/afbcore/views/branch_viewset.py @@ -2,8 +2,8 @@ from rest_framework import filters, viewsets from rest_framework.permissions import IsAuthenticated -from .models import Branch -from .serializers import BranchSerializer +from ..models import Branch +from ..serializers import BranchSerializer class BranchViewSet(viewsets.ModelViewSet): @@ -21,8 +21,20 @@ class BranchViewSet(viewsets.ModelViewSet): "delivery_type", "spay_neuter_requirement", ] - search_fields = ["display_name", "city", "province_state", "country"] - ordering_fields = ["display_name", "city", "province_state", "country"] + search_fields = [ + "display_name", + "location_name", + "city", + "state_or_province", + "country", + ] + ordering_fields = [ + "display_name", + "location_name", + "city", + "state_or_province", + "country", + ] def get_queryset(self): """