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 , . , , - , . — .