Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How we use Django at Cheesecake Labs

How we use Django at Cheesecake Labs

Palestra apresentada por Karran Besen na 76º Python Floripa https://www.youtube.com/watch?v=vug0ZQ1kj2o&t=1s

Python Floripa

June 17, 2024
Tweet

More Decks by Python Floripa

Other Decks in Technology

Transcript

  1. Who am I? - Karran - Manezinho - Bachelor's Degree

    in Computer Science @ UFSC - Principal Software Engineer @ Cheesecake Labs Passionate about coding, no matter the stack. Besides Python frameworks I have already worked with: - Delphi, PHP, Java, Node.js, Rust… - Angular, React, React native, Vue…
  2. What Cheesecake Labs Does? We're your dedicated partner in building

    mobile and web applications. > Staff Augmentation > Dedicated Teams > Software Outsourcing
  3. Dockerized, production-ready project containing: - Modules / Apps Logic required

    for any project; Easy to extend and remove - Pre-configured packages The best settings for Django and Django Rest framework; Error monitoring; OpenAPI… - Styleguide Comprehensive documentation offering detailed architectural insights, best practices, and conventions. What's the Django Starter
  4. - Duplicated Work Almost all apps have a user authentication

    flow. Why should we implement this every project? - Architecture Variance Developers might kick-off projects with their own preferences. How about we we standardize it and avoid having to learn and maintain different structures? - Time to focus on business rules Some projects took up a month to be set and to get a minimal authentication flow. Maybe we should have basic apps that can be extended and focus on the clients' core requirements? What problems does the Django Starter solve?
  5. What's included in the Django Starter? Authentication App - Sign-up

    - Sign-in - Sign-out - Refresh token - Reset Password Flow - Terms of agreements Stack - Django Rest Framework - Django Extensions - Django Storages - Django Debug Toolbar - Pytest - Sentry - Black/iSort/Flake8/MyPy - DRF Spectacular
  6. The Architecture Composed of two types of modules — the

    core app and other apps. The main goal is to ensure that all apps, besides the core app, remain independent and decoupled. - The Core App Serves as the backbone of our project, contains essential components that are required across the entire application. - Other Apps Should only interact with core and themselves.
  7. What is the Core App responsible for? - Django Settings

    The core app is the appropriate place to store Django settings that are common to the entire project - Common Code Base classes, mixins, and utility functions, which can be utilized by other apps - Dependency Management Other apps within the project should ideally have imports only from the core app and themselves
  8. App Independence and Code Reusability - Each app should encapsulate

    its specific functionality and not rely on imports from other apps (aside from Core) - Code snippets that are used across multiple apps should ideally be extracted to the core app. However, it's acceptable to have some duplicated code
  9. App Independence and Code Reusability - If you need to

    add a very specific property to a core model, utilize a Proxy Model. from core.models import User as CoreUser class AuthenticationUser (CoreUser): def my_app_specific_property (self): ... class Meta: proxy = True
  10. App Independence and Code Reusability - Relationship with External Model

    When creating a relationship with a model that's neither from your app nor from the core, consider using an managed model.
  11. # location/models/country.py class Country(BaseModel): name = models.TextField() acronym = models.TextField()

    ... # product/models/product.py class ProductCountry (BaseModel): acronym = models.TextField() class Meta: managed = False db_table = 'location_country' class Product(BaseModel): country = models.ForeignKey( ProductCountry , ... )
  12. Good practices: Business Logic In Django, business logic should live

    in: ✅ - Use-cases - Services - Model properties & methods - Model clean method for additional validations In Django, business logic should not live in: ❌ - Views. - Serializers - Signals.
  13. Good Practices: Models - Models should usually extend BaseModel which

    sets the created, modified, and id of uuid type fields and resides in core - Always call full_clean before saving an object def course_create(name: str, start_date: date, end_date: date) -> Course: course = Course(name=name, start_date=start_date, end_date=end_date) course.full_clean() course.save() return course
  14. Good Practices: Models - If you can do validation using

    Django's constraints, then you should aim for that. - Model properties are great way to quickly access a derived value from a model's instance. - Model methods are also very powerful tool, that can build on top of properties.
  15. Good Practices: APIs What should an API folder contain? -

    View: Handles the incoming requests and returns the appropriate responses. - Use-case: The business logic of the API. - Doc: Documentation specified for DRF-Spectacular - Tests: Integration tests that ensure the correctness and reliability of APIs - Request and Response Serializers: Request and response serializers help in data validation and transformation.
  16. Good Practices: Views - The view handles the incoming requests

    and returns the appropriate responses. - One View per Operation - Inheritance from Simple APIView - Separate Business Logic from Views
  17. class Signup(APIView): permission_classes = (AllowAny,) @extend_schema(**docs) def post(self, request: Request)

    -> Response: serializer = SignupRequestSerializer (data=request.data) serializer.is_valid(raise_exception=True) user = SignupUseCase().execute(**serializer.validated_data) response_body = SignupResponseSerializer ( { "message": AuthMessages.CREATE_USER_SUCCESSFULLY .value, "data": {"user": user}, } ).data return Response(response_body, status=status.HTTP_201_CREATED)
  18. Good Practices: Serializers - Serializers play a vital role in

    data validation and transformation for our APIs. They provide a structured approach to validate incoming request data and serialize outgoing response data. - Request and Response Separation:: Each API should have a dedicated request serializer and a dedicated response serializer. - Unique Serializer Names: Avoid having two serializers with the same name, as this can impact the generation of OpenAPI documentation and lead to confusion.
  19. class SignupRequestSerializer(serializers.Serializer): email = serializers.EmailField( validators=[UniqueValidator(queryset=User.objects.all())], ) full_name = serializers.CharField()

    password = serializers.CharField(trim_whitespace=False) class SignupResponseUserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ("id", "email", "full_name") class SignupResponseSerializer(BaseResponseSerializer): data = inline_serializer( name="SignupResponseDataSerializer", fields={"user": SignupResponseUserSerializer()} )
  20. Good Practices: Use-Cases - The core of our business logic

    and encapsulate specific functionalities within the software. - Inherits from BaseUseCase: All use-cases should inherit from BaseUseCase, which provides essential common functionality and ensures consistency across use-cases.
  21. class SigninUseCase(BaseUseCase): def execute(self, email: str, password: str) -> dict:

    user = authenticate(email=email, password=password) if not user: raise AuthenticationFailed return get_tokens_for_user(user)
  22. • This architecture is far from being a silver bullet

    • Future work: - More built-in modules - Devices / Notifications - More packages - Django Silk - New Relic - Framework Alternatives - Django Ninja Conclusion