Django rest framework where to write complex logic in serializer.py or views.py? - django

I am new to Django Rest Framework. Using serializer and views a simple CRUD is easy. When the logics increase, it is quite confusing where to write logics in serializer or views.
Some developers do prefer "Thick serializer and thin views" and some developers "Thick views and thin serializer".
Maybe this is not a big issue and I think it is up to the developer whether to write more on views or serializer, but as a newbie what will be your suggestion to follow? Should I write more on views or serializer?
There are many answers on Django View Template but can not find a satisfying answer for Django Rest Framework.
Thoughts of experienced developers will be highly appreciated. Thank you.

Personally I prefer to have the business logic separated from both view and serializer. I usually create a new class with the business logic which can be used in both serializer and view based on necessity. Basically I treat it as a service. Reason for that is:
Makes the code cleaner(no thick and thin stuff).
Writing tests for those business logic is easier.
You can use that this business logic service in both view and serializer, depending on your need.
while testing APIs, you can mock those buiness logic if needed.
Reuse any logic in multiple places.
An example would be like this:
class BusinessLogicService(object):
def __init__(self, request):
self.request = request
def do_some_logical_ops(self, data_required_one, data_required_two):
# do processing
return processed_data
Example usage in serializer:
class SomeSerializer(serializer.Serialize):
...
def create(self, validated_data):
business_logic_data = BusinessLogicService(self.request).do_some_logical_ops(**validated_data)
return Model.objects.create(**business_logic_data)

I've asked a similar question about this before. Actually, it depends on what logic you are going to adapt. After doing further research, I have come up with some approaches. Here are my suggestions:
If you want to add some logic before doing serializer validation, it is better to include that in your view (eg. override your views create method). An example to this case would be; your POST body does not contain a value that is needed by the serializer hence is not valid yet, so add that in your view's create function.
If you are doing some custom authentication logic, such as parsing a custom token in your http header, do it in your view as well, because serializer has nothing to do with it. Moreover, you can create your own authentication decorator for that.
If you want to add logic which is directly related to the representation of your data, such as an adaptation of a timestamp from UTC to some other, you can add in your serializer as it is directly related to your object representation. You can use SerializerMethodField etc. to do that.

I try to segregate it on the basis of requirements of the context. Like:
If I'm supposed to deal with the request object(like current user), I try to implement that in view.
If I need to deal with querying models to create a view I would switch to serializer.
It may also depend upon how I'm planning to maintain the code (If I have huge number of views in a single file, I will try to avoid implementing logical stuffs as much as possible).

Related

Should small specific views be implemented with Function Based views or CBVs?

I'm developing an app in Django and would like to know what is the correct way to implement some small views that need to implement some specific function. I'm still a beginner and I've been using CBVs from the start but I'm not sure if I should use FBVs for this.
I now need to implement some specific functions when integrating Stripe, for example, to reactive a canceled subscription or to upgrade a subscription and was wondering if I should use FBVs for this?
If not, should I for example use the POST of
class SubscriptionView(APIView):
def post(self, request):
# Make a new subscription...
that I use to create a subscription and just check if the user is trying to reactive/upgrade with a parameter or something like that?
Consider this chart if you are indecisive on what type of method to use.
But generally some of the advantages of using CBVs on my experience are:
They can be easily extended
Code re usability
Remove branching through the use of class methods, hence clean code.

Does the model, the view or the serializer represent a REST resource in Django Rest Framework?

