Slide 1

Slide 1 text

How we use Django at Cheesecake Labs

Slide 2

Slide 2 text

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…

Slide 3

Slide 3 text

What Cheesecake Labs Does? We're your dedicated partner in building mobile and web applications. > Staff Augmentation > Dedicated Teams > Software Outsourcing

Slide 4

Slide 4 text

Starter

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

- 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?

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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.

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

The Other Apps Architecture

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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.

Slide 14

Slide 14 text

# 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 , ... )

Slide 15

Slide 15 text

Styleguide Good Practices

Slide 16

Slide 16 text

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.

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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.

Slide 19

Slide 19 text

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.

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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)

Slide 22

Slide 22 text

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.

Slide 23

Slide 23 text

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()} )

Slide 24

Slide 24 text

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.

Slide 25

Slide 25 text

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)

Slide 26

Slide 26 text

● 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

Slide 27

Slide 27 text

https://github.com/HackSoftware/Django- Styleguide/ Reference

Slide 28

Slide 28 text

No content