diff --git a/backend/market/migrations/0004_listing_status.py b/backend/market/migrations/0004_listing_status.py new file mode 100644 index 0000000..c4fb157 --- /dev/null +++ b/backend/market/migrations/0004_listing_status.py @@ -0,0 +1,26 @@ +# Generated by Django 5.0.2 on 2026-01-19 16:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("market", "0003_delete_phoneverification"), + ] + + operations = [ + migrations.AddField( + model_name="listing", + name="status", + field=models.CharField( + choices=[ + ("pending", "Pending"), + ("verified", "Verified"), + ("rejected", "Rejected"), + ], + default="pending", + max_length=10, + ), + ), + ] diff --git a/backend/market/models.py b/backend/market/models.py index 741ae34..0eff17c 100644 --- a/backend/market/models.py +++ b/backend/market/models.py @@ -91,6 +91,16 @@ class Meta: created_at = models.DateTimeField(auto_now_add=True) expires_at = models.DateTimeField() + status = models.CharField( + max_length=10, + choices=[ + ('pending', 'Pending'), + ('verified', 'Verified'), + ('rejected', 'Rejected'), + ], + default='pending', + ) + def __str__(self): return f"{self.title} by {self.seller}" diff --git a/backend/market/serializers.py b/backend/market/serializers.py index 257af71..159b66c 100644 --- a/backend/market/serializers.py +++ b/backend/market/serializers.py @@ -137,6 +137,7 @@ class Meta: "images", "listing_type", "additional_data", + "status", ] read_only_fields = [ "id", @@ -148,6 +149,8 @@ class Meta: ] def validate(self, attrs): + if 'status' in attrs and not self.context['request'].user.is_superuser: + raise ValidationError({"status": "Only superusers can modify the status."}) if not self.instance: listing_type = self.initial_data.get("listing_type") additional_data = self.initial_data.get("additional_data", {}) @@ -193,6 +196,8 @@ def contains_profanity(self, text): def create(self, validated_data): validated_data["seller"] = self.context["request"].user + validated_data["status"] = "pending" + listing_type = self.initial_data.get("listing_type") additional_data = self.initial_data.get("additional_data", {}) @@ -263,6 +268,11 @@ def update(self, instance, validated_data): setattr(instance, attr, value) if tags: instance.tags.set(tags) + + # updates to certain fields require reapproval + if any(field in self.validated_data for field in ["images", "title", "description"]): + instance.status = "pending" + if listing_type and listing_type != self.get_listing_type(instance): raise ValidationError( diff --git a/backend/market/views.py b/backend/market/views.py index 965469d..5a1c217 100644 --- a/backend/market/views.py +++ b/backend/market/views.py @@ -118,6 +118,7 @@ def get_filter_dict(listing_type): "min_price": "price__gte", "max_price": "price__lte", "negotiable": "negotiable", + "status": "status", } item_filters = { @@ -168,7 +169,7 @@ def list(self, request, *args, **kwargs): if request.query_params.get("seller", "false").lower() == "true": queryset = queryset.filter(seller=request.user) else: - queryset = queryset.filter(expires_at__gte=timezone.now()) + queryset = queryset.filter(expires_at__gte=timezone.now(), status="verified") page = self.paginate_queryset(queryset) if page is not None: @@ -180,9 +181,11 @@ def list(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs): instance = self.get_object() - if instance.seller == request.user: + if instance.seller == request.user or request.user.is_superuser: serializer_class = ListingSerializer else: + if instance.status != "verified": + raise exceptions.NotFound("No Listing matches the given query") serializer_class = ListingSerializerPublic serializer = serializer_class(instance) return Response(serializer.data) diff --git a/backend/utils/moderation.py b/backend/utils/moderation.py new file mode 100644 index 0000000..e69de29