### GET /users Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md An example of a simple list API implementation following the Django Styleguide patterns. ```APIDOC ## GET /users ### Description Retrieves a list of users from the system using a selector pattern. ### Method GET ### Endpoint /users ### Parameters None ### Request Body None ### Request Example None ### Response #### Success Response (200) - **id** (string) - The unique identifier of the user - **email** (string) - The email address of the user #### Response Example [ { "id": "1", "email": "user@example.com" } ] ``` -------------------------------- ### Detail API Example Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Provides an example of a Django REST Framework API view for retrieving a single course detail, including input validation and serialization. ```APIDOC ## GET /courses/{course_id} ### Description Retrieves the details of a specific course. ### Method GET ### Endpoint `/courses/{course_id}` ### Parameters #### Path Parameters - **course_id** (string) - Required - The unique identifier for the course. ### Request Example (No request body for GET requests) ### Response #### Success Response (200) - **id** (string) - The unique identifier of the course. - **name** (string) - The name of the course. - **start_date** (date) - The start date of the course. - **end_date** (date) - The end date of the course. #### Response Example ```json { "id": "some-course-id", "name": "Course Name", "start_date": "2023-01-01", "end_date": "2023-12-31" } ``` ``` -------------------------------- ### Create API Example Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates a Django REST Framework API view for creating a new course, including input serialization and validation. ```APIDOC ## POST /courses ### Description Creates a new course with the provided details. ### Method POST ### Endpoint `/courses` ### Parameters #### Request Body - **name** (string) - Required - The name of the course. - **start_date** (date) - Required - The start date of the course. - **end_date** (date) - Required - The end date of the course. ### Request Example ```json { "name": "New Course Title", "start_date": "2024-01-01", "end_date": "2024-12-31" } ``` ### Response #### Success Response (201) Returns an empty response with a 201 Created status code upon successful creation. #### Response Example (No response body for 201 Created status) ``` -------------------------------- ### Django Test File Structure Example Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Provides an example of a typical Django project file structure for organizing tests. It shows how tests are separated by type (models, selectors, services) within an app's `tests` directory, mirroring the application's module structure. ```python project_name ├── app_name │   ├── __init__.py │   └── tests │   ├── __init__.py │   ├── factories.py │   ├── models │   │   └── __init__.py │   │   └── test_some_model_name.py │   ├── selectors │   │   └── __init__.py │   │   └── test_some_selector_name.py │   └── services │   ├── __init__.py │   └── test_some_service_name.py └── __init__.py ``` -------------------------------- ### Update API Example Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Illustrates a Django REST Framework API view for updating an existing course, allowing partial updates. ```APIDOC ## POST /courses/{course_id} ### Description Updates an existing course with the provided details. Fields are optional and only provided fields will be updated. ### Method POST ### Endpoint `/courses/{course_id}` ### Parameters #### Path Parameters - **course_id** (string) - Required - The unique identifier for the course to update. #### Request Body - **name** (string) - Optional - The new name of the course. - **start_date** (date) - Optional - The new start date of the course. - **end_date** (date) - Optional - The new end date of the course. ### Request Example ```json { "name": "Updated Course Title" } ``` ### Response #### Success Response (200) Returns an empty response with a 200 OK status code upon successful update. #### Response Example (No response body for 200 OK status) ``` -------------------------------- ### Install markdown-toc for TOC generation Source: https://github.com/hacksoftware/django-styleguide/blob/master/tools/README.md This command installs the `markdown-toc` npm package, which is a dependency for the `update_toc.py` script. It enables the automatic generation and updating of the Table of Contents in markdown files. ```bash npm install markdown-toc ``` -------------------------------- ### Implement Class-Based Service in Django Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md A class-based service example for file uploads, providing a namespace for related operations like create and update while allowing internal method reuse. ```python class FileStandardUploadService: def __init__(self, user: BaseUser, file_obj): self.user = user self.file_obj = file_obj def _infer_file_name_and_type(self, file_name: str = "", file_type: str = "") -> Tuple[str, str]: file_name = file_name or self.file_obj.name if not file_type: guessed_file_type, encoding = mimetypes.guess_type(file_name) file_type = guessed_file_type or "" return file_name, file_type @transaction.atomic def create(self, file_name: str = "", file_type: str = "") -> File: _validate_file_size(self.file_obj) file_name, file_type = self._infer_file_name_and_type(file_name, file_type) obj = File( file=self.file_obj, original_file_name=file_name, file_name=file_generate_name(file_name), file_type=file_type, uploaded_by=self.user, upload_finished_at=timezone.now() ) obj.full_clean() obj.save() return obj @transaction.atomic def update(self, file: File, file_name: str = "", file_type: str = "") -> File: _validate_file_size(self.file_obj) file_name, file_type = self._infer_file_name_and_type(file_name, file_type) file.file = self.file_obj file.original_file_name = file_name file.file_name = file_generate_name(file_name) file.file_type = file_type file.uploaded_by = self.user file.upload_finished_at = timezone.now() file.full_clean() file.save() return file ``` -------------------------------- ### API Error Response Example Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md An example of a desired API error response format, featuring a single 'message' key for error details. ```json { "message": "Some error message here" } ``` -------------------------------- ### Class-based API Example with BaseApi Inheritance (Python) Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates a class-based API view inheriting from a custom BaseApi. This approach allows for easy extension and organization of API logic using class attributes and inheritance. ```Python class SomeApi(BaseApi): def get(self, request): data = something() return Response(data) ``` -------------------------------- ### Django Model Inheritance Example Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates how to inherit from a custom BaseModel in Django to include predefined fields like created_at and updated_at. This simplifies model creation and ensures consistent metadata. ```python class SomeModel(BaseModel): pass ``` -------------------------------- ### Function-based API Example with Decorator (Python) Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Illustrates a function-based API view using a decorator to handle common API functionalities like request method validation. This is an alternative to class-based views, achieving similar results through decorators. ```Python @base_api(["GET"]) def some_api(request): data = something() return Response(data) ``` -------------------------------- ### Implement File Upload API View Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md An example of a Django APIView that utilizes a service class to handle file uploads. It abstracts the business logic away from the view layer. ```python class FileDirectUploadApi(ApiAuthMixin, APIView): def post(self, request): service = FileDirectUploadService( user=request.user, file_obj=request.FILES["file"] ) file = service.create() return Response(data={"id": file.id}, status=status.HTTP_201_CREATED) ``` -------------------------------- ### Define Multi-Step Service Class Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md A service class implementation that manages a multi-step flow (start and finish) and provides a namespace for file upload operations. ```python class FileDirectUploadService: def __init__(self, user: BaseUser): self.user = user @transaction.atomic def start(self, *, file_name: str, file_type: str) -> Dict[str, Any]: file = File( original_file_name=file_name, file_name=file_generate_name(file_name), file_type=file_type, uploaded_by=self.user, file=None ) file.full_clean() file.save() upload_path = file_generate_upload_path(file, file.file_name) file.file = file.file.field.attr_class(file, file.file.field, upload_path) file.save() presigned_data: Dict[str, Any] = {} if settings.FILE_UPLOAD_STORAGE == FileUploadStorage.S3: presigned_data = s3_generate_presigned_post( file_path=upload_path, file_type=file.file_type ) else: presigned_data = { "url": file_generate_local_upload_url(file_id=str(file.id)), } return {"id": file.id, **presigned_data} @transaction.atomic def finish(self, *, file: File) -> File: file.upload_finished_at = timezone.now() file.full_clean() file.save() return file ``` -------------------------------- ### Django BaseModel Example Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Defines a reusable BaseModel for Django models, including common fields like creation and update timestamps. This promotes consistency and reduces boilerplate code across different models. ```python from django.db import models from django.utils import timezone class BaseModel(models.Model): created_at = models.DateTimeField(db_index=True, default=timezone.now) updated_at = models.DateTimeField(auto_now=True) class Meta: abstract = True ``` -------------------------------- ### Custom Validation Error for BaseUser Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Shows how to handle validation errors for a `BaseUser` model, specifically when required fields like 'password' or 'email' are missing. This example demonstrates a common scenario in user management where clean data is essential. ```python class BaseUser: def full_clean(self): # Simulate validation errors if not hasattr(self, 'password') or not self.password: raise Exception("Password cannot be blank.") if not hasattr(self, 'email') or not self.email: raise Exception("Email cannot be blank.") def some_service(): user = BaseUser() user.full_clean() ``` -------------------------------- ### Django Nested Serializer Example Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates how to implement nested serializers in Django REST Framework using a helper function `inline_serializer`. This is useful for representing one-to-many or many-to-many relationships within a single API response. ```Python class Serializer(serializers.Serializer): weeks = inline_serializer(many=True, fields={ 'id': serializers.IntegerField(), 'number': serializers.IntegerField(), }) ``` -------------------------------- ### Calling `full_clean` Before Saving in Django Service Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Illustrates the recommended practice of calling the `full_clean` method on a model instance before saving it, typically within a service layer function. This ensures that all model-level validations, including those in the `clean` method, are executed. The example shows a `course_create` function. ```python def course_create(*, name: str, start_date: date, end_date: date) -> Course: obj = Course(name=name, start_date=start_date, end_date=end_date) obj.full_clean() obj.save() return obj ``` -------------------------------- ### Organizing Django URLs with include and path Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates how to organize Django URLs by grouping related paths into separate lists and including them in the main `urlpatterns`. This approach enhances flexibility and maintainability, especially for large projects. ```python from django.urls import path, include from project.education.apis import ( CourseCreateApi, CourseUpdateApi, CourseListApi, CourseDetailApi, CourseSpecificActionApi, ) course_patterns = [ path('', CourseListApi.as_view(), name='list'), path('/', CourseDetailApi.as_view(), name='detail'), path('create/', CourseCreateApi.as_view(), name='create'), path('/update/', CourseUpdateApi.as_view(), name='update'), path( '/specific-action/', CourseSpecificActionApi.as_view(), name='specific-action' ), ] urlpatterns = [ path('courses/', include((course_patterns, 'courses'))), ] ``` ```python from django.urls import path, include from styleguide_example.files.apis import ( FileDirectUploadApi, FilePassThruUploadStartApi, FilePassThruUploadFinishApi, FilePassThruUploadLocalApi, ) urlpatterns = [ path( "upload/", include(( path( "direct/", FileDirectUploadApi.as_view(), name="direct" ), path( "pass-thru/", include(( path( "start/", FilePassThruUploadStartApi.as_view(), name="start" ), path( "finish/", FilePassThruUploadFinishApi.as_view(), name="finish" ), path( "local//", FilePassThruUploadLocalApi.as_view(), name="local" ) ], "pass-thru")) ) ], "upload")) ) ] ``` -------------------------------- ### Implement Function-Based Service in Django Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md A simple function-based service for creating a user. It demonstrates keyword-only arguments, type annotation, and orchestration of other services. ```python def user_create(*, email: str, name: str) -> User: user = User(email=email) user.full_clean() user.save() profile_create(user=user, name=name) confirmation_email_send(user=user) return user ``` -------------------------------- ### Django Settings Structure with `config` Directory Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Illustrates a common Django settings organization using a `config` directory, separating Django-specific settings from other configurations. This promotes modularity and allows for environment-specific overrides. ```python import environ env = environ.Env() ``` ```python from config.env import env ``` ```python from config.settings.cors import * from config.settings.sessions import * from config.settings.celery import * from config.settings.sentry import * ``` -------------------------------- ### DRF Serializer Definitions Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md These are example definitions for DRF serializers, `PlainSerializer` and `NestedSerializer`. They are used in the context of defining data structures for validation and serialization within a Django REST Framework application. ```python from rest_framework import serializers class NestedSerializer(serializers.Serializer): bar = serializers.CharField() class PlainSerializer(serializers.Serializer): foo = serializers.CharField() ``` -------------------------------- ### Implement Model Methods for Logic and State Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Shows how to implement methods for logic requiring arguments or for synchronizing multiple attribute updates. This ensures data consistency when setting related fields. ```python def is_within(self, x: date) -> bool: return self.start_date <= x <= self.end_date def set_new_secret(self): now = timezone.now() self.secret = get_random_string(255) self.expiry = now + settings.TOKEN_EXPIRY_TIMEDELTA return self ``` -------------------------------- ### Implement Create, Update, and Detail APIs in Django REST Framework Source: https://context7.com/hacksoftware/django-styleguide/llms.txt Demonstrates the pattern of using InputSerializers for validation and service layer calls for data manipulation. It ensures a clean separation between request handling and business logic. ```python from rest_framework.views import APIView from rest_framework import serializers, status from rest_framework.response import Response class CourseCreateApi(SomeAuthenticationMixin, APIView): class InputSerializer(serializers.Serializer): name = serializers.CharField() start_date = serializers.DateField() end_date = serializers.DateField() def post(self, request): serializer = self.InputSerializer(data=request.data) serializer.is_valid(raise_exception=True) course_create(**serializer.validated_data) return Response(status=status.HTTP_201_CREATED) class CourseUpdateApi(SomeAuthenticationMixin, APIView): class InputSerializer(serializers.Serializer): name = serializers.CharField(required=False) start_date = serializers.DateField(required=False) end_date = serializers.DateField(required=False) def post(self, request, course_id): serializer = self.InputSerializer(data=request.data) serializer.is_valid(raise_exception=True) course_update(course_id=course_id, **serializer.validated_data) return Response(status=status.HTTP_200_OK) class CourseDetailApi(SomeAuthenticationMixin, APIView): class OutputSerializer(serializers.Serializer): id = serializers.CharField() name = serializers.CharField() start_date = serializers.DateField() end_date = serializers.DateField() def get(self, request, course_id): course = course_get(id=course_id) serializer = self.OutputSerializer(course) return Response(serializer.data) ``` -------------------------------- ### Nested Serializers Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates how to use nested serializers, including a custom `inline_serializer` utility for defining nested fields. ```APIDOC ## Nested Serializers ### Description This section explains how to implement nested serializers in Django REST Framework, particularly using a helper function like `inline_serializer` for concise definition of nested fields. ### Example Usage ```python from rest_framework import serializers # Assuming inline_serializer is defined elsewhere and imported # from .utils import inline_serializer class ParentSerializer(serializers.Serializer): # Example of a nested serializer for a list of weeks weeks = inline_serializer(many=True, fields={ 'id': serializers.IntegerField(), 'number': serializers.IntegerField(), }) # Other fields for the parent serializer can be added here # parent_field = serializers.CharField() ``` ### Explanation - The `inline_serializer` function (implementation typically found in a `utils` module) allows for defining nested serializer fields directly within the parent serializer definition. - `many=True` is used when the nested serializer should handle a list of objects. - The `fields` argument is a dictionary defining the fields of the nested serializer. ``` -------------------------------- ### Object Fetching Utility Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Explains a utility function for fetching objects, handling potential Http404 exceptions by returning None. ```APIDOC ## Object Fetching ### Description This utility function `get_object` attempts to retrieve an object using Django's `get_object_or_404`. If a `Http404` exception occurs, it returns `None` instead of raising the exception. This provides a more flexible way to handle cases where an object might not exist. ### Usage ```python from django.http import Http404 from django.shortcuts import get_object_or_404 def get_object(model_or_queryset, **kwargs): """ Reuse get_object_or_404 since the implementation supports both Model && queryset. Catch Http404 & return None """ try: return get_object_or_404(model_or_queryset, **kwargs) except Http404: return None ``` ### Parameters - **model_or_queryset**: A Django model or queryset to fetch the object from. - **kwargs**: Additional keyword arguments to filter the object (e.g., `id=object_id`). ### Returns - The fetched object if found, otherwise `None`. ``` -------------------------------- ### Organize Django Settings Source: https://context7.com/hacksoftware/django-styleguide/llms.txt A modular approach to Django configuration. Settings are split into base files and specific integration modules to maintain clarity and manage environment variables effectively. ```python # config/django/base.py import os from config.env import env, environ BASE_DIR = environ.Path(__file__) - 3 env.read_env(os.path.join(BASE_DIR, ".env")) from config.settings.cors import * from config.settings.sessions import * from config.settings.celery import * from config.settings.sentry import * # config/settings/sentry.py from config.env import env SENTRY_DSN = env('SENTRY_DSN', default='') if SENTRY_DSN: import sentry_sdk from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.celery import CeleryIntegration ``` -------------------------------- ### Handling DRF ValidationError (Simple String) in Python Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md This Python example shows how a DRF `ValidationError` with a simple string detail is handled. The custom handler formats this into a validation error response, placing the string message into the 'fields' part of the 'extra' object. ```python from rest_framework.exceptions import ValidationError as RestValidationError def some_service(): raise RestValidationError("Some error message") ``` -------------------------------- ### Implement User Update Service in Django Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates a service pattern for updating user models by separating generic field updates from side-effect logic. This approach uses a helper function to handle repetitive field assignments and ensures efficient database updates. ```python def user_update(*, user: User, data) -> User: non_side_effect_fields = ['first_name', 'last_name'] user, has_updated = model_update( instance=user, fields=non_side_effect_fields, data=data ) # Side-effect fields update here (e.g. username is generated based on first & last name) # ... some additional tasks with the user ... return user ``` -------------------------------- ### Django Model Validation with `clean` Method Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates how to define a custom validation logic within a Django model's `clean` method. This method is called by `full_clean` and is useful for validating non-relational fields. The example shows a `Course` model with date validation. ```python class Course(BaseModel): name = models.CharField(unique=True, max_length=255) start_date = models.DateField() end_date = models.DateField() def clean(self): if self.start_date >= self.end_date: raise ValidationError("End date cannot be before start date") ``` -------------------------------- ### Load Environment Variables from .env file with django-environ Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md This code demonstrates how to use django-environ to load environment variables from a local .env file. It sets up the base directory and then reads the .env file, making its variables available for Django settings. It's crucial not to commit the .env file to source control. ```python import os from config.env import env, environ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = environ.Path(__file__) - 3 env.read_env(os.path.join(BASE_DIR, ".env")) ``` -------------------------------- ### Organize Django URL Patterns by Domain Source: https://context7.com/hacksoftware/django-styleguide/llms.txt Shows how to structure URL routing using nested patterns to keep the project organized and maintainable. It uses include() to group related API endpoints under a common namespace. ```python from django.urls import path, include from project.education.apis import ( CourseCreateApi, CourseUpdateApi, CourseListApi, CourseDetailApi, CourseSpecificActionApi, ) course_patterns = [ path('', CourseListApi.as_view(), name='list'), path('/', CourseDetailApi.as_view(), name='detail'), path('create/', CourseCreateApi.as_view(), name='create'), path('/update/', CourseUpdateApi.as_view(), name='update'), path('/specific-action/', CourseSpecificActionApi.as_view(), name='specific-action'), ] urlpatterns = [ path('courses/', include((course_patterns, 'courses'))), ] ``` -------------------------------- ### Django API Endpoint with Custom Serialization Function Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md This Python code demonstrates a Django API endpoint that utilizes a custom serialization function (`some_feed_serialize`) to handle complex data fetching and serialization. The `get` method retrieves feed data and then passes it to the serializer function for processing. ```python from rest_framework.response import Response from .serializers import some_feed_serialize from .api_utils import some_feed_get class SomeGenericFeedApi(BaseApi): def get(self, request): feed = some_feed_get( user=request.user, ) data = some_feed_serialize(feed) return Response(data) ``` -------------------------------- ### Implement Data Selector with django-filter Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates using the django-filter library within a selector function to encapsulate query filtering logic. ```python import django_filters from styleguide_example.users.models import BaseUser class BaseUserFilter(django_filters.FilterSet): class Meta: model = BaseUser fields = ('id', 'email', 'is_admin') def user_list(*, filters=None): filters = filters or {} qs = BaseUser.objects.all() return BaseUserFilter(filters, qs).qs ``` -------------------------------- ### Django Model Validation with Database Constraints Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Shows how to use Django's `Meta.constraints` to define database-level validation rules. This approach leverages the database to enforce data integrity, reducing the need for explicit Python validation in some cases. The example uses `models.CheckConstraint` for date validation. ```python class Course(BaseModel): name = models.CharField(unique=True, max_length=255) start_date = models.DateField() end_date = models.DateField() class Meta: constraints = [ models.CheckConstraint( name="start_date_before_end_date", check=Q(start_date__lt=F("end_date")) ) ] ``` -------------------------------- ### Create Paginated List APIs Source: https://context7.com/hacksoftware/django-styleguide/llms.txt APIs act as thin interfaces that coordinate between selectors and serializers. This pattern includes pagination utilities and input validation via serializers. ```python class UserListApi(ApiErrorsMixin, APIView): class Pagination(LimitOffsetPagination): default_limit = 10 class FilterSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) is_admin = serializers.NullBooleanField(required=False) email = serializers.EmailField(required=False) def get(self, request): filters_serializer = self.FilterSerializer(data=request.query_params) filters_serializer.is_valid(raise_exception=True) users = user_list(filters=filters_serializer.validated_data) return get_paginated_response( pagination_class=self.Pagination, serializer_class=self.OutputSerializer, queryset=users, request=request, view=self ) ``` -------------------------------- ### Implement Paginated API View with DRF Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Shows how to create an APIView that uses a FilterSerializer for input validation and a custom pagination utility to return paginated results. ```python from rest_framework.views import APIView from rest_framework import serializers from styleguide_example.api.mixins import ApiErrorsMixin from styleguide_example.api.pagination import get_paginated_response, LimitOffsetPagination from styleguide_example.users.selectors import user_list class UserListApi(ApiErrorsMixin, APIView): class Pagination(LimitOffsetPagination): default_limit = 1 class FilterSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) is_admin = serializers.NullBooleanField(required=False) email = serializers.EmailField(required=False) class OutputSerializer(serializers.Serializer): id = serializers.CharField() email = serializers.CharField() is_admin = serializers.BooleanField() def get(self, request): filters_serializer = self.FilterSerializer(data=request.query_params) filters_serializer.is_valid(raise_exception=True) users = user_list(filters=filters_serializer.validated_data) return get_paginated_response( pagination_class=self.Pagination, serializer_class=self.OutputSerializer, queryset=users, request=request, view=self ) ``` -------------------------------- ### Django Course Detail API Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Implements a detail API endpoint for retrieving course information. It uses a serializer to define the output structure and fetches the course object using its ID. Dependencies include Django REST Framework serializers and a course retrieval function. ```Python class CourseDetailApi(SomeAuthenticationMixin, APIView): class OutputSerializer(serializers.Serializer): id = serializers.CharField() name = serializers.CharField() start_date = serializers.DateField() end_date = serializers.DateField() def get(self, request, course_id): course = course_get(id=course_id) serializer = self.OutputSerializer(course) return Response(serializer.data) ``` -------------------------------- ### Test Django Services Source: https://context7.com/hacksoftware/django-styleguide/llms.txt Guidelines for writing exhaustive service tests. The approach emphasizes hitting the database for state verification and using unittest.mock to isolate external dependencies. ```python from unittest.mock import patch, Mock from django.test import TestCase from django.core.exceptions import ValidationError class ItemBuyTests(TestCase): @patch('project.payments.services.items_get_for_user') def test_buying_item_that_is_already_bought_fails(self, items_get_for_user_mock: Mock): user = User(username='Test User') item = Item(name='Test Item', price=10.15) items_get_for_user_mock.return_value = [item] with self.assertRaises(ValidationError): item_buy(user=user, item=item) @patch('project.payments.services.payment_charge.delay') def test_buying_item_creates_a_payment_and_calls_charge_task(self, payment_charge_mock: Mock): user = given_a_user(username="Test user") item = given_a_item(name='Test Item', price=10.15) payment = item_buy(user=user, item=item) self.assertEqual(1, Payment.objects.count()) payment_charge_mock.assert_called_once() ``` -------------------------------- ### Implement Abstract Base Model for Timestamps Source: https://context7.com/hacksoftware/django-styleguide/llms.txt Defines an abstract BaseModel that provides consistent created_at and updated_at fields. This pattern ensures all models inherit standard tracking functionality. ```python from django.db import models from django.utils import timezone class BaseModel(models.Model): created_at = models.DateTimeField(db_index=True, default=timezone.now) updated_at = models.DateTimeField(auto_now=True) class Meta: abstract = True class Course(BaseModel): name = models.CharField(unique=True, max_length=255) start_date = models.DateField() end_date = models.DateField() ``` -------------------------------- ### Perform Model Validation with clean() Source: https://context7.com/hacksoftware/django-styleguide/llms.txt Demonstrates implementing validation logic within the model's clean method and invoking full_clean() in a service layer. This ensures data integrity before persistence. ```python from django.core.exceptions import ValidationError class Course(BaseModel): def clean(self): if self.start_date >= self.end_date: raise ValidationError("End date cannot be before start date") def course_create(*, name: str, start_date: date, end_date: date) -> Course: obj = Course(name=name, start_date=start_date, end_date=end_date) obj.full_clean() obj.save() return obj ``` -------------------------------- ### Integrate Celery Tasks with Services Source: https://context7.com/hacksoftware/django-styleguide/llms.txt Demonstrates how to treat Celery tasks as thin interfaces that delegate business logic to services. It includes error handling via on_failure callbacks and asynchronous task triggering within atomic transactions. ```python from celery import shared_task from celery.utils.log import get_task_logger from django.db import transaction logger = get_task_logger(__name__) def _email_send_failure(self, exc, task_id, args, kwargs, einfo): email_id = args[0] email = Email.objects.get(id=email_id) from styleguide_example.emails.services import email_failed email_failed(email) @shared_task(bind=True, on_failure=_email_send_failure) def email_send(self, email_id): email = Email.objects.get(id=email_id) from styleguide_example.emails.services import email_send try: email_send(email) except Exception as exc: logger.warning(f"Exception occurred while sending email: {exc}") self.retry(exc=exc, countdown=5) @transaction.atomic def user_complete_onboarding(user: User) -> User: email = email_get_onboarding_template(user=user) transaction.on_commit(lambda: email_send_task.delay(email.id)) return user ``` -------------------------------- ### Configure Periodic Tasks with Management Command Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md This snippet demonstrates a Django management command to programmatically clear and recreate periodic tasks using django-celery-beat. It ensures all schedules are defined in a single, version-controlled location. ```python from django.core.management.base import BaseCommand from django.db import transaction from django_celery_beat.models import IntervalSchedule, CrontabSchedule, PeriodicTask from project.app.tasks import some_periodic_task class Command(BaseCommand): help = f""" Setup celery beat periodic tasks. Following tasks will be created: - {some_periodic_task.name} """ @transaction.atomic def handle(self, *args, **kwargs): print('Deleting all periodic tasks and schedules...\n') IntervalSchedule.objects.all().delete() CrontabSchedule.objects.all().delete() PeriodicTask.objects.all().delete() periodic_tasks_data = [ { 'task': some_periodic_task, 'name': 'Do some peridoic stuff', 'cron': { 'minute': '15', 'hour': '*', 'day_of_week': '*', 'day_of_month': '*', 'month_of_year': '*', }, 'enabled': True }, ] for periodic_task in periodic_tasks_data: print(f'Setting up {periodic_task["task"].name}') cron = CrontabSchedule.objects.create(**periodic_task['cron']) PeriodicTask.objects.create( name=periodic_task['name'], task=periodic_task['task'].name, crontab=cron, enabled=periodic_task['enabled'] ) ``` -------------------------------- ### Django Course Create API Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Provides an API endpoint for creating new course records. It defines an input serializer for data validation and uses a course creation function to persist the data. Returns an HTTP 201 status code upon successful creation. ```Python class CourseCreateApi(SomeAuthenticationMixin, APIView): class InputSerializer(serializers.Serializer): name = serializers.CharField() start_date = serializers.DateField() end_date = serializers.DateField() def post(self, request): serializer = self.InputSerializer(data=request.data) serializer.is_valid(raise_exception=True) course_create(**serializer.validated_data) return Response(status=status.HTTP_201_CREATED) ``` -------------------------------- ### Create Paginated Response Utility Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md A reusable utility function to handle pagination logic, checking if a queryset should be paginated and returning the appropriate response. ```python from rest_framework.response import Response def get_paginated_response(*, pagination_class, serializer_class, queryset, request, view): paginator = pagination_class() page = paginator.paginate_queryset(queryset, request, view=view) if page is not None: serializer = serializer_class(page, many=True) return paginator.get_paginated_response(serializer.data) serializer = serializer_class(queryset, many=True) return Response(data=serializer.data) ``` -------------------------------- ### Implement Business Logic Service Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md A service function that handles business operations, including validation via selectors, database transactions, and triggering asynchronous tasks. ```python @transaction.atomic def item_buy(*, item: Item, user: User) -> Payment: if item in items_get_for_user(user=user): raise ValidationError(f'Item {item} already in {user} items.') payment = Payment(item=item, user=user, successful=False) payment.full_clean() payment.save() transaction.on_commit(lambda: payment_charge.delay(payment_id=payment.id)) return payment ``` -------------------------------- ### Integrate Service Layer with Django Admin Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates how to trigger service methods within the Django admin panel's save_model method. This ensures consistent business logic execution regardless of the entry point. ```python @admin.register(File) class FileAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): try: cleaned_data = form.cleaned_data service = FileDirectUploadService( file_obj=cleaned_data["file"], user=cleaned_data["uploaded_by"] ) if change: service.update(file=obj) else: service.create() except ValidationError as exc: self.message_user(request, str(exc), messages.ERROR) ``` -------------------------------- ### Plain List API Implementation with DRF (Python) Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md A straightforward implementation of a list API using Django Rest Framework's APIView and serializers. It fetches a list of users using a selector and serializes the output for the response. Authentication is noted as a separate concern. ```Python from rest_framework.views import APIView from rest_framework import serializers from rest_framework.response import Response from styleguide_example.users.selectors import user_list from styleguide_example.users.models import BaseUser class UserListApi(APIView): class OutputSerializer(serializers.Serializer): id = serializers.CharField() email = serializers.CharField() def get(self, request): users = user_list() data = self.OutputSerializer(users, many=True).data return Response(data) ``` -------------------------------- ### Implement Derived Model Properties Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates using the @property decorator for simple derived values based on non-relational model fields. This approach is ideal for values that do not require arguments or complex database queries. ```python from django.utils import timezone from django.core.exceptions import ValidationError class Course(BaseModel): name = models.CharField(unique=True, max_length=255) start_date = models.DateField() end_date = models.DateField() @property def has_started(self) -> bool: now = timezone.now() return self.start_date <= now.date() @property def has_finished(self) -> bool: now = timezone.now() return self.end_date <= now.date() ``` -------------------------------- ### Implement Class-Based Services for Business Logic Source: https://context7.com/hacksoftware/django-styleguide/llms.txt Class-based services encapsulate complex operations such as file uploads or database updates. They provide a clean namespace for shared logic and ensure atomic transactions. ```python from typing import Tuple from django.db import transaction from django.utils import timezone import mimetypes class FileStandardUploadService: def __init__(self, user: BaseUser, file_obj): self.user = user self.file_obj = file_obj def _infer_file_name_and_type(self, file_name: str = "", file_type: str = "") -> Tuple[str, str]: file_name = file_name or self.file_obj.name if not file_type: guessed_file_type, encoding = mimetypes.guess_type(file_name) file_type = guessed_file_type or "" return file_name, file_type @transaction.atomic def create(self, file_name: str = "", file_type: str = "") -> File: _validate_file_size(self.file_obj) file_name, file_type = self._infer_file_name_and_type(file_name, file_type) obj = File( file=self.file_obj, original_file_name=file_name, file_name=file_generate_name(file_name), file_type=file_type, uploaded_by=self.user, upload_finished_at=timezone.now() ) obj.full_clean() obj.save() return obj ``` -------------------------------- ### Run update_toc.py script Source: https://github.com/hacksoftware/django-styleguide/blob/master/tools/README.md This command executes the `update_toc.py` Python script from the root directory of the project. The script automatically updates the Table of Contents in the `README.md` file. ```python python update_toc.py ``` -------------------------------- ### Raising DRF NotFound Exception Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Illustrates raising a DRF NotFound exception. The default response payload includes a 'detail' key with a predefined message. ```python from rest_framework import exceptions def some_service(): raise exceptions.NotFound() ``` -------------------------------- ### Extend LimitOffsetPagination Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Customizing the default DRF LimitOffsetPagination to include specific metadata like limit and offset in the response. ```python from collections import OrderedDict from rest_framework.pagination import LimitOffsetPagination as _LimitOffsetPagination from rest_framework.response import Response class LimitOffsetPagination(_LimitOffsetPagination): default_limit = 10 max_limit = 50 def get_paginated_data(self, data): return OrderedDict([ ('limit', self.limit), ('offset', self.offset), ('count', self.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('results', data) ]) def get_paginated_response(self, data): return Response(OrderedDict([ ('limit', self.limit), ('offset', self.offset), ('count', self.count) ])) ``` -------------------------------- ### Django Utility for Fetching Objects Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md A utility function to safely fetch Django model instances or querysets. It wraps `get_object_or_404` and catches `Http404` exceptions, returning `None` instead. This promotes consistent object fetching across the API. ```Python def get_object(model_or_queryset, **kwargs): """ Reuse get_object_or_404 since the implementation supports both Model && queryset. Catch Http404 & return None """ try: return get_object_or_404(model_or_queryset, **kwargs) except Http404: return None ``` -------------------------------- ### Define Function-Based Services Source: https://context7.com/hacksoftware/django-styleguide/llms.txt Services encapsulate business logic using keyword-only arguments and type annotations. They orchestrate database operations and side effects, often wrapped in atomic transactions. ```python from django.db import transaction @transaction.atomic def item_buy(*, item: Item, user: User) -> Payment: payment = Payment(item=item, user=user, successful=False) payment.full_clean() payment.save() transaction.on_commit( lambda: payment_charge.delay(payment_id=payment.id) ) return payment ``` -------------------------------- ### Celery Use Cases in Django Projects Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Lists common use cases for Celery in Django projects, including asynchronous communication with third-party services (like sending emails or notifications), offloading computationally intensive tasks from the HTTP request cycle, and scheduling periodic tasks using Celery Beat. ```python # Example of a Celery task (not a full implementation) from celery import shared_task @shared_task def send_email_task(recipient, subject, body): # Logic to send email pass # Example of a periodic task setup (using Celery Beat) # from celery.schedules import crontab # from django_celery_beat.models import PeriodicTask, IntervalSchedule # schedule, _ = IntervalSchedule.objects.get_or_create(every=60, period=IntervalSchedule.SECONDS) # PeriodicTask.objects.create(task='your_app.tasks.your_task', interval=schedule, name='My Periodic Task') ``` -------------------------------- ### Test Django Service Layer Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Unit tests for a service layer function, demonstrating how to mock external dependencies like selectors and task queues while verifying database state. ```python class ItemBuyTests(TestCase): @patch('project.payments.services.items_get_for_user') def test_buying_item_that_is_already_bought_fails(self, items_get_for_user_mock: Mock): user = User(username='Test User') item = Item(name='Test Item', description='Test Item description', price=10.15) items_get_for_user_mock.return_value = [item] with self.assertRaises(ValidationError): item_buy(user=user, item=item) @patch('project.payments.services.payment_charge.delay') def test_buying_item_creates_a_payment_and_calls_charge_task(self, payment_charge_mock: Mock): user = given_a_user(username="Test user") item = given_a_item(name='Test Item', description='Test Item description', price=10.15) self.assertEqual(0, Payment.objects.count()) payment = item_buy(user=user, item=item) self.assertEqual(1, Payment.objects.count()) self.assertFalse(payment.successful) payment_charge_mock.assert_called_once() ``` -------------------------------- ### Configure Sentry Integration with django-environ Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md This snippet shows how to configure Sentry integration using django-environ. It reads the SENTRY_DSN from environment variables and conditionally imports Sentry SDK and integrations if a DSN is provided. This approach allows for disabling Sentry during local development. ```python from config.env import env SENTRY_DSN = env('SENTRY_DSN', default='') if SENTRY_DSN: import sentry_sdk from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.celery import CeleryIntegration # ... we proceed with sentry settings here ... ``` -------------------------------- ### Handling Django Http404 in Python Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md This Python code illustrates how a Django `Http404` exception is processed. The custom exception handler translates it into a 'Not found' response, including a default message and an empty 'extra' object. ```python from django.http import Http404 def some_service(): raise Http404() ``` -------------------------------- ### Customizing DRF Exception Handling Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Demonstrates how to customize DRF's default exception handler to return specific JSON responses for validation errors, including field-specific messages and required field information. This approach allows for more structured and informative error reporting. ```python from rest_framework import serializers class NestedSerializer(serializers.Serializer): bar = serializers.CharField() class PlainSerializer(serializers.Serializer): email = serializers.EmailField(min_length=200) nested = NestedSerializer() def some_service(): serializer = PlainSerializer(data={ "email": "foo", "nested": {} }) serializer.is_valid(raise_exception=True) ``` -------------------------------- ### Django Test Naming Conventions Source: https://github.com/hacksoftware/django-styleguide/blob/master/README.md Outlines the recommended naming conventions for Django test files and test cases. Test files should be named `test_the_name_of_the_thing_that_is_tested.py`, and test classes should follow the pattern `TheNameOfTheThingThatIsTestedTests(TestCase)`. ```python def a_very_neat_service(*args, **kwargs): pass ``` ```python # project_name/app_name/tests/services/test_a_very_neat_service.py from django.test import TestCase class AVeryNeatServiceTests(TestCase): pass ``` ```python # project_name/common/utils.py def utility_function(): pass ``` ```python # project_name/common/tests/test_utils.py from django.test import TestCase class UtilityFunctionTests(TestCase): pass ``` ```python # project_name/common/utils/files.py def file_processing_function(): pass ``` ```python # project_name/common/tests/utils/test_files.py from django.test import TestCase class FileProcessingFunctionTests(TestCase): pass ```