I am building an API using Django Rest Framework, and I'm trying to make it as RESTful as possible. Following this question (and also this question on SoftwareEngineering), I have described a number of resources that my API endpoints will expose, such as an Invoice that can be seen at the following URL: /api/v1/invoices/<invoicenumber>/
However, I am having trouble relating the RESTful design principles to the concrete workings of Django Rest Framework. It is unclear to me what constitues a resource: the model, the serializer or the view?
In particular, I am confused about the correct place to implement my calculate_total() method. In a regular Django application it would certainly live in the the Invoice model:
class InvoiceModel(models.Model):
def calculate_total(self):
# do calculation
return total
Although the actual calculation could be complex, the "total" is conceptually a part of the representation of the Invoice. In this sense, the resource is more equivalent to the InvoiceSerializer:
class InvoiceSerializer(serializers.Serializer):
total = serializers.SerializerMethodField()
def get_total(self, obj):
# do calculation
return total
Finally, the resource will always be accessed by a view. So you could also argue that the view is actually the resource, while the serializer and the model are simply implementation details:
class InvoiceView(APIView):
def calculate_total(self, obj):
# do calculation
return total
If I would have to designate one of the above classes as the canonical representation of the Invoice as a resource, which one would it be? Should I implement my method on the model class, the serializer class, or the view class? Or perhaps I am overthinking this and any one of them will do?
Well, although I agree that there might be different solutions, In this case I would definitely go with the model. As the resource and the place where to put the function.
In the case of the function, it could even be implemented as a calculated attribute of the invoice. Actually, to me TOTAL looks more like an attribute that a method.
Anyway, it seems interesting to me your train of thought and how you get to the other two options, serializer and the view.
I think that the model it is definitely a resource. In this case for me, the invoice resource is the model. So I would say that every model that your API is going to publish, is a resource.
I also think that whenever the resource is not directly related to a single model, the view could be considered the resource.
Now, the serializer is an enigma to me. I can't think of a case that it could be think it as a resource.

Good Django design practice to add a REST api later following DRY

I am starting a web application in pure Django. However, in the future, there might be a requirement for REST api. If it happens, the most obvious choice will be Django REST framework.
Both the "old-fashioned" and REST parts share the models, however, the views are slightly different (permissions definitions, for example) and forms are replaced with serializers. Doing it the most obvious way would mean to duplicate the application logic several times, and thus a failure to follow DRY principle, and so the code becomes unmaintainable.
I got an idea to write all the logic into models (since they are shared), but in such case, there will be no use of permission mixins, generic views and the code would not be among the nicest ones.
Now I ran out of ideas. What is the best practice here?
I'd try to keep things simple as you're not sure about the future requirements for the API, and guessing can introduce extra complexity that may not even be needed when requirements will be clear.
Both Django forms and Rest Framework serializers already offer you a declarative approach that abstracts away the boilerplate code needed for basic stuff, which normally accounts for most of your code anyway.
For example, one of your Django form could look like this:
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['title', 'content']
And in the future the DRS serializer would be:
class ArticleSerializer(ModelSerializer):
class Meta:
model = Article
fields = ['title', 'content']
As you can see, if you try and stick to ModelForm and ModelSerializer, there won't be much duplication anyway. You can also simply store the fields list in a variable and just reuse that.
For more custom things, you can start by sharing logic into simple functions, for example:
def save_article_with_author(article_data, author_data):
# custom data manipulation before saving, consider that article_data will be a dictionary either if it comes from deserialized JSON (api) or POST data
# send email, whatever
This function can be shared between your form and serializer.
For everything related to data fetching, I'd try to use Model Managers as much as possible, defining custom querysets that can be resued e.g. for options by forms and serializers.
I tend to avoid writing any logic that doesn't directly read or write data into the model classes. I think that couples too much the business logic with the data layer. As an example, I never want to write any auth/permission checks into a save() method of a model, because that couples different layers too tightly.
As a rule of thumb, imagine this scenario: you add say permissions checks or the logic to send an email when a user is created overriding the save() method of your Article model.
Then, later on you're asked to write a simple manage command that batch-import users from a spreadsheet. At this point, what you did in your save() method really gets in the way, as you can freely access your data through your model without having to bother with permissions, emails and all of that.
Regarding the view layer and assuming you need to implement some shared auth/permission checks and you don't want to have separate views, you can use this approach:
https://www.django-rest-framework.org/topics/html-and-forms/
Blockquote
REST framework is suitable for returning both API style responses, and regular HTML pages. Additionally, serializers can be used as HTML forms and rendered in templates.
Here's some guidelines on how you could dynamically switch from HTML to JSON based to the request content type:
https://www.django-rest-framework.org/api-guide/renderers/#advanced-renderer-usage
This seems like a good option in your situation, I'd just write down a quick proof-of-concept before you go all in to see if you are not too limited for what you need to do.

Django Rest Framework -A concrete example of when you would use a serializer class vs a model serializer class

I am learning django rest framework, while I understand what a serializer does and when you would use it, I cannot fully seat the need for a serializer and a model serialzer class. Can one of you please give me a concrete real world example use case of both please?
Yes I have gone through the tutorial on DRF website several times and I still am experiencing fuzziness
There is an excellent example on the DRF tutorial and it would take too much to cover in an answer, but I would like to make some points.
First, the DRF documentation explains:
Our SnippetSerializer class is replicating a lot of information that's also contained in the Snippet model. It would be nice if we could keep our code a bit more concise.
In the same way that Django provides both Form classes and ModelForm classes, REST framework includes both Serializer classes, and ModelSerializer classes.
The Snippet model is the name of the model used in that example. So as the documentation says, rather than typing again the same fields from the model over to a Serializer, we can use a ModelSerializer as a shortcut, in a similar way that we would use a ModelForm over a simple Form.
But this leaves the question essentially as "ok, then why is there a simple Serializer class at all?", as you pointed out in your comment.
In the vast majority of cases where you have models and you need to serialize/deserialize relevant data (usually JSON but not limited to), then ModelSerializer is the way to go. Even if additional fields, related serializers or arbitrary logic is required, a ModelSerializer can be easily tweaked. Personally it has never occured to me with any of my projects that ModelSerializer is not suitable for data related to a model.
But there may be cases where you need to handle data that do not abide to a model. Such data would be POSTed to a DRF view and a Serializer would handle them. Such cases could be for example to send a mail message, to setup a Celery task, to add data to session, and many others that do not involve a model at all.

django form methods are for validation only or not?

While I am studying the new django docs on class-based views, I notice this example code:
# forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
def send_email(self):
# send email using the self.cleaned_data dictionary
pass
The fact of seeing send_email as a method of ContactForm really annoys me. I have always thought form methods should be for validation purpose and methods that consume forms (like send_email in this case) should be in the views layer. Am I missing anything here? Or should the example be corrected?
There is no correct answer for this one. It really depends on your coding style. Using a form for more than just validation is as valid as doing this method in the view.
The above example has some kind of advantage though. Lets say you have one model and you want to use different forms (with different logic) for it. Instead of putting the logic in the view and check which form is now used it probably better to put this logic on the form level.
The first time I got the same weird feeling was when I encountered the LoginForm from django.contrib.auth that also verifies that the user's browser is capable of working with cookies aside from managing the credentials passed in.
Personally, I agree with you. I'm more inclined to think that the view should be the responsible actor for performing the action of sending the email rather than the form and that send_email should be a method defined in the view.
But, then again, you can easily observe how a lot of us use Django differently or have a different approach to solving the same problems. Due to the fact that we have different concerns in mind when developing our applications, it's quite possible we have a different understanding of how certain framework components are meant to be used, which is a bit of a moot point. In the end, what's important to acknowledge is that it's possible to delegate some of the heavy-lifting from the view to the form in a manner that's well-defined.
There is nothing wrong with the example. For any but the simplest of cases, your form will contain only cleaning methods; most forms do extra validation and other actions. In the end, it is just a Python class and you can do whatever you would like in it; there is no "rule" except maybe DRY.
We have form methods that validate against external services, call other procedures and trigger workflows. For such complicated logic, it is best to embed it in to the form class as it increases reusability of the code. Developers working on the view simply use the form class as a library and don't have to worry about exotic validation requirements. In this case, it actually promotes DRY.
When we have to update the logic we only update the form class's "private" methods (those prefixed with _). This keeps the "public" interface (documented by django) intact and all view code using that form doesn't have to be updated.