Arquitectura en proyectos de Django: cómo sobrevivir

Creo que no es un secreto para nadie que en las conversaciones de desarrolladores experimentados de Python, y no solo, a menudo se deslizan frases de que Django es malvado, que Django tiene una mala arquitectura y es imposible escribir un gran proyecto sin dolor. Incluso un proyecto medio de Django suele ser difícil de mantener y ampliar. Propongo averiguar por qué sucede esto y qué está mal con los proyectos de Django.





Un poco de teoría

Cuando empezamos a aprender Django sin experiencia en otros lenguajes y frameworks, además de documentación, leemos tutoriales, artículos, libros y en casi todos vemos algo como esto:





Django es un marco que utiliza el patrón de diseño Model-View-Controller (MVC).





Y luego un montón de diagramas y explicaciones inexactos sobre qué es MVC. Por qué son inexactos y qué les pasa, puede ver aquí o aquí .





Normalmente, estos esquemas describen MVC de esta manera:





  • Modelo: acceso al almacén de datos





  • La vista es la interfaz con la que interactúa el usuario. 





  • El controlador es una especie de objeto de conexión entre el modelo y la vista.





Estos esquemas comunes solo confunden y se interponen en el camino cuando desea escribir una aplicación que contenga lógica empresarial.





Hay dos cosas que vale la pena notar.





, M MVC — , , , . , MVC MV*. MVC M domain model — , . , M MVC , domain model , . , , -.





,   Django controller, , Django views — , . , FAQ, , View MVC, , DRF, Controller Django . FAQ, , Django MTV (Model, Template, and View). Web MVC  Django , view .





, Django MVC , MVC  — - .  , .





Django , :





  • front-end/templates





  • serializers/forms





  • views





  • models





, . Django  c DRF.  , . 





— .  :





















  • ,





— . , :





  •  





serializers/forms

serializers ( serializers forms):









  • Python





  • Python Python (, Django dict)





, create update, save() view.





:





class CommentSerializer(serializers.Serializer):
	email = serializers.EmailField()
  content = serializers.CharField(max_length=200)
  created = serializers.DateTimeField()

  def create(self, validated_data):
  	return Comment.objects.create(**validated_data)

  def update(self, instance, validated_data):
    instance.email = validated_data.get('email', instance.email)
    instance.content = validated_data.get('content', instance.content)
    instance.created = validated_data.get('created', instance.created)
    instance.save()
    return instance
      
      



- view:





# .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
comment = serializer.save()
      
      



, .





ModelSerializer ModelViewSet, CRUD - .





# serializers.py
class OrderSerializer(serializers.ModelSerializer):
	class Meta:
  	model = Order
    fields = ‘__all__’

# views.py
class OrderViewsSet(viewsets.ModelViewSet):
	queryset = Order.objects.all()
  serializer_class = OrderSerializer

      
      



ModelViewSet ModelSerializer, , . 





, , create , - .





# serializers.py
class OrderSerializer(serializers.ModelSerializer):
	class Meta:
		model = Order
		fields = []

	def create(self, validated_data):
		# ,       
		...
		#      
		instance = super(OrderSerializer, self).create(validated_data)
    #    
    ...
    #   
    ...
    #  
    ...
    return instance
  
# views.py
class OrderViewsSet(viewsets.ModelViewSet):
	queryset = Order.objects.all()
  serializer_class = OrderSerializer
      
      



- , view.





:





:

CRUD





Django DRF CRUD.





MVC





- (/) . - .





View MVC Django. ,   MVC — -.









, - .









- .









— , views. - , , .





- , .









DRF Serializers Django Forms. serializers, . Django Forms DRF Serializers .





/





, .









. , , , -, - ( ).





-.  - , CRUD, save , .





ModelSerializer create update ( ModelSerializer read only — ). - , CRUD , DRF .





Views

View Django , , . , , , views -, -. , views Django , - , («, , »; Fat Stupid Ugly Controllers).





- :





# views.py
class OrderViewsSet(viewsets.ModelViewSet):
	queryset = Order.objects.all()
  serializer_class = OrderSerializer
                                
  def perform_create(self, serializer):
  	# ,       
    ...
    #      
    super(OrderViewsSet, self).perform_create(serializer)
    #    
    ...
    #   
    ...
    #  
    ...
      
      



, ModelViewSet , . , perform_create  ( super, ModelViewSet). ModelViewSet  APIView:





# views.py
class OrderCreateApi(views.APIView):
	class InputSerializer(serializers.ModelSerializer):
  	number = serializers.IntegerField()
    ...                   
  def post(self, request):
  	serializer = self.InputSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    # ,       
    ...
    #                                               
    order = Order.objects.create(**serializer.validated_data)
    #    
    ...
    #   
    ...
    #  
    ...                   
    return Response(status=status.HTTP_201_CREATED)

      
      



serializers, views.





# views.py
class OrderViewsSet(viewsets.ModelViewSet):
	queryset = Order.objects.all()
  serializer_class = OrderSerializer
                          
  def get_queryset(self):
  	queryset = super(OrderViewsSet, self).get_queryset()
    queryset = queryset.filter(user=self.request.user)
    return queryset
      
      



, , CRUD, , , . , .





:





, save  serializers. serializers “” “” .





CRUD





Django DRF , CRUD views .





MVC





- .









view view, - . , - , , Celery .









DRF View Django View. views, . Django View DRF View .









views serializers Django + http client .









views , Celery , , views , — , .





 





view , -.









.





-





-.









- .





serializers — views -.





ModelViewSet , , APIView GenericAPIView.





CRUD, ModelViewSet .





models

Model MVC, , models -.





:





# models.py
class Order(models.Model):
	number = serializers.IntegerField()
  created = models.DateTimeField(auto_now_add=True)
  status = models.CharField(max_length=16)
                                             
  def update_status(self, status: str) -> None:
  	self.status = status
    self.save(update_fields=('status',))
  ...
  
  @classmethod
  def create(cls, data...):
  	instance = cls(...)
    # ,       
    ...
    #      
    instance = instance.save()
    #    
    ...
    #    (         )
    ...
    #  
    ...

# views.py
class OrderCreateApi(views.APIView):
	class InputSerializer(serializers.ModelSerializer):
  	number = serializers.IntegerField()
    ... 
   
  def post(self, request):
  	serializer = self.InputSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    
    Order.create(**serializer.validated_data)    
             
   	return Response(status=status.HTTP_201_CREATED)

      
      



view serializer .  classmethod, . , , - .





, - save(), .





Managers.





# views.py
class OrderListApi(views.APIView):
	class OutputSerializer(serializers.ModelSerializer):
  	class Meta:
    	model = Order
      fields = ‘__all__’ 
                          
  def get(self, request):
  	orders = Order.objects.filter(user=request.user)
    #      
    # , Order.objects.filter(user=request.user, is_deleted=False, is_archived=False...)
    #       Manager
    
    data = self.OutputSerializer(orders, many=True).data
    
    return Response(data)
      
      



:





serializers views “” - .





MVC





. View , . MVC.









- , , , views serializers. python . save managers , .









, DRF Views, Django Views, Celery ..









, . Django models ORM  — .









- , , . , . , , , , . 





CRUD





CRUD, .





MVC . , - .





Services

Django , , Model MVC , . 





services Model, - . models property, -, , . :





# models.py
class Order(models.Model):
	number = serializers.IntegerField()
  created = models.DateTimeField(auto_now_add=True)
  status = models.CharField(max_length=16)
                                             
  def update_status(self, status: str) -> None:
  	self.status = status
    self.save(update_fields=('status',))
	...

# services.py
#       DTO
def order_create(name: str, number: int ...) -> bool:
	# ,       
  ...
  #      
  order = Order.objects.create(...)
  #    
  ...
  #    (         )
  ...
  #  
  ...

# views.py
class OrderCreateApi(views.APIView):
	class InputSerializer(serializers.ModelSerializer):
  	number = serializers.IntegerField()
    ...
                           
  def post(self, request):
  	serializer = self.InputSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    
  	services.order_create(**serializer.validated_data)
             
    return Response(status=status.HTTP_201_CREATED)
      
      



:





  • views — , ,





  • serializers — ,





  • services — (Service Objects)





  • managers — ( )





  • models —





:





# services.py
def order_get_by_user(user: User) -> Iterable[Order]:
	return Order.objects.filter(user=user)

# views.py
class OrderListApi(views.APIView):
	class OutputSerializer(serializers.ModelSerializer):
  	class Meta:
    	model = Order
      fields = ('id', 'number', ...)
                          
  def get(self, request):
  	orders = services.order_get_by_user(user=request.user)
                                         
    data = self.OutputSerializer(orders, many=True).data
                            
    return Response(data)
      
      



:





MVC





, - .









Python , .









+ - . 









-   , . 









.









. Django . , Django ORM, .





CRUD





CRUD, .





. , . , .





, , , .





Django, HackSoftware , (services selectors) managers. serializers views . dry-python.





, “” Django . Django DRF , . , , - , . — .








All Articles