Skip to content

Commit

Permalink
Finished most tests for market, need to write a few tests for images …
Browse files Browse the repository at this point in the history
…but should work
  • Loading branch information
minghansun1 committed Jan 24, 2025
1 parent 9bcafc0 commit a8a0ca8
Show file tree
Hide file tree
Showing 6 changed files with 770 additions and 75 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 5.0.2 on 2025-01-23 05:49

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("market", "0007_alter_sublet_baths_alter_sublet_beds"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.RenameField(
model_name="offer",
old_name="created_date",
new_name="created_at",
),
migrations.AlterField(
model_name="offer",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="offers",
to=settings.AUTH_USER_MODEL,
),
),
]
16 changes: 2 additions & 14 deletions backend/market/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ class Offer(models.Model):
class Meta:
constraints = [models.UniqueConstraint(fields=["user", "item"], name="unique_offer_market")]

user = models.ForeignKey(User, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="offers")
item = models.ForeignKey("Item", on_delete=models.CASCADE)
email = models.EmailField(max_length=255, null=True, blank=True)
phone_number = PhoneNumberField(null=True, blank=True)
message = models.CharField(max_length=255, blank=True)
created_date = models.DateTimeField(auto_now_add=True)
created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return f"Offer for {self.item} made by {self.user}"
Expand Down Expand Up @@ -55,23 +55,11 @@ class Item(models.Model):

def __str__(self):
return f"{self.title} by {self.seller}"

def clean(self):
# Check that sublet is not null when category is "Sublet"
if self.category.name == "Sublet" and not hasattr(self, "sublet"):
raise ValidationError({"sublet": "Sublet must not be null when category is 'Sublet'."})

# Check that sublet is null when category is not "Sublet"
if self.category.name != "Sublet" and hasattr(self, "sublet") and self.sublet:
raise ValidationError({"sublet": "Sublet must be null when category is not 'Sublet'."})

def save(self, *args, **kwargs):
self.full_clean() # Validate the object
super().save(*args, **kwargs)




class Sublet(models.Model):
item = models.OneToOneField(Item, on_delete=models.CASCADE, related_name="sublet")
address = models.CharField(max_length=255)
Expand Down
45 changes: 37 additions & 8 deletions backend/market/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,13 @@ class Meta:

# complex item serializer for use in C/U/D + getting info about a singular item
class ItemSerializer(serializers.ModelSerializer):
images = ItemImageSerializer(many=True, required=False)
images = ItemImageSerializer(many=True, required=False, read_only=True)
sublet_id = serializers.SerializerMethodField(read_only=True)

class Meta:
model = Item
fields = "__all__"
read_only_fields = ["id", "created_at", "seller", "buyers", "images", "favorites"]
read_only_fields = ["id", "created_at", "seller", "buyers", "images", "favorites", "sublet_id"]

def validate_title(self, value):
if self.contains_profanity(value):
Expand All @@ -92,6 +94,9 @@ def validate_description(self, value):
def contains_profanity(self, text):
return predict([text])[0]

def get_sublet_id(self, obj):
return obj.sublet.pk if hasattr(obj, "sublet") else None

def create(self, validated_data):
try:
self.validate(validated_data)
Expand All @@ -102,15 +107,22 @@ def create(self, validated_data):

def update(self, instance, validated_data):
try:
if instance.category.name == "Sublet" and "category" in validated_data and validated_data.get("category", None).name != "Sublet":
raise serializers.ValidationError("Cannot change category from Sublet")
if instance.category.name != "Sublet" and "category" in validated_data and validated_data.get("category", None).name == "Sublet":
raise serializers.ValidationError("Cannot change category to Sublet")

return super().update(instance, validated_data)
except exceptions.ValidationError as e:
raise serializers.ValidationError(e.message_dict)


# Read-only serializer for use when reading a single item
class ItemSerializerRetrieve(serializers.ModelSerializer):
class ItemSerializerPublic(serializers.ModelSerializer):
buyer_count = serializers.SerializerMethodField()
favorite_count = serializers.SerializerMethodField()
images = ItemImageURLSerializer(many=True)
sublet_id = serializers.SerializerMethodField()

class Meta:
model = Item
Expand All @@ -124,20 +136,28 @@ class Meta:
"description",
"price",
"negotiable",
"created_at",
"expires_at",
"images",
"favorites",
"favorite_count",
"sublet_id",
]
read_only_fields = fields

def get_buyer_count(self, obj):
return obj.buyers.count()

def get_sublet_id(self, obj):
return obj.sublet.pk if hasattr(obj, "sublet") else None

def get_favorite_count(self, obj):
return obj.favorites.count()


# Read-only serializer for use when pulling all items/etc
class ItemSerializerList(serializers.ModelSerializer):
images = ItemImageURLSerializer(many=True)
sublet_id = serializers.SerializerMethodField()

class Meta:
model = Item
fields = [
Expand All @@ -151,9 +171,13 @@ class Meta:
"expires_at",
"images",
"favorites",
"sublet_id",
]
read_only_fields = fields

def get_sublet_id(self, obj):
return obj.sublet.pk if hasattr(obj, "sublet") else None


class SubletSerializer(serializers.ModelSerializer):
item = ItemSerializer(required=True)
Expand All @@ -164,15 +188,20 @@ class Meta:
read_only_fields = ["id"]

