Skip to content

Commit

Permalink
Serializers are better now
Browse files Browse the repository at this point in the history
  • Loading branch information
ineshbose committed Dec 22, 2021
1 parent df9c1dd commit f230e96
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 74 deletions.
120 changes: 60 additions & 60 deletions src/backend/main/signals.py
Original file line number Diff line number Diff line change
@@ -1,60 +1,60 @@
import string
import secrets

from django.db.models import signals, Max
from django.dispatch import receiver
from django.contrib.auth.hashers import make_password

from . import models


@receiver([signals.pre_init], sender=models.User)
def load_user_fixture(sender, *args, **kwargs):
if (
isinstance(kwargs.get("kwargs"), dict)
and kwargs["kwargs"].get("email")
and kwargs["kwargs"].get("id") # Comment out when id is not serialized
):
if not kwargs["kwargs"].get("id"):
kwargs["kwargs"]["id"] = (
models.User.objects.aggregate(Max("id")).get("id__max", 0) + 1
)

if not kwargs["kwargs"].get("password"):
password = "".join(
secrets.choice(string.ascii_letters + string.digits) for _ in range(10)
)
kwargs["kwargs"]["password"] = make_password(password)

print(f"User {kwargs['kwargs']['id']}: {kwargs['kwargs']['email']} {password}")


def user_created_hook(instance, *args, **kwargs):
EATWELL_MAP = {
"Carbohydrates": 6,
"Fruits & Vegetables": 6,
"Protein": 3,
"Dairy": 3,
"Oils & Fats": 1,
}

for portion_item in models.PortionItem.objects.filter(is_default=True):
models.TrackItem.objects.create(
item=portion_item,
user=instance,
target=EATWELL_MAP.get(portion_item.name, 1),
order=portion_item.id,
)


def user_updated_hook(instance, *args, **kwargs):
pass


@receiver([signals.post_save], sender=models.User)
def user_post_save(sender, instance, *args, **kwargs):
(
user_created_hook
if (kwargs.get("created") or kwargs.get("raw"))
else user_updated_hook
)(instance, *args, **kwargs)
import string
import secrets

from django.db.models import signals, Max
from django.dispatch import receiver
from django.contrib.auth.hashers import make_password

from . import models


@receiver([signals.pre_init], sender=models.User)
def load_user_fixture(sender, *args, **kwargs):
if (
isinstance(kwargs.get("kwargs"), dict)
and kwargs["kwargs"].get("email")
and kwargs["kwargs"].get("id") # Comment out when id is not serialized
):
if not kwargs["kwargs"].get("id"):
kwargs["kwargs"]["id"] = (
models.User.objects.aggregate(Max("id")).get("id__max", 0) + 1
)

if not kwargs["kwargs"].get("password"):
password = "".join(
secrets.choice(string.ascii_letters + string.digits) for _ in range(10)
)
kwargs["kwargs"]["password"] = make_password(password)

print(f"User {kwargs['kwargs']['id']}: {kwargs['kwargs']['email']} {password}")


def user_created_hook(instance, *args, **kwargs):
EATWELL_MAP = {
"Carbohydrates": 6,
"Fruits & Vegetables": 6,
"Protein": 3,
"Dairy": 3,
"Oils & Fats": 1,
}

for portion_item in models.PortionItem.objects.filter(is_default=True):
models.TrackItem.objects.update_or_create(
item=portion_item,
user=instance,
target=EATWELL_MAP.get(portion_item.name, 1),
order=portion_item.id,
)


def user_updated_hook(instance, *args, **kwargs):
pass


@receiver([signals.post_save], sender=models.User)
def user_post_save(sender, instance, *args, **kwargs):
(
user_created_hook
if (kwargs.get("created") or kwargs.get("raw"))
else user_updated_hook
)(instance, *args, **kwargs)
10 changes: 10 additions & 0 deletions src/backend/rest/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from rest_framework.permissions import BasePermission


class IsAdminOrIsSelf(BasePermission):
def has_object_permission(self, request, view, obj):
return (
getattr(obj, 'user', obj) == request.user
or request.user.is_staff
or request.user.is_superuser
)
83 changes: 75 additions & 8 deletions src/backend/rest/serializers.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,102 @@
from datetime import timedelta
from django.utils import timezone
from rest_framework import serializers
from main import models


