Skip to content

Commit

Permalink
add previous sublet stuff back
Browse files Browse the repository at this point in the history
  • Loading branch information
vcai122 committed Oct 22, 2024
1 parent b726fe8 commit 124f751
Show file tree
Hide file tree
Showing 6 changed files with 678 additions and 0 deletions.
19 changes: 19 additions & 0 deletions backend/sublet/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.contrib import admin
from django.utils.html import mark_safe

from sublet.models import Amenity, Offer, Sublet, SubletImage


class SubletAdmin(admin.ModelAdmin):
def image_tag(self, instance):
images = ['<img src="%s" height="150" />' for image in instance.images.all()]
return mark_safe("<br>".join(images))

image_tag.short_description = "Sublet Images"
readonly_fields = ("image_tag",)


admin.site.register(Offer)
admin.site.register(Amenity)
admin.site.register(Sublet, SubletAdmin)
admin.site.register(SubletImage)
58 changes: 58 additions & 0 deletions backend/sublet/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from django.contrib.auth import get_user_model
from django.db import models
from phonenumber_field.modelfields import PhoneNumberField


User = get_user_model()


class Offer(models.Model):
class Meta:
constraints = [models.UniqueConstraint(fields=["user", "sublet"], name="unique_offer")]

user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="offers_made")
sublet = models.ForeignKey("Sublet", on_delete=models.CASCADE, related_name="offers")
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)

def __str__(self):
return f"Offer for {self.sublet} made by {self.user}"


class Amenity(models.Model):
name = models.CharField(max_length=255, primary_key=True)

def __str__(self):
return self.name


class Sublet(models.Model):
subletter = models.ForeignKey(User, on_delete=models.CASCADE)
sublettees = models.ManyToManyField(
User, through=Offer, related_name="sublets_offered", blank=True
)
favorites = models.ManyToManyField(User, related_name="sublets_favorited", blank=True)
amenities = models.ManyToManyField(Amenity, blank=True)

title = models.CharField(max_length=255)
address = models.CharField(max_length=255, null=True, blank=True)
beds = models.IntegerField(null=True, blank=True)
baths = models.DecimalField(max_digits=3, decimal_places=1, null=True, blank=True)
description = models.TextField(null=True, blank=True)
external_link = models.URLField(max_length=255, null=True, blank=True)
price = models.IntegerField()
negotiable = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
expires_at = models.DateTimeField()
start_date = models.DateField()
end_date = models.DateField()

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


class SubletImage(models.Model):
sublet = models.ForeignKey(Sublet, on_delete=models.CASCADE, related_name="images")
image = models.ImageField(upload_to="sublet/images")
57 changes: 57 additions & 0 deletions backend/sublet/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from rest_framework import permissions


class IsSuperUser(permissions.BasePermission):
"""
Grants permission if the current user is a superuser.
"""

def has_object_permission(self, request, view, obj):
return request.user.is_superuser

def has_permission(self, request, view):
return request.user.is_superuser


class SubletOwnerPermission(permissions.BasePermission):
"""
Custom permission to allow the owner of a Sublet to edit or delete it.
"""

def has_permission(self, request, view):
return request.user.is_authenticated

def has_object_permission(self, request, view, obj):
# Check if the user is the owner of the Sublet.
if request.method in permissions.SAFE_METHODS:
return True
return obj.subletter == request.user


class SubletImageOwnerPermission(permissions.BasePermission):
"""
Custom permission to allow the owner of a SubletImage to edit or delete it.
"""

def has_permission(self, request, view):
return request.user.is_authenticated

def has_object_permission(self, request, view, obj):
# Check if the user is the owner of the Sublet.
return request.method in permissions.SAFE_METHODS or obj.sublet.subletter == request.user


class OfferOwnerPermission(permissions.BasePermission):
"""
Custom permission to allow owner of an offer to delete it.
"""

def has_permission(self, request, view):
return request.user.is_authenticated

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
return obj.subletter == request.user
# This is redundant, here for safety
return obj.user == request.user
189 changes: 189 additions & 0 deletions backend/sublet/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
from phonenumber_field.serializerfields import PhoneNumberField
from profanity_check import predict
from rest_framework import serializers

from sublet.models import Amenity, Offer, Sublet, SubletImage


class AmenitySerializer(serializers.ModelSerializer):
class Meta:
model = Amenity
fields = "__all__"


class OfferSerializer(serializers.ModelSerializer):
phone_number = PhoneNumberField()

class Meta:
model = Offer
fields = "__all__"
read_only_fields = ["id", "created_date", "user"]

def create(self, validated_data):
validated_data["user"] = self.context["request"].user
return super().create(validated_data)


# Create/Update Image Serializer
class SubletImageSerializer(serializers.ModelSerializer):
image = serializers.ImageField(write_only=True, required=False, allow_null=True)

class Meta:
model = SubletImage
fields = ["sublet", "image"]


# Browse images
class SubletImageURLSerializer(serializers.ModelSerializer):
image_url = serializers.SerializerMethodField("get_image_url")

def get_image_url(self, obj):
image = obj.image

if not image:
return None
if image.url.startswith("http"):
return image.url
elif "request" in self.context:
return self.context["request"].build_absolute_uri(image.url)
else:
return image.url

class Meta:
model = SubletImage
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
)

class Meta:
model = Sublet
read_only_fields = [
"id",
"created_at",
"subletter",
"sublettees",
# "images"
]
fields = [
"id",
"subletter",
"amenities",
"title",
"address",
"beds",
"baths",
"description",
"external_link",
"price",
"negotiable",
"start_date",
"end_date",
"expires_at",
# "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
]

def validate_title(self, value):
if self.contains_profanity(value):
raise serializers.ValidationError("The title contains inappropriate language.")
return value

def validate_description(self, value):
if self.contains_profanity(value):
raise serializers.ValidationError("The description contains inappropriate language.")
return value

def contains_profanity(self, text):
return predict([text])[0]

def create(self, validated_data):
validated_data["subletter"] = 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
if (
self.context["request"].user == instance.subletter
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.")

def destroy(self, instance):
# Check if the user is the subletter before allowing the delete
if (
self.context["request"].user == instance.subletter
or self.context["request"].user.is_superuser
):
instance.delete()
else:
raise serializers.ValidationError("You do not have permission to delete this sublet.")


class SubletSerializerRead(serializers.ModelSerializer):
amenities = serializers.PrimaryKeyRelatedField(
many=True, queryset=Amenity.objects.all(), required=False
)
images = SubletImageURLSerializer(many=True, required=False)

class Meta:
model = Sublet
read_only_fields = ["id", "created_at", "subletter", "sublettees"]
fields = [
"id",
"subletter",
"amenities",
"title",
"address",
"beds",
"baths",
"description",
"external_link",
"price",
"negotiable",
"start_date",
"end_date",
"expires_at",
"images",
]


# simple sublet serializer for use when pulling all serializers/etc
class SubletSerializerSimple(serializers.ModelSerializer):
amenities = serializers.PrimaryKeyRelatedField(
many=True, queryset=Amenity.objects.all(), required=False
)
images = SubletImageURLSerializer(many=True, required=False)

class Meta:
model = Sublet
fields = [
"id",
"subletter",
"amenities",
"title",
"address",
"beds",
"baths",
"price",
"negotiable",
"start_date",
"end_date",
"images",
]
read_only_fields = ["id", "subletter"]
49 changes: 49 additions & 0 deletions backend/sublet/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from django.urls import path
from rest_framework import routers

from sublet.views import (
Amenities,
CreateImages,
DeleteImage,
Favorites,
Offers,
Properties,
UserFavorites,
UserOffers,
)


app_name = "sublet"

router = routers.DefaultRouter()
router.register(r"properties", Properties, basename="properties")

additional_urls = [
# List of all amenities
path("amenities/", Amenities.as_view(), name="amenities"),
# 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
path(
"properties/<sublet_id>/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
path(
"properties/<sublet_id>/offers/",
Offers.as_view({"get": "list", "post": "create", "delete": "destroy"}),
),
# Image Creation
path("properties/<sublet_id>/images/", CreateImages.as_view()),
# Image Deletion
path("properties/images/<image_id>/", DeleteImage.as_view()),
]

urlpatterns = router.urls + additional_urls
Loading

0 comments on commit 124f751

Please sign in to comment.