Add a new feature - Backend
In this tutorial, we walk through adding a new feature to the application. The focus is on the backend.
You have launched your app to production in 5mins. Congrats. You are collecting emails for the concept, but now you have to do the dirty work to build a feature. You are in luck. It is very easy.
The tutorial will cover the entire workflow, from understanding feature requests to creating a pull request.
Understanding Feature Requests
The first step in adding a new feature is understanding the feature request.
User stories are a good way to think about what you want to build. It puts you in the perspective of the user. For example
As a user i want to create a ‘store’ app where I can see all of my employees, their departments and update their details
Based on this we can come up with a list of features to build.
-
Model - Store employees and departments
-
Views - Create API, InertiaJS views
-
Schemas - (or forms if you prefere) - Define my data input and output structures or Da
Creating a Branch
Next, you need to create a new branch for the feature. Open your terminal, navigate to your project directory, and run the following command to create and switch to a new branch:
$ git checkout -b feature-branch-name
Planning
Break down the feature request into smaller tasks. Write down these tasks and prioritize them. For example, if you’re adding a new webhooks feature, your tasks might include:
-
Adding a model with fields for name, URL, and date sent
-
Creating a new webhooks endpoint
-
Adding API views and wiring up the URLs
-
Displaying a list of all the webhooks
Backend Model Work
Identify if the feature requires changes to the data model. If changes are required, update the models in your Django application.
from apps.tenants.mixins import TenantModelManager
from apps.tenants.models import TenantModelMixin
class Department(models.Model):
title = models.CharField(max_length=100)
class EmployeeManager(TenantModelManager)
def change_employee_department(self, data) -> Employee | None:
employee = self.filter(id=data.id).first()
if employee is None:
return None
employee.department = data.department
employee.save()
return employee
class Employee(TenantModelMixin):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
department = models.ForeignKey(Department, on_delete=models.CASCADE)
birthdate = models.DateField(null=True, blank=True)
cv = models.FileField(null=True, blank=True)
-
Keep business logic in the manage or create a new services file. Do not add this business logic to the view. It will make your app 10x times simpler to test.
-
Use the TenantModelMixin and TenantModelManager for both
API Addition with Django Ninja
Create a new API endpoint in Django using Django Ninja. Define the necessary HTTP methods (GET, POST, etc.) for the endpoints and schemas.
Create the schemas
class EmployeeInSchema(Schema):
first_name: str
last_name: str
department_id: int = None
birthdate: date = None
class Config:
alias_generator = humps.camelize
allow_population_by_field_name = True
class EmployeeOutSchema(Schema):
id: int
first_name: str
last_name: str
department_id: int = None
birthdate: date = None
class Config:
alias_generator = humps.camelize
allow_population_by_field_name = True
Create the API endpoints
This is straight from the django ninja docs
@api.post(
"/employees/",
response="EmployeeOutSchema",
operationId="createEmployee",
by_alias=True
)
def create_employee(request, payload: EmployeeIn):
employee = Employee.objects.create(**payload.dict())
return {"id": employee.id}
@api.get(
"/employees/{employee_id}/",
response=EmployeeOutSchema,
operationId="getEmployee",
by_alias=True
)
def get_employee(request, employee_id: int):
employee = get_object_or_404(Employee, id=employee_id)
return employee
@api.get(
"/employees/",
response=List[EmployeeOutSchema],
operationId="listEmployee",
by_alias=True
)
def list_employees(request):
qs = Employee.objects.all()
return qs
@api.put(
"/employees/{employee_id}/",
response=EmployeeSchema,
operationId="updateEmployee",
by_alias=True
)
def update_employee(request, employee_id: int, payload: EmployeeIn):
employee = get_object_or_404(Employee, id=employee_id)
for attr, value in payload.dict().items():
setattr(employee, attr, value)
employee.save()
employee.refresh_from_db()
return employee
@api.delete(
"/employees/{employee_id}",
response=EmployeeOutSchema,
operationId="deleteEmployee",
by_alias=True
)
def delete_employee(request, employee_id: int):
employee = get_object_or_404(Employee, id=employee_id)
employee.delete()
return {"success": True}
-
We added response to the api model. This will expose the correct schemas on the frontend.
-
Also include tags for EVERY endpoint. We can that access the data via the tags interface in SwaggerJS
InertiaJS Views
Next step is to create the views. You probably at need at least one view to show the list. The views for the detail and edit page are optional.
from django.shortcuts import get_object_or_404
from apps.common.inertia import inertia
@inertia("app/store/employees")
@login_required
def employees(request):
return {} # <- you can return a list of the apis here if you prefer
@inertia("app/store/employee-detail")
@login_required
def employee_detail(request, id):
get_object_or_404(Employee, id=id)
return {} # <- you can return the object here if you want
-
import the inertia render decorator from the common module.
-
URLs Wiring
Add a new URL pattern in Django’s urls.py for your InertiaJS view. Test the URL to ensure it’s correctly wired to the view.
Frontend Components
Identify the frontend components needed for the feature (e.g., a datatable). Create these components using InertiaJS and ensure they interact correctly with your API.
Checkout the frontend section for how to create queries and mutations for the form, add a list view
Testing
Write tests for your models, APIs, and views. Run the tests to ensure everything is working as expected.
Committing Changes
Stage your changes with
git add .
, then commit your changes with
git commit -m "commit message"
.
Creating a Pull Request
Push your branch to GitHub with
git push origin feature-branch-name
. Then, go to your repository on GitHub and create a new pull request for your branch.
This tutorial provides a step-by-step guide to working with Django, Django Ninja, InertiaJS, and Git. It assumes that you have a basic understanding of these tools.