Where is the business logic in the model method included in the Django Rest Framework?

I have 3 models that are related to each other with one-to-many relationships.

  • Model A can have many instances of Model B.
  • Model A can have many instances of Model C.
  • Model B can have many instances of Model C.

The idea is that the user creates an instance of model A (for example, a stock portfolio), and then enters the holdings (model C). When model B fits, I want me to do stock-based calculations / logic (model C) in the portfolio (model A) and using another class / model to track things, makes life easier, therefore, model B.

At first, I had the logic for these calculations in the Django view, but in Two Scoops Django read that business logic should be separated from the views. As a result, I moved the logic to the model A method (portfolio) and now I call this method from the view. This logic goes through stock holdings and creates new instances of Model B. Results.

Now I am interested in learning the django-rest-framework to provide a javascript API (e.g. Angular) for the API. I assume that I will not be able to have such data manipulations in my REST interfaces. However, the result of this logic (data in model B) should be visible through REST. So where does this type of calculation / logic go?

+5
source share
1 answer

The main parts of the Django Rest Framework are views (ViewSets, ApiViews, etc.) and serializers. None of these are the perfect place to write logic. As you already mentioned, writing logic in any representation is not good. What for?

  • Cannot use the same logic from another part of the application
  • It is not possible to encapsulate logic (you need to call the view for the logic to run)
  • There is no way to unit test logic because it is associated with a view

IMHO, models are not a good place to write logic. Think of the model as defining your database. I would put them as simple as possible. You can override "save" and other methods to perform trivial tasks. Any other advanced features should live outside of it.

I can think of two best places for what you need:

One of them is django signal

Better is a custom class. Encapsulate / separate the logic in your class (you can use static or instance methods, it doesn't matter), and then you can:

  • Unit test those methods / mock this class
  • Reusing this logic somewhere else
  • Simplify your code according to KISS principle
  • Use it from a signal containing a simple signal code.
  • Use it from the celery task (if you will implement this in the future) without changing one line of your code.

UPDATE An example of how to organize the code.

From the comments it is clear that the signal will not work, because the analysis operation will be performed at the request of the user. A signal would be useful if this operation should be performed automatically while saving a specific model.

I assume that you know how to use the django-rest-framework api views or views, serializers, etc. I do not know how about this, better ask another question. This will be a python explanation rather than anything else.

In your application module, create the app_business_logic.py file or whatever you want to name. You can place it at the same level as models.py, for example, but this is not necessary:

 class HoldingsAnalyser: # static method sample. Call it like this: "HoldingsAnalyser.run(..)" @staticmethod def run(holding_list): # do your model generation here or whatever you need return True # or whatever you need to return # instance method sample. Create an instance first and then call the method: # analyser = HoldingsAnalyser() # analyser.run(...) def run(self, holding_list): # do your model generation here or whatever you need return True # or whatever you need to return 

Now, in the api view or in the django-rest-framework view, create a POST method that will be called when the client application user clicks a button (this button generates analysis):

 from yourapp.app_business_logic import HoldingsAnalyser class StockPortfolioViewSet(WhatEverMixingYouNeedToInheritFrom): serializer_class = whatever # look at the docs @detail_route(methods=['post']) def run_analysis(self, request, pk): stock_portfolio = get the object based on the pk holdings = make your query to get the holdings analysis_result = HoldingsAnalyser.run(holdings) if analysis_result: # everything ok return Response(status=status.HTTP_204_NO_CONTENT) else: return Response(a useful error for your client) 

The URL for this will be something like http://server.com/api_path/stockportfolio/21/run_analysis/ , where 21 will be the StockPortfolio identifier

+11
source

Source: https://habr.com/ru/post/1243404/


All Articles