- Överanvänd inte SerializerMethodField
- När du mappar om fält, glöm inte bort "source"
- Undvik att overrida lifecycle i vyer
- Om du inte använder ModelSerializer, se över vad du döper dina argument
- Var sparsam med context och använd bara i nödfall
- Se context som ett argument och använd bara fallback i undantagsfall
- Har du logik i en serialiserare, skriv ett test!
Använd dom bara när du vill transformera data.
-
👎 här är
SerializerMethodField
omotiveradclass UserSerializer(serializers.ModelSerializer): id = serializers.SerializerMethodField() class Meta: model = User fields = ["id"] def get_id(self, obj): return obj.id
-
👍 här är allt deklarerat i metaklassen
class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ["id"]
-
👍 här använder vi SerializerMethodField för att transformerar
class UserSerializer(serializers.ModelSerializer): full_name = serializers.SerializerMethodField() class Meta: model = User fields = ["full_name"] def get_full_name(self, obj): return f"{obj.first_name} {obj.last_name}"
-
👎 onödig SerializerMethodField
class ProductSerializer(serializers.ModelSerializer): title = serializers.SerializerMethodField() class Meta: model = Product fields = ["contact_email"] def get_title(self, obj): return obj.text
-
👍 här använder vi source för att mappa om fältnamnet
class ProductSerializer(serializers.ModelSerializer): title = serializers.CharField(source="text") class Meta: model = Product fields = ["title"]
-
👎 onödig SerializerMethodField
class ProductSerializer(serializers.ModelSerializer): producer = serializers.SerializerMethodField() class Meta: model = Product fields = ["producer"] def get_producer(self, obj): return obj.company.producer
-
👍 här använder vi också nästling fast genom relationen
class ProductSerializer(serializers.ModelSerializer): producer = serializers.CharField(source="company.producer") class Meta: model = Product fields = ["producer"]
...serializers kan göra jobbet
-
👎 här implementerar vi i vyn vad som redan finns i serialiseraren
# my_app/views.py class ProductCreateView(CreateAPIView): def post(self, request, *args, **kwargs): serializer = ProductSerializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response({}, status=status.HTTP_201_CREATED) # my_app/serializers.py class ProductSerializer(ModelSerializer): class Meta: model = Product fields = ("title", "price", "color")
-
👍 här utnyttjar vi mappningen mellan
CreateAPIView
ochModelSerializer
för att skapa en ny modell# my_app/views.py class ProductCreateView(CreateAPIView): serializer_class = ProductSerializer # my_app/serializers.py class ProductSerializer(ModelSerializer): class Meta: model = Product fields = ("title", "price", "color")
-
👎 vad innebär
obj
iget_last_logged_in
?class CreateUserSerializer(serializers.Serializer): def get_last_logged_in(self, obj): return obj.history.order_by("create").first()
-
👍 genom att ersätta obj med user så blir det tydligare vad det är för data vi jobbar med
class CreateUserSerializer(serializers.Serializer): def get_last_logged_in(self, user): return user.history.order_by("create").first()
-
👎 requesten har inget med serialiseraren att göra, detta kan man göra någon annan stans
# views.py def get_activity_page(self, request, activity_id): activity = Activity.objects.get(id=activity_id) return ActivitySerializer( activity, context={"request": request} ).data # my_app/serializers.py class ActivitySerializer(ModelSerializer): ... def get_domain(self, obj): request = self.context.get("request") site = request.site return site.domain
-
👍 domain har inget med activity att göra, så vi returerar den i nivån ovanför
# views.py def get_activity_page(self, request, activity_id): activity = Activity.objects.get(id=activity_id) return { "activity": ActivitySerializer(activity).data, "domain": request.site.domain, } # my_app/serializers.py class ActivitySerializer(ModelSerializer): ...
-
👎 request är ett onödigt lager för att nå property
user
# helpers.py def activity_to_props(self): return ActivitySerializer( activity, context={"request": self.request} ).data # my_app/serializers.py class ActivitySerializer(ModelSerializer): ... def get_has_activity(self, obj): request = self.context.get("request") user = request.user return UserActivity.objects.exists(user=user)
-
👍 vi passar här in endast det serialiseraren behöver för att fungera
# helpers.py def activity_to_props(self): return ActivitySerializer( activity, context={"user": self.request.user} ).data # my_app/serializers.py class ActivitySerializer(ModelSerializer): ... def get_has_activity(self, obj): user = self.context.get("user") return UserActivity.objects.exists(user=user)
-
👎 genom att falla tillbaka på None när user inte finns gör vi det svårare att felsöka
get_has_activity
då den nu har två skäl att returera False.# my_app/serializers.py class ActivitySerializer(ModelSerializer): ... def get_has_activity(self, obj): user = self.context.get("user", None) if not user: return False return UserActivity.objects.exists(user=user)
-
👍 nu finns det bara en anledning till
get_has_activity
att returera False. Vi kommer också att märka om user ej är nerpassad genom en exception, vilket ger oss ett tydligare kontrakt mot serialiseraren.# my_app/serializers.py class ActivitySerializer(ModelSerializer): ... def get_has_activity(self, obj): user = self.context.get("user") return UserActivity.objects.exists(user=user)
-
Har du fält i din serialiserare med logik, skriv ett test för att underlätta refakturering.
# my_app/serializers.py class OrderSerializer(ModelSerializer): ... def get_shipping_cost(self, obj): if obj.shipping_option.pickup_in_store: return 0 if obj.shipping_option.type == ShippingType.INSTABOX: return 0 weight = obj.total_weight if weight > 2: return 200 return 50
-
Ett test kan se ut så här:
from django.test import TestCase from .serializers import OrderSerializer from .factories import OrderFactory def OrderShippingTest(TestCase): def test_that_order_shipping_cost_is_correct(self): self.assertEqual( OrderSerializer( OrderFactory(shipping_option__pickup_in_store=True) ).data["shipping_cost"], 0 ) self.assertEqual( OrderSerializer( OrderFactory(shipping_option__type="INSTABOX") ).data["shipping_cost"], 0 ) self.assertEqual( OrderSerializer( OrderFactory(shipping_option__type="INSTABOX", total_weight=3) ).data["shipping_cost"], 0 )