R6 Do You Need to Know Callouts Before Ranked

In this article I want to share some ideas and tricks that I got to know while working with Django Residual Framework in Profile Software - Python Software House. Django Remainder Framework, created by Tom Christie, is probably the most endorsed packet for building RESTful APIs in Django. Examples shown here are compatible with version three of DRF.

1.png

1. Viewsets

Let'south start with a simple (thus one of my favorites) DRF functionality. Viewsets tin can be considered as extremely useful templates for your API views that provide typical interactions with your Django models. While regular views act as handlers for HTTP methods, viewsets requite you deportment, like create or list. The smashing thing almost viewsets is how they brand your code consistent and save you from repetition. Every time you lot write views that should do more than one affair, a viewset is the thing that you want to go for.

Allow'due south imagine there is a Tag model in your project and yous need to prepare a functionality that will permit your users: listing all the tags, create a new tag and retrieve its details. Here's how you can define a viewset for that:

class TagViewSet(
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
GenericViewSet
):
"""
The post-obit endpoints are fully provided past mixins:
* Listing view
* Create view
"""
queryset = Tag.objects.all()
serializer_class = TagSerializer
permission_classes = (permissions.IsAuthenticated,)

Viewset mixins tin be combined as needed. Y'all can define your ain mixins or apply ModelViewSet, which provides the post-obit actions: .list(), .recollect(), .create(), .update(), .partial_update(), and .destroy() .

In improver, when using viewsets you lot typically desire to use routers for url configuration. This enforces all-time practices in naming your ulrs, making your API urls easily predictable.

from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter

api_router = DefaultRouter()
api_router.register(r'tag', TagViewSet, 'tag')

urlpatterns = [
url(r'^v1/', include(api_router.urls, namespace='v1'))
]

Now your viewset is functional plenty that you lot can:

  • list all your tags past sending Go request to v1/tag/,
  • POST new tag to v1/tag/ or
  • retrieve one of the tags by Become v1/tag/<tag_id>.

Yous tin can even add together some custom action to your viewset using @action decorator.

Now that writing views is finished, yous've saved enough fourth dimension to have a cup of coffee.

ii. Understanding different types of serializers

As a DRF user you don't demand to bother with views and url configurations, so you will probably pay nearly of your attention to serializers, which act equally translators betwixt Django model instances and their representations such as json. In that location is a handful of functionalities continued with serializers that you might desire to know.

Every serializer can be used for both reading and writing operations. The way it is initialized determines the action that it will fulfill. In these terms we tin can distinguish 3 types of serializers: create, update and retrieve.

If in your view you desire to serialize data that will be transmitted outside of your API, this is how you lot practice it:

def retrieve(cocky, request, *args, **kwargs):
case = self.get_object()
serializer = ProfileSerializer(instance=instance)
return Response(serializer.data)

But in your create view you will define information technology in a different style:

def create(self, request, *args, **kwargs):
serializer = ProfileSerializer(data=asking.data)
serializer.is_valid(raise_exception=True)
serializer.relieve()
return Response(serializer.data)

And finally, when updating an case, you need to provide case also equally data:

def update(self, request, *args, **kwargs):
instance = self.get_object()
serializer = ProfileSerializer(
case=instance,
information=request.data
)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)

serializer.save() invokes an advisable internal method based on arguments passed at initialization.

3. Using SerializerMethodField

SerializerMethodField is a read only field that computes its value at request processing time, by calling a method on the serializer form it is attached to. Permit's say you accept a model that stores datetime in a models.DateTimeField, just y'all want to use timestamp from epoch in your serialized representation:

from rest_framework import serializers

form TagSerializer(serializers.ModelSerializer):
created = serializers.SerializerMethodField()

course Meta:
model = Tag
fields = ('label', 'created')

def get_created(cocky, obj):
return circular(obj.created.timestamp())

SerializerMethodField accepts method_name, but it's normally more convenient to employ the default design for naming those methods, which is get_<field_name>. Only make sure you're not overburdening your method fields with whatsoever heavy-lifting operations.

four. Using the source parameter

Very often, your model field names will differ from the required representation. Using the serializer field source parameter will allow you handle this easily. Take a look at this example:

from rest_framework import serializers

course TaskSerializer(serializers.ModelSerializer):
job_type = serializers.CharField(source='task_type')

class Meta:
model = Task
fields = ('job_type',)

The field task_type is in the Task model, only it will exist represented in your API every bit a job_type. This works for both read and write operations.

Moreover, you can apply it to fetch information from related objects using dotted notation:

owner_email = serializers.CharField(source='owner.electronic mail')

v. Serializer field validation

Aside from a validators argument that can exist passed when initializing a serializer field and aserializer.validate() claw, at that place is besides field-level validation, which allows you to write a unique validation method for each field separately. At that place are two reasons I observe it useful: first, information technology decouples different checks that are related only to a particular field, and second, information technology generates well formatted fault responses. Usage of this kind of validation is similar to using SerializerMethodField, but this time you have to follow a method naming convention: def validate_<field_name>. Hither's an case:

from rest_framework import serializers

course TransactionSerializer(serializers.ModelSerializer):
bid = serializers.IntegerField()

def validate_bid(self, bid: int) -> int:
if bid > self.context['request'].user.available_balance:
raise serializers.ValidationError(
_('Bid is greater than your balance')
)
return bid

If the bid exceeds the user's balance, this is how the response should await:

{
"bid": ["Bid is greater than your balance"]
}

Validation methods must always return a value, which is afterwards passed to a model instance. Keep in mind that field level validation is invoked past serializer.to_internal_value(), which takes place before calling serializer.validate().