def create(self, validated_data):
category = validated_data.get("item", {}).get("category", None)
if category is None or category.name != "Sublet":
raise serializers.ValidationError({"item": "Item category must be 'Sublet'."})
item_serializer = ItemSerializer(data=validated_data.pop("item"), context=self.context)
item_serializer.is_valid(raise_exception=True)
validated_data["item"] = item_serializer.save()
instance = super().create(validated_data)
return instance

def update(self, instance, validated_data):
category = validated_data.get("item", {}).get("category", None)
if category is None or category.name != "Sublet":
raise serializers.ValidationError({"item": "Item category must be 'Sublet'."})
if item_data := validated_data.pop("item", None):
item_data.pop("category", None)
item_serializer = ItemSerializer(
instance=instance.item, data=item_data, context=self.context, partial=True
)
Expand All @@ -191,8 +220,8 @@ def destroy(self, instance):
instance.delete()


class SubletSerializerRetrieve(serializers.ModelSerializer):
item = ItemSerializerRetrieve(required=True)
class SubletSerializerPublic(serializers.ModelSerializer):
item = ItemSerializerPublic(required=True)

class Meta:
model = Sublet
Expand Down
23 changes: 15 additions & 8 deletions backend/market/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
ItemImageSerializer,
ItemImageURLSerializer,
ItemSerializer,
ItemSerializerRetrieve,
ItemSerializerPublic,
ItemSerializerList,
OfferSerializer,
SubletSerializer,
SubletSerializerRetrieve,
SubletSerializerPublic,
SubletSerializerList,
)

Expand Down Expand Up @@ -86,8 +86,8 @@ class Items(viewsets.ModelViewSet):
def get_serializer_class(self):
if self.action=="list":
return ItemSerializerList
elif self.action=="retrieve":
return ItemSerializerRetrieve
elif self.action=="retrieve" and self.get_object().seller != self.request.user:
return ItemSerializerPublic
else:
return ItemSerializer

Expand Down Expand Up @@ -128,6 +128,8 @@ def list(self, request, *args, **kwargs):


def create(self, request, *args, **kwargs):
if request.data.get("category", None) == "Sublet":
return Response("Sublet must be created using /sublets/", status=status.HTTP_400_BAD_REQUEST)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
Expand All @@ -150,8 +152,8 @@ class Sublets(viewsets.ModelViewSet):
def get_serializer_class(self):
if self.action=="list":
return SubletSerializerList
elif self.action=="retrieve":
return SubletSerializerRetrieve
elif self.action=="retrieve" and self.get_object().item.seller != self.request.user:
return SubletSerializerPublic
else:
return SubletSerializer

Expand Down Expand Up @@ -285,7 +287,10 @@ class Offers(viewsets.ModelViewSet):
serializer_class = OfferSerializer

def get_queryset(self):
return Offer.objects.filter(item_id=int(self.kwargs["item_id"])).order_by("created_date")
if Item.objects.filter(pk=int(self.kwargs["item_id"])).exists():
return Offer.objects.filter(item_id=int(self.kwargs["item_id"])).order_by("created_at")
else:
raise exceptions.NotFound("No Item matches the given query")

def create(self, request, *args, **kwargs):
data = request.data
Expand All @@ -301,12 +306,14 @@ def create(self, request, *args, **kwargs):

def destroy(self, request, *args, **kwargs):
queryset = self.get_queryset()
filter = {"user": self.request.user.id, "item": int(self.kwargs["item_id"])}
filter = {"user": self.request.user, "item": int(self.kwargs["item_id"])}
obj = get_object_or_404(queryset, **filter)
self.check_object_permissions(self.request, obj)
self.perform_destroy(obj)
return Response(status=status.HTTP_204_NO_CONTENT)

def list(self, request, *args, **kwargs):
if not Item.objects.filter(pk=int(self.kwargs["item_id"])).exists():
raise exceptions.NotFound("No Item matches the given query")
self.check_object_permissions(request, Item.objects.get(pk=int(self.kwargs["item_id"])))
return super().list(request, *args, **kwargs)
10 changes: 7 additions & 3 deletions backend/tests/market/mock_items.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"external_link": "https://example.com/book",
"price": 20.0,
"negotiable": true,
"expires_at": "2024-12-12T00:00:00-05:00"
"created_at": "2024-11-13T20:14:34.604238-05:00",
"expires_at": "2025-12-12T00:00:00-05:00"
},
{
"tags": ["New"],
Expand All @@ -17,7 +18,8 @@
"external_link": "https://example.com/doritos",
"price": 5.0,
"negotiable": false,
"expires_at": "2024-10-12T00:00:00-05:00"
"created_at": "2024-11-13T20:14:34.604238-05:00",
"expires_at": "2025-10-12T00:00:00-05:00"
},
{
"tags": ["Laptop", "New"],
Expand All @@ -27,6 +29,7 @@
"external_link": "https://example.com/macbook",
"price": 2000.0,
"negotiable": true,
"created_at": "2024-11-13T20:14:34.604238-05:00",
"expires_at": "2025-08-12T00:00:00-05:00"
},
{
Expand All @@ -37,6 +40,7 @@
"external_link": "https://example.com/couch",
"price": 400.0,
"negotiable": true,
"expires_at": "2025-1-12T00:00:00-05:00"
"created_at": "2024-11-13T20:14:34.604238-05:00",
"expires_at": "2025-12-12T00:00:00-05:00"
}
]
Loading

0 comments on commit a8a0ca8

Please sign in to comment.