diff --git a/backend/market/admin.py b/backend/market/admin.py index db11398e..48b42d8d 100644 --- a/backend/market/admin.py +++ b/backend/market/admin.py @@ -1,19 +1,21 @@ from django.contrib import admin from django.utils.html import mark_safe -from market.models import Amenity, Offer, Sublet, SubletImage +from market.models import Tag, Category, Offer, Item, Sublet, ItemImage -class SubletAdmin(admin.ModelAdmin): +class ItemAdmin(admin.ModelAdmin): def image_tag(self, instance): images = ['' for image in instance.images.all()] return mark_safe("
".join(images)) - image_tag.short_description = "Sublet Images" + image_tag.short_description = "Item Images" readonly_fields = ("image_tag",) admin.site.register(Offer) -admin.site.register(Amenity) -admin.site.register(Sublet, SubletAdmin) -admin.site.register(SubletImage) +admin.site.register(Tag) +admin.site.register(Category) +admin.site.register(Item, ItemAdmin) +admin.site.register(Sublet) +admin.site.register(ItemImage) diff --git a/backend/market/migrations/0001_initial.py b/backend/market/migrations/0001_initial.py new file mode 100644 index 00000000..e9b4757f --- /dev/null +++ b/backend/market/migrations/0001_initial.py @@ -0,0 +1,168 @@ +# Generated by Django 5.0.2 on 2024-10-25 16:41 + +import django.db.models.deletion +import phonenumber_field.modelfields +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Category", + fields=[ + ("name", models.CharField(max_length=50, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name="Tag", + fields=[ + ("name", models.CharField(max_length=255, primary_key=True, serialize=False)), + ], + ), + migrations.CreateModel( + name="Item", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("title", models.CharField(max_length=255)), + ("description", models.TextField(blank=True, null=True)), + ("external_link", models.URLField(blank=True, max_length=255, null=True)), + ("price", models.IntegerField()), + ("negotiable", models.BooleanField(default=True)), + ("used", models.BooleanField(blank=True, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("expires_at", models.DateTimeField()), + ( + "category", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="items", + to="market.category", + ), + ), + ( + "favorites", + models.ManyToManyField( + blank=True, related_name="items_favorited", to=settings.AUTH_USER_MODEL + ), + ), + ( + "seller", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ), + ], + ), + migrations.CreateModel( + name="ItemImage", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("image", models.ImageField(upload_to="marketplace/images")), + ( + "item", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="images", + to="market.item", + ), + ), + ], + ), + migrations.CreateModel( + name="Offer", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("email", models.EmailField(blank=True, max_length=255, null=True)), + ( + "phone_number", + phonenumber_field.modelfields.PhoneNumberField( + blank=True, max_length=128, null=True, region=None + ), + ), + ("message", models.CharField(blank=True, max_length=255)), + ("created_date", models.DateTimeField(auto_now_add=True)), + ( + "item", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="offers", + to="market.item", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="offers_made_market", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.AddField( + model_name="item", + name="buyers", + field=models.ManyToManyField( + blank=True, + related_name="items_offered", + through="market.Offer", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.CreateModel( + name="Sublet", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ("address", models.CharField(max_length=255)), + ("beds", models.IntegerField()), + ("baths", models.IntegerField()), + ("start_date", models.DateTimeField()), + ("end_date", models.DateTimeField()), + ( + "item", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="sublet", + to="market.item", + ), + ), + ], + ), + migrations.AddField( + model_name="item", + name="tags", + field=models.ManyToManyField(blank=True, related_name="items", to="market.tag"), + ), + migrations.AddConstraint( + model_name="offer", + constraint=models.UniqueConstraint(fields=("user", "item"), name="unique_offer_market"), + ), + ] diff --git a/backend/market/models.py b/backend/market/models.py index 6b0cbbbc..20fa43bd 100644 --- a/backend/market/models.py +++ b/backend/market/models.py @@ -8,9 +8,9 @@ class Offer(models.Model): class Meta: - constraints = [models.UniqueConstraint(fields=["user", "item"], name="unique_offer")] + constraints = [models.UniqueConstraint(fields=["user", "item"], name="unique_offer_market")] - user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="offers_made") + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="offers_made_market") item = models.ForeignKey("Item", on_delete=models.CASCADE, related_name="offers") email = models.EmailField(max_length=255, null=True, blank=True) phone_number = PhoneNumberField(null=True, blank=True) @@ -59,11 +59,7 @@ class Item(models.Model): User, through=Offer, related_name="items_offered", blank=True ) tags = models.ManyToManyField(Tag, related_name="items", blank=True) - category = models.CharField( - max_length=50, - choices=Category.choices, - default=Category.OTHER, - ) + category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="items") favorites = models.ManyToManyField(User, related_name="items_favorited", blank=True) title = models.CharField(max_length=255) diff --git a/backend/market/permissions.py b/backend/market/permissions.py index 594a7148..b62b2dbc 100644 --- a/backend/market/permissions.py +++ b/backend/market/permissions.py @@ -49,7 +49,7 @@ def has_permission(self, request, view): def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: - # Check if the user owns the sublet when getting list + # Check if the user owns the item when getting list return obj.seller == request.user # This is redundant, here for safety return obj.user == request.user diff --git a/backend/market/serializers.py b/backend/market/serializers.py index c8967cde..06d7e1db 100644 --- a/backend/market/serializers.py +++ b/backend/market/serializers.py @@ -2,12 +2,18 @@ from profanity_check import predict from rest_framework import serializers -from market.models import Amenity, Offer, Sublet, SubletImage +from market.models import Tag, Category, Offer, Item, Sublet, ItemImage -class AmenitySerializer(serializers.ModelSerializer): +class TagSerializer(serializers.ModelSerializer): class Meta: - model = Amenity + model = Tag + fields = "__all__" + + +class CategorySerializer(serializers.ModelSerializer): + class Meta: + model = Category fields = "__all__" @@ -25,16 +31,16 @@ def create(self, validated_data): # Create/Update Image Serializer -class SubletImageSerializer(serializers.ModelSerializer): +class ItemImageSerializer(serializers.ModelSerializer): image = serializers.ImageField(write_only=True, required=False, allow_null=True) class Meta: - model = SubletImage - fields = ["sublet", "image"] + model = ItemImage + fields = ["item", "image"] # Browse images -class SubletImageURLSerializer(serializers.ModelSerializer): +class ItemImageURLSerializer(serializers.ModelSerializer): image_url = serializers.SerializerMethodField("get_image_url") def get_image_url(self, obj): @@ -50,31 +56,31 @@ def get_image_url(self, obj): return image.url class Meta: - model = SubletImage + model = ItemImage fields = ["id", "image_url"] -# complex sublet serializer for use in C/U/D + getting info about a singular sublet -class SubletSerializer(serializers.ModelSerializer): - # amenities = AmenitySerializer(many=True, required=False) - # images = SubletImageURLSerializer(many=True, required=False) - amenities = serializers.PrimaryKeyRelatedField( - many=True, queryset=Amenity.objects.all(), required=False +# complex item serializer for use in C/U/D + getting info about a singular tag +class ItemSerializer(serializers.ModelSerializer): + # amenities = ItemSerializer(many=True, required=False) + # images = ItemImageURLSerializer(many=True, required=False) + tags = serializers.PrimaryKeyRelatedField( + many=True, queryset=Tag.objects.all(), required=False ) class Meta: - model = Sublet + model = Item read_only_fields = [ "id", "created_at", - "subletter", - "sublettees", + "seller", + "buyer", # "images" ] fields = [ "id", - "subletter", - "amenities", + "seller", + "tags", "title", "address", "beds", @@ -89,7 +95,7 @@ class Meta: # "images", # images are now created/deleted through a separate endpoint (see urls.py) # this serializer isn't used for getting, - # but gets on sublets will include ids/urls for images + # but gets on tags will include ids/urls for images ] def validate_title(self, value): @@ -106,47 +112,47 @@ def contains_profanity(self, text): return predict([text])[0] def create(self, validated_data): - validated_data["subletter"] = self.context["request"].user + validated_data["seller"] = self.context["request"].user instance = super().create(validated_data) instance.save() return instance # delete_images is a list of image ids to delete def update(self, instance, validated_data): - # Check if the user is the subletter before allowing the update + # Check if the user is the seller before allowing the update if ( - self.context["request"].user == instance.subletter + self.context["request"].user == instance.seller or self.context["request"].user.is_superuser ): instance = super().update(instance, validated_data) instance.save() return instance else: - raise serializers.ValidationError("You do not have permission to update this sublet.") + raise serializers.ValidationError("You do not have permission to update this item.") def destroy(self, instance): - # Check if the user is the subletter before allowing the delete + # Check if the user is the seller before allowing the delete if ( - self.context["request"].user == instance.subletter + self.context["request"].user == instance.seller or self.context["request"].user.is_superuser ): instance.delete() else: - raise serializers.ValidationError("You do not have permission to delete this sublet.") + raise serializers.ValidationError("You do not have permission to delete this item.") -class SubletSerializerRead(serializers.ModelSerializer): +class ItemSerializerRead(serializers.ModelSerializer): amenities = serializers.PrimaryKeyRelatedField( - many=True, queryset=Amenity.objects.all(), required=False + many=True, queryset=Tag.objects.all(), required=False ) - images = SubletImageURLSerializer(many=True, required=False) + images = ItemImageURLSerializer(many=True, required=False) class Meta: - model = Sublet - read_only_fields = ["id", "created_at", "subletter", "sublettees"] + model = Item + read_only_fields = ["id", "created_at", "seller", "buyer"] fields = [ "id", - "subletter", + "seller", "amenities", "title", "address", @@ -163,18 +169,18 @@ class Meta: ] -# simple sublet serializer for use when pulling all serializers/etc -class SubletSerializerSimple(serializers.ModelSerializer): +# simple tag serializer for use when pulling all serializers/etc +class ItemSerializerSimple(serializers.ModelSerializer): amenities = serializers.PrimaryKeyRelatedField( - many=True, queryset=Amenity.objects.all(), required=False + many=True, queryset=Tag.objects.all(), required=False ) - images = SubletImageURLSerializer(many=True, required=False) + images = ItemImageURLSerializer(many=True, required=False) class Meta: - model = Sublet + model = Item fields = [ "id", - "subletter", + "seller", "amenities", "title", "address", @@ -186,4 +192,4 @@ class Meta: "end_date", "images", ] - read_only_fields = ["id", "subletter"] + read_only_fields = ["id", "seller"] diff --git a/backend/market/urls.py b/backend/market/urls.py index 14efe102..4149f863 100644 --- a/backend/market/urls.py +++ b/backend/market/urls.py @@ -2,7 +2,7 @@ from rest_framework import routers from market.views import ( - Amenities, + Tags, CreateImages, DeleteImage, Favorites, @@ -13,37 +13,37 @@ ) -app_name = "sublet" +app_name = "market" router = routers.DefaultRouter() router.register(r"properties", Properties, basename="properties") additional_urls = [ # List of all amenities - path("amenities/", Amenities.as_view(), name="amenities"), + path("tags/", Tags.as_view(), name="tags"), # All favorites for user path("favorites/", UserFavorites.as_view(), name="user-favorites"), # All offers made by user path("offers/", UserOffers.as_view(), name="user-offers"), # Favorites - # post: add a sublet to the user's favorites - # delete: remove a sublet from the user's favorites + # post: add an item to the user's favorites + # delete: remove an item from the user's favorites path( - "properties//favorites/", + "properties//favorites/", Favorites.as_view({"post": "create", "delete": "destroy"}), ), # Offers - # get: list all offers for a sublet - # post: create an offer for a sublet - # delete: delete an offer for a sublet + # get: list all offers for an item + # post: create an offer for an item + # delete: delete an offer for an item path( - "properties//offers/", + "properties//offers/", Offers.as_view({"get": "list", "post": "create", "delete": "destroy"}), ), # Image Creation - path("properties//images/", CreateImages.as_view()), + path("properties//images/", CreateImages.as_view()), # Image Deletion - path("properties/images//", DeleteImage.as_view()), + path("properties/images//", DeleteImage.as_view()), ] urlpatterns = router.urls + additional_urls diff --git a/backend/market/views.py b/backend/market/views.py index 6d64bbd4..f8df8ec7 100644 --- a/backend/market/views.py +++ b/backend/market/views.py @@ -7,21 +7,23 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from market.models import Amenity, Offer, Sublet, SubletImage +from market.models import Tag, Category, Offer, Item, Sublet, ItemImage from market.permissions import ( IsSuperUser, OfferOwnerPermission, - SubletImageOwnerPermission, - SubletOwnerPermission, + ItemImageOwnerPermission, + ItemOwnerPermission, ) from market.serializers import ( - AmenitySerializer, + TagSerializer, OfferSerializer, - SubletImageSerializer, - SubletImageURLSerializer, - SubletSerializer, - SubletSerializerRead, - SubletSerializerSimple, + CategorySerializer, + ItemImageSerializer, + ItemImageURLSerializer, + ItemSerializer, + ItemSerializerRead, + ItemSerializerRead, + ItemSerializerSimple, ) from pennmobile.analytics import Metric, record_analytics @@ -29,9 +31,9 @@ User = get_user_model() -class Amenities(generics.ListAPIView): - serializer_class = AmenitySerializer - queryset = Amenity.objects.all() +class Tags(generics.ListAPIView): + serializer_class = TagSerializer + queryset = Tag.objects.all() def get(self, request, *args, **kwargs): temp = super().get(self, request, *args, **kwargs).data @@ -40,12 +42,12 @@ def get(self, request, *args, **kwargs): class UserFavorites(generics.ListAPIView): - serializer_class = SubletSerializerSimple + serializer_class = ItemSerializerSimple permission_classes = [IsAuthenticated] def get_queryset(self): user = self.request.user - return user.sublets_favorited + return user.items_favorited class UserOffers(generics.ListAPIView): @@ -60,33 +62,33 @@ def get_queryset(self): class Properties(viewsets.ModelViewSet): """ list: - Returns a list of Sublets that match query parameters (e.g., amenities) and belong to the user. + Returns a list of Items that match query parameters (e.g., amenities) and belong to the user. create: - Create a Sublet. + Create an Item. partial_update: - Update certain fields in the Sublet. Only the owner can edit it. + Update certain fields in the Item. Only the owner can edit it. destroy: - Delete a Sublet. + Delete an Item. """ - permission_classes = [SubletOwnerPermission | IsSuperUser] + permission_classes = [ItemOwnerPermission | IsSuperUser] def get_serializer_class(self): - return SubletSerializerRead if self.action == "retrieve" else SubletSerializer + return ItemSerializerRead if self.action == "retrieve" else ItemSerializer def get_queryset(self): - return Sublet.objects.all() + return Item.objects.all() def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) # Check if the data is valid - instance = serializer.save() # Create the Sublet - instance_serializer = SubletSerializerRead(instance=instance, context={"request": request}) + instance = serializer.save() # Create the Item + instance_serializer = ItemSerializerRead(instance=instance, context={"request": request}) - record_analytics(Metric.SUBLET_CREATED, request.user.username) + #record_analytics(Metric.SUBLET_CREATED, request.user.username) return Response(instance_serializer.data, status=status.HTTP_201_CREATED) @@ -105,7 +107,7 @@ def update(self, request, *args, **kwargs): # and then re-prefetch related objects instance._prefetched_objects_cache = {} prefetch_related_objects([instance], *queryset._prefetch_related_lookups) - return Response(SubletSerializerRead(instance=instance).data) + return Response(ItemSerializerRead(instance=instance).data) # This is currently redundant but will leave for use when implementing image creation # def create(self, request, *args, **kwargs): @@ -121,19 +123,19 @@ def update(self, request, *args, **kwargs): # serializer = self.get_serializer(data=new_data) # serializer.is_valid(raise_exception=True) - # sublet = serializer.save() - # sublet.amenities.set(amenities) - # sublet.save() + # item = serializer.save() + # item.amenities.set(amenities) + # item.save() # return Response(serializer.data, status=status.HTTP_201_CREATED) def list(self, request, *args, **kwargs): - """Returns a list of Sublets that match query parameters and user ownership.""" + """Returns a list of Items that match query parameters and user ownership.""" # Get query parameters from request (e.g., amenities, user_owned) params = request.query_params amenities = params.getlist("amenities") title = params.get("title") address = params.get("address") - subletter = params.get("subletter", "false") # Defaults to False if not specified + seller = params.get("seller", "false") # Defaults to False if not specified starts_before = params.get("starts_before", None) starts_after = params.get("starts_after", None) ends_before = params.get("ends_before", None) @@ -148,8 +150,8 @@ def list(self, request, *args, **kwargs): # Apply filters based on query parameters - if subletter.lower() == "true": - queryset = queryset.filter(subletter=request.user) + if seller.lower() == "true": + queryset = queryset.filter(seller=request.user) else: queryset = queryset.filter(expires_at__gte=timezone.now()) if title: @@ -178,46 +180,46 @@ def list(self, request, *args, **kwargs): if baths: queryset = queryset.filter(baths=baths) - record_analytics(Metric.SUBLET_BROWSE, request.user.username) + #record_analytics(Metric.SUBLET_BROWSE, request.user.username) # Serialize and return the queryset - serializer = SubletSerializerSimple(queryset, many=True) + serializer = ItemSerializerSimple(queryset, many=True) return Response(serializer.data) class CreateImages(generics.CreateAPIView): - serializer_class = SubletImageSerializer + serializer_class = ItemImageSerializer http_method_names = ["post"] - permission_classes = [SubletImageOwnerPermission | IsSuperUser] + permission_classes = [ItemImageOwnerPermission | IsSuperUser] parser_classes = ( MultiPartParser, FormParser, ) def get_queryset(self, *args, **kwargs): - sublet = get_object_or_404(Sublet, id=int(self.kwargs["sublet_id"])) - return SubletImage.objects.filter(sublet=sublet) + item = get_object_or_404(Item, id=int(self.kwargs["item_id"])) + return ItemImage.objects.filter(item=item) # takes an image multipart form data and creates a new image object def post(self, request, *args, **kwargs): images = request.data.getlist("images") - sublet_id = int(self.kwargs["sublet_id"]) - self.get_queryset() # check if sublet exists + item_id = int(self.kwargs["item_id"]) + self.get_queryset() # check if item exists img_serializers = [] for img in images: - img_serializer = self.get_serializer(data={"sublet": sublet_id, "image": img}) + img_serializer = self.get_serializer(data={"item": item_id, "image": img}) img_serializer.is_valid(raise_exception=True) img_serializers.append(img_serializer) instances = [img_serializer.save() for img_serializer in img_serializers] - data = [SubletImageURLSerializer(instance=instance).data for instance in instances] + data = [ItemImageURLSerializer(instance=instance).data for instance in instances] return Response(data, status=status.HTTP_201_CREATED) class DeleteImage(generics.DestroyAPIView): - serializer_class = SubletImageSerializer + serializer_class = ItemImageSerializer http_method_names = ["delete"] - permission_classes = [SubletImageOwnerPermission | IsSuperUser] - queryset = SubletImage.objects.all() + permission_classes = [ItemImageOwnerPermission | IsSuperUser] + queryset = ItemImage.objects.all() def destroy(self, request, *args, **kwargs): queryset = self.get_queryset() @@ -230,50 +232,50 @@ def destroy(self, request, *args, **kwargs): class Favorites(mixins.DestroyModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet): - serializer_class = SubletSerializer + serializer_class = ItemSerializer http_method_names = ["post", "delete"] permission_classes = [IsAuthenticated | IsSuperUser] def get_queryset(self): user = self.request.user - return user.sublets_favorited + return user.items_favorited def create(self, request, *args, **kwargs): - sublet_id = int(self.kwargs["sublet_id"]) + item_id = int(self.kwargs["item_id"]) queryset = self.get_queryset() - if queryset.filter(id=sublet_id).exists(): + if queryset.filter(id=item_id).exists(): raise exceptions.NotAcceptable("Favorite already exists") - sublet = get_object_or_404(Sublet, id=sublet_id) - self.get_queryset().add(sublet) + item = get_object_or_404(Item, id=item_id) + self.get_queryset().add(item) - record_analytics(Metric.SUBLET_FAVORITED, request.user.username) + #record_analytics(Metric.SUBLET_FAVORITED, request.user.username) return Response(status=status.HTTP_201_CREATED) def destroy(self, request, *args, **kwargs): queryset = self.get_queryset() - sublet = get_object_or_404(queryset, pk=int(self.kwargs["sublet_id"])) - self.get_queryset().remove(sublet) + item = get_object_or_404(queryset, pk=int(self.kwargs["item_id"])) + self.get_queryset().remove(item) return Response(status=status.HTTP_204_NO_CONTENT) class Offers(viewsets.ModelViewSet): """ list: - Returns a list of all offers for the sublet matching the provided ID. + Returns a list of all offers for the item matching the provided ID. create: - Create an offer on the sublet matching the provided ID. + Create an offer on the item matching the provided ID. destroy: - Delete the offer between the user and the sublet matching the ID. + Delete the offer between the user and the item matching the ID. """ permission_classes = [OfferOwnerPermission | IsSuperUser] serializer_class = OfferSerializer def get_queryset(self): - return Offer.objects.filter(sublet_id=int(self.kwargs["sublet_id"])).order_by( + return Offer.objects.filter(item_id=int(self.kwargs["item_id"])).order_by( "created_date" ) @@ -282,19 +284,19 @@ def create(self, request, *args, **kwargs): request.POST._mutable = True if self.get_queryset().filter(user=self.request.user).exists(): raise exceptions.NotAcceptable("Offer already exists") - data["sublet"] = int(self.kwargs["sublet_id"]) + data["item"] = int(self.kwargs["item_id"]) data["user"] = self.request.user.id serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() - record_analytics(Metric.SUBLET_OFFER, request.user.username) + #record_analytics(Metric.SUBLET_OFFER, request.user.username) return Response(serializer.data, status=status.HTTP_201_CREATED) def destroy(self, request, *args, **kwargs): queryset = self.get_queryset() - filter = {"user": self.request.user.id, "sublet": int(self.kwargs["sublet_id"])} + filter = {"user": self.request.user.id, "item": int(self.kwargs["item_id"])} obj = get_object_or_404(queryset, **filter) # checking permissions here is kind of redundant self.check_object_permissions(self.request, obj) @@ -302,5 +304,5 @@ def destroy(self, request, *args, **kwargs): return Response(status=status.HTTP_204_NO_CONTENT) def list(self, request, *args, **kwargs): - self.check_object_permissions(request, Sublet.objects.get(pk=int(self.kwargs["sublet_id"]))) + self.check_object_permissions(request, Item.objects.get(pk=int(self.kwargs["item_id"]))) return super().list(request, *args, **kwargs) diff --git a/backend/pennmobile/settings/base.py b/backend/pennmobile/settings/base.py index c54b3e21..4d73e450 100644 --- a/backend/pennmobile/settings/base.py +++ b/backend/pennmobile/settings/base.py @@ -53,6 +53,7 @@ "options.apps.OptionsConfig", "sublet", "phonenumber_field", + "market", ] MIDDLEWARE = [ diff --git a/backend/pennmobile/urls.py b/backend/pennmobile/urls.py index 5e94960e..3387caf2 100644 --- a/backend/pennmobile/urls.py +++ b/backend/pennmobile/urls.py @@ -27,6 +27,7 @@ path("dining/", include("dining.urls")), path("penndata/", include("penndata.urls")), path("sublet/", include("sublet.urls")), + path("market/", include("market.urls")), ] urlpatterns = [