class UserSerializer(serializers.HyperlinkedModelSerializer):
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""
A ModelSerializer that takes an additional `fields` argument that
controls which fields should be displayed.
Source: https://www.django-rest-framework.org/api-guide/serializers/
"""

def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop("fields", None)

# Don't pass the 'exclude' arg up to the superclass
exclude = kwargs.pop("exclude", None)

# Instantiate the superclass normally
super().__init__(*args, **kwargs)

if fields is not None:
# Drop any fields that are not specified in the `fields` argument.
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)

if exclude is not None:
# Drop any fields that are specified in the `exclude` argument.
for field_name in exclude:
if field_name in self.fields:
self.fields.pop(field_name)


class UserSerializer(DynamicFieldsModelSerializer):
items = serializers.SerializerMethodField()

def get_items(self, obj):
return TrackItemSerializer(
obj.trackitem_set, many=True, exclude=["user", "logs"]
).data

class Meta:
model = models.User
fields = [
"url",
"id",
"email",
"first_name",
"last_name",
"picture",
"age",
"height",
"weight",
"items",
]


class PortionItemSerializer(serializers.HyperlinkedModelSerializer):
class PortionItemSerializer(DynamicFieldsModelSerializer):
class Meta:
model = models.PortionItem
fields = ["url", "name", "is_default"]
fields = ["id", "name", "is_default"]


class TrackItemSerializer(DynamicFieldsModelSerializer):
item = PortionItemSerializer()
logs = serializers.SerializerMethodField()

def get_logs(self, obj):
return UserLogSerializer(
obj.userlog_set.filter(
timestamp__gte=timezone.now().replace(
hour=0, minute=0, second=0, microsecond=0
)
- timedelta(days=obj.frequency)
),
many=True,
exclude=["item"],
).data

class TrackItemSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.TrackItem
fields = ["url", "item", "user", "target", "order", "frequency"]
fields = [
"id",
"item",
"user",
"target",
"order",
"frequency",
"logs",
]


class UserLogSerializer(DynamicFieldsModelSerializer):
item = TrackItemSerializer(exclude=["logs"])

class UserLogSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.UserLog
fields = ["url", "item", "timestamp"]
fields = ["id", "item", "timestamp"]
46 changes: 40 additions & 6 deletions src/backend/rest/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from rest_framework import viewsets
from rest_framework import permissions
from rest_framework import permissions as drf_permissions

from rest import serializers
from rest import permissions
from main import models


Expand All @@ -12,17 +13,36 @@ class UserViewSet(viewsets.ModelViewSet):

queryset = models.User.objects.all().order_by("-date_joined")
serializer_class = serializers.UserSerializer
permission_classes = [permissions.IsAuthenticated]

def get_permissions(self):
if self.action in ["list"]:
self.permission_classes = [drf_permissions.IsAdminUser]

elif self.action in ["retrieve"]:
self.permission_classes = [permissions.IsAdminOrIsSelf]

return super(self.__class__, self).get_permissions()


class PortionItemViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows portion items to be viewed or edited.
"""

queryset = models.PortionItem.objects.filter(is_default=True)
queryset = models.PortionItem.objects.all().order_by("id")
serializer_class = serializers.PortionItemSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
permission_classes = [drf_permissions.IsAuthenticatedOrReadOnly]

def get_queryset(self):
return (
self.queryset.filter(
id__in=models.TrackItem.objects.filter(
user=self.request.user
).values_list("item", flat=True)
)
if self.request.user and self.request.user.is_authenticated
else self.queryset.filter(is_default=True)
)


class TrackItemViewSet(viewsets.ModelViewSet):
Expand All @@ -32,7 +52,14 @@ class TrackItemViewSet(viewsets.ModelViewSet):

queryset = models.TrackItem.objects.all().order_by("order")
serializer_class = serializers.TrackItemSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [drf_permissions.IsAuthenticated]

def get_queryset(self):
return (
self.queryset
if self.request.user.is_staff
else self.queryset.filter(user=self.request.user)
)


class UserLogViewSet(viewsets.ModelViewSet):
Expand All @@ -42,4 +69,11 @@ class UserLogViewSet(viewsets.ModelViewSet):

queryset = models.UserLog.objects.all().order_by("-timestamp")
serializer_class = serializers.UserLogSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [drf_permissions.IsAuthenticated]

def get_queryset(self):
return (
self.queryset
if self.request.user.is_staff
else self.queryset.filter(item__user=self.request.user)
)

0 comments on commit f230e96

Please sign in to comment.