6. Passing a value directly to the relieve method

In some cases information technology is convenient to pass a value from outside of a serializer directly to its save() method. This method will take arguments that tin can be equated with serialized objects. Values passed this way won't be validated. It may be used to force an override of the initial data. For instance:

serializer = EmailSerializer(data=asking.information)
serializer.is_valid(raise_exception=True)
serializer.salvage(owner_id=asking.user.id)

vii. Using CurrentUserDefault

When it comes to automatically setting a user as a resources possessor, there is an even a ameliorate way than the one presented in the previous instance. It's past using the CurrentUserDefault class, which doesn't require any override of views.

from rest_framework import serializers

form EmailSerializer(serializers.ModelSerializer):
owner = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)

It does two things. First, the user authenticated in the request object volition be fix as default. 2nd, because of using HiddenField, any incoming data is not taken into account, so it'due south impossible to set another user as an owner.

8. Serializers initial information

Sometimes you may need to access a serializer's raw input. Information technology's either because data has been already modified by running serializer.is_valid(), or it's needed to compare the value of some other field in a validation method when validated_data is not yet available. Information technology can be achieved by accessing serializer.initial_data, which stores raw input as a Dict, every bit shown in this example:

from rest_framework import serializers

grade SignupSerializer(serializers.ModelSerializer):
password1 = serializers.CharField()
password2 = serializers.CharField()

def validate_password1(cocky, password1):
if password1 != self.initial_data['password2']:
enhance serializers.ValidationError(
'Passwords practise non match'
)

9. Handling multiple creates/updates/deletes in nested serializers

Nearly of the time serializers are completely straightforward and with some experience, in that location's nil that could go wrong. Notwithstanding, there are some limitations. Things can become a little tricky when you have to support multiple creates, updates and deletes in nested serializers inside one high-level serializer. It comes with a trade-off: at that place is a smaller number of requests to process at the cost of a longer processing fourth dimension. By default, DRF doesn't support multiple updates at all. Information technology's hard to imagine how it could back up all possible types of nested insertions and deletions. That's why the creators of DRF chose flexibility over an out-of-the-box "do-everything" solution, and left that privilege for us.

There are two paths yous can follow in this case:

  • use the quite popular, 3rd political party library DRF Writable Nested
  • exercise information technology on your ain

I would recommend choosing the second option at least one time, so y'all will know what's going underneath.

After analyzing incoming data, in near scenarios, we are able to brand the following assumptions:

  • all items that should be updated have id,
  • all items that should exist created don't have id,
  • all items that should be deleted are present in information storage (eg. database), only are missing in the incoming request.data

Based on this, we know what to exercise with particular items on the list. Below is a snippet that shows this process in particular:

course CUDNestedMixin(object):
@staticmethod
def cud_nested(
queryset: QuerySet,
data: List[Dict],
serializer: Type[Serializer],
context: Dict
):
"""
Logic for handling multiple updates, creates and deletes on nested resources
:param queryset: queryset for objects existing in DB
:param data: initial data to validate passed from higher level serializer to nested serializer
:param serializer: nested serializer to utilise
:param context: context passed from college level serializer
:render N/A
"""
updated_ids = list()
for_create = list()
for item in data:
item_id = item.go('id')
if item_id:
case = queryset.get(id=item_id)
update_serializer = serializer(
instance=instance,
data=item,
context=context
)
update_serializer.is_valid(raise_exception=True)
update_serializer.save()
updated_ids.suspend(instance.id)
else:
for_create.suspend(detail)

delete_queryset = queryset.exclude(id__in=updated_ids)
delete_queryset.delete()

create_serializer = serializer(
data=for_create,
many=Truthful,
context=context
)
create_serializer.is_valid(raise_exception=True)
create_serializer.salvage()

And here is the simplified version of how a high-level serializer can make use of this mixin:

from rest_framework import serializers

class AccountSerializer(serializers.ModelSerializer, CUDNestedMixin):
phone_numbers = PhoneSerializer(
many=True,
source='phone_set',
)

course Meta:
model = User
fields = ('first_name', 'last_name', 'phone_numbers')

def update(self, instance, validated_data):
self.cud_nested(
queryset=example.phone_set.all(),
data=self.initial_data['phone_numbers'],
serializer=PhoneSerializer,
context=self.context
)
...
return instance

Keep in heed that nested objects should eat initial_data instead of validated_data. That's because running validation calls field.to_internal_value() on each of a serializer's fields, which may modify data stored by a detail field (eg. by irresolute primary key to model instance).

10. Override data to force ordering

By default, Django querysets are not ordered at all. Enforcing ordering on the list view can easily be accomplished by calculation ordering to the view's queryset, just in cases where nested resources should also exist ordered, it's not so simple. For read-only fields, information technology can be done within SerializerMethodField, but what to do in a state of affairs where a field has to be writable? In such a instance, a serializer's information property tin can exist overridden, every bit shown in this example:

@property
def data(self):
information = super().data
data['phone_numbers'].sort(central=lambda p: p['id'])
return data

Conclusion

I hope that you've found some new interesting techniques in this article. There are plenty of other functionalities that I would like to encompass in another article on this blog, then it'southward worth coming back here.

Exercise you know any DRF tricks that y'all want to share? Postal service your ideas in the comment section!

paulinctiary.blogspot.com

Source: https://profil-software.com/blog/development/10-things-you-need-know-effectively-use-django-rest-framework/

0 Response to "R6 Do You Need to Know Callouts Before Ranked"

Postar um comentário

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel