32
votes

I am trying to create a backend with Django Rest Framework and am trying to determine where to place the business logic. Would it go in the views.py? I would like to create more complex services than just getting a list of objects or grabbing one specific object. Any guidance would be appreciated, thanks. I realize there is a discussion about the business logic in a generic Django project but I am asking specifically about the django rest framework.

3
There is already a big discussion about this: stackoverflow.com/questions/12578908/…Spencer
Yeah I looked at that one but was hoping to ask specifically about the location within django rest frameworkperp
Did you find a good Design Pattern for this one? Thankscyberjoac

3 Answers

26
votes

It is more about design patterns rather than Django Rest Framework.

Here are some tips:

  • Providing interfaces using REST should not involve any specific code related to data manipulation or business logic.
  • Using an MVC approach does not mean that you shouldn't layer your application.
  • You should be able to test your business logic without touching the UI at all.
  • Some people may suggest putting business logic in models. But I do not agree with them, since Django models are different from domain models and business related tasks such as tax calculation.
  • Before getting stuck in MVC, You could read more about The MVC implemented in MVC three-tier architecture
  • I suggest having a business layer and related apps putting your business logic there.

MVC + three-tier diagram

Suppose that you have an online coffee shop & you'd like to provide a REST API for ordering coffees.

Here are my suggested code samples:

myapp/views.py:

    def order(request, quantity=1):
        # Process the order by calling the mapped method
        order_id = CoffeeShopService.place_order(quantity)
        return HttpResponse({'order_id': order_id, mimetype='application/json')

myapp/services.py:

    class CoffeeShopService(object):
        @staticmethod
        def place_order(quantity):
           # do the business logic here
           return order_id
1
votes

It is a design patterns Questing in Rest Framework, I guess. Here is a Detailed Overview of how i use Layered Approach in my API build on Rest Framework!

It is a little more layered for the sake of Easy Maintenance and utilizes Design Patterns and GRASP Principal most importantly!

Layered Approach Package Level View

enter image description here

Further Classification:

enter image description here enter image description here

Now an Example of How I go through Layers:

  1. A Request is made to example.com/Customer/profile @project/urls.py enter image description here

  2. The Request is forwarded to the Respective URL's Layer (@app/urls/Customer_Url) The Request is forwarded to the Respective URL's Layer

  3. URL's Pass it to the Respective Viewset (@app/Viewsets/Customer_Viewsets/Customer_Signup.py) enter image description here

  4. It being a Post Request, (I assume for this Example) is Forwarded to Business Logic Layer (@app/BusinessLogicLayer/BLL.py) enter image description here

  5. The Business Logic Layer has an Abstract Implementation(Act as Interface of IBLL) and it forwards the request to respective method to check for all my Business Rules! (@app/BusinessLogicLayer/SUB_BLL/Customer/*) enter image description here

  6. The Request is then Forwarded to the Data Access Layer which stores the Data of User in Database. and So forth! (@app/DataAccessLayer/DAL.py)

-3
votes

maybe this is a slightly eccentric way, but I think it's quite helpful to wrap the logic into the serializer by adding methods in it.

For example

serializer:

class OrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        fields = (
            'id',
            'total',
            'discount',
        )

    def calculate_discount(self, whatever_params):
        # calculate discount if you need... and return it

    def calculate_tax(self, whatever_params):
        # calculate tax amount if you need...and return it

    def calculate_grand_total(self, whatever_params):
        # calculate grand total if you need and return it

    def create(self, validated_data):
        # You can make an order by applying 
        # some logic in the calculation method.
        # Maybe by adding a bit of the context 
        # you sent as parameters from view.