If you’re looking to enhance your Django REST Framework project with the power of MongoDB Atlas and PyMongo, you’re in the right place. In this blog post, we’ll guide you through the process of setting up MongoDB Atlas, connecting it to Django, and incorporating the flexibility of NoSQL data handling in your Django apps development.
MongoDB is a widely used NoSQL database that stores data in a flexible, JSON-like format. To interact with MongoDB visually, we’ll be using MongoDB Compass, a user-friendly graphical interface. Before we delve into the Django integration, let’s take a moment to understand MongoDB and set it up for Python queries.
Related read: Django REST Framework with MongoDB: A Comprehensive Tutorial
Head over to the MongoDB Atlas website here and hit the “Start Free” button. Fill in your details, agree to the terms, and click “Get Started Free” to kick off the account creation.
Once your account is set up, log in to MongoDB Atlas. Click on “Build a Cluster,” choose your preferences, and click “Create Cluster.” Wait a few minutes for the cluster to be deployed.
Congratulations! MongoDB Atlas is now set up.
MongoDB Compass provides a visual representation of your MongoDB data, allowing easy exploration and interaction with databases.
🔸 Install MongoDB Compass from the official website.
🔸 Open MongoDB Compass, Click “New Connection.”
🔸 Copy the connection string from MongoDB Atlas (found on the cluster overview page) and paste it into MongoDB Compass.
🔸 Click “Connect” to establish a connection to your MongoDB Atlas cluster.
You’ve successfully set up MongoDB Atlas and connected to your cluster using MongoDB Compass. This opens up possibilities for viewing, adding, editing, and deleting documents directly in MongoDB.
Now that we’ve successfully set up MongoDB Atlas and connected to our cluster using MongoDB Compass, let’s take the next step and explore MongoDB with Python. In this section, we’ll walk through a simple example using the PyMongo library to connect to MongoDB and perform basic operations.
Before we dive into the example, ensure you have the PyMongo library installed. If not, you can easily install it using the following command:
pip install pymongo
To interact with MongoDB in Python, we’ll utilize the PyMongo library. Below is a straightforward script that establishes a connection to MongoDB Compass. Remember to replace <username> and <password> with your MongoDB Atlas cluster credentials.
from pymongo import MongoClient
# MongoDB connection URI (replace with your own connection string)
connection_uri = "mongodb+srv://<username>:<password>@cluster0.mongodb.net/test?retryWrites=true&w=majority"
# Connect to MongoDB
client = MongoClient(connection_uri)
Once connected, we can specify the database and collection we want to work with. In our example, let’s assume we have a database named ‘blog_db’ and a collection named ‘posts’:
# Select the database
db = client.blog_db
# Select the collection
collection = db.posts
Replace ‘blog_db’ and ‘posts’ with your actual database and collection names.
Now, let’s perform a simple query on the collection. Suppose we want to find all posts written by a specific author. We can define a filter for the query and execute it as follows:
# Define a filter for the query
filter_criteria = {"author": "John Doe"}
# Query the collection with the filter
result = collection.find(filter_criteria)
Replace “author”: “John Doe” with your actual filter criteria.
Finally, we can iterate over the query results and print them:
# Print the results
for document in result:
print(document)
This script will print each document (or record) that matches the specified filter criteria. It’s a simple yet powerful way to retrieve and display data from your MongoDB collection.
In the next part of our blog, we’ll continue building on this example, showcasing more advanced MongoDB operations in conjunction with Django. Stay tuned for a deeper dive into the world of MongoDB and Python integration!
Now that we’ve explored connecting to MongoDB using PyMongo, it’s time to step into the world of Django REST Framework (DRF). In this part of our blog series, I’ll guide you through setting up a full-fledged Django REST Framework project with MongoDB as the backend. We’ve chosen PyJWT for token-based authentication, aligning with the flexibility of MongoDB.
In our Django REST Framework project, the choice between PyJWT and SimpleJWT is deliberate. While SimpleJWT is tailored for SQL databases, PyJWT’s adaptability fits seamlessly with our MongoDB backend.
Step 1: Create a Virtual Environment
In your terminal, navigate to your desired directory and activate a virtual environment:
Navigate to the desired directory.
cd path/to/your/directory
Activate virtual environment.
source venv/bin/activate # For Unix/Linux
venv\Scripts\activate # For Windows
Step 2: Install Django and Required Packages
With the virtual environment activated, install Django, Django REST Framework, and PyJWT:
Install Django.
pip install django
Install Django REST Framework.
pip install djangorestframework
Install PyJWT.
pip install pyjwt
Step 3: Create a Django Project
Now that we have the necessary packages installed, create a new Django project named “django_with_mongo”:
Create a New Django Project.
django-admin startproject django_with_mongo
Navigate into the project directory.
cd django_with_mongo
Create the ‘customuser’ and ‘project’ apps:
Create ‘customuser’ app.
python manage.py startapp customuser
Create ‘project’ app.
python manage.py startapp project
Step 4: Configure Settings.py
Update the INSTALLED_APPS in the settings.py file to include the necessary apps:
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # <- Django REST Framework
'customuser', # <- 'customuser' app
'project', # <- 'project' app
Remove the DATABASE settings from settings.py, as MongoDB does not require it for configuration.
With these steps, you’ve successfully set up the foundation for your Django REST Framework project with MongoDB. In the next part, we’ll delve into creating API’s, handling user registration, and incorporating PyJWT for authentication. Stay tuned for more exciting developments in our Django and MongoDB journey!
Till now, we have set up the foundation for our Django REST Framework project with MongoDB. Now, let’s take a closer look at the authentication and permission classes that will empower our application.
MongoDB doesn’t provide built-in authentication classes, so we’ve crafted our own custom classes using the PyJWT library. Below is an explanation of the MongoDBAuthentication class:
# user/permissions.py
import os
from pymongo import MongoClient
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
client = MongoClient(os.getenv("CONNECTION_STRING"))
database = client[os.getenv("DATABASE")]
customuser_collection = database.customuser
def get_user_by_id(self, user_id):
return customuser_collection.find_one({"_id": ObjectId(user_id)})
class MongoDBAuthentication(BaseAuthentication):
"""
Class to authenticate incoming requests.
"""
def authenticate_user(self, token):
try:
# checking if token is blacklisted
black_list_collection.find(
{"token": token}
)
if black_list_collection is not None:
raise ExpiredSignatureError
# decoding token to get user_id from token payload
user_id = jwt.decode(token, os.getenv("JWT_PUBLIC_KEY"), algorithms=["RS256"])
if user_id and user_id["token_type"] == "access":
# get user object from CustomUser collection and return
user_obj = get_user_by_id(user_id["id"])
user_obj.pop("password")
return user_obj
else:
raise AuthenticationFailed(
_("Given token not valid for any token type."),
)
except InvalidSignatureError:
raise AuthenticationFailed(
_("Given token not valid for any token type."),
)
except ExpiredSignatureError:
raise AuthenticationFailed(
_("Token is invalid or expired."),
)
def authenticate(self, request):
auth_header = request.META.get('HTTP_AUTHORIZATION')
if auth_header:
key, token = auth_header.split(' ')
if key == 'Bearer':
user_data = self.authenticate_user(token)
if user_data:
user_data["is_authenticated"] = True
return parse_json(user_data), None
else:
return None
else:
raise AuthenticationFailed(
_("Authorization header must contain two space-delimited values."),
)
return None
Explanation:
🔸 Authenticates incoming requests using JWT tokens.
🔸 Examines the Authorization header in the request.
🔸 Extracts the token and attempts to decode it using the JWT public key.
🔸 Checks for revoked tokens in the black_list_collection.
🔸 Retrieves user information from the custom_user collection if the token is valid.
🔸 Returns the user data with an is_authenticated flag set to True.
🔸 Raises appropriate exceptions for invalid or expired tokens.
🔸 Parse_json converts MongoDB objects to json, find more about it in next part of the blog.
Key Points:
🔸 Stores revoked tokens in MongoDB for efficient invalidation.
🔸 Uses the RS256 algorithm for secure token signing.
🔸 Retrieves user information from a custom model, allowing flexibility in user management.
I’ve also defined custom permission classes to control API access based on user roles. Let’s explore these classes:
class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user["is_authenticated"])
Explanation:
🔸 Allows access only to authenticated users.
🔸 Checks for the presence of a valid user object in the request.
class IsManager(BasePermission):
"""
Allows access only to PM.
"""
def has_permission(self, request, view):
return bool(request.user and request.user["role"] == "PM")
Explanation:
🔸 Allows access only to users with the “PM” role.
🔸 Verifies the user’s role from their user data.
class IsAdmin(BasePermission):
"""
Allows access only to admin.
"""
def has_permission(self, request, view):
return bool(request.user and request.user["role"] == "ADMIN")
Explanation:
🔸 Allows access only to users with the “ADMIN” role.
🔸 Similarly verifies the user’s role.
Key Points:
🔸 Custom permissions allow fine-grained control over API access based on user roles.
🔸 Clear separation of concerns between authentication and authorization.
In our next blog installment, we’ll integrate these custom classes into our Django REST Framework project, showcasing a seamless flow of authentication and authorization with MongoDB.
Stay tuned for a deeper dive into the world of Django, MongoDB, and secure API access!
Now let us see a method that converts data received from MongoDB to Json, we will be using this in our API’s.
def convert_object_ids_to_str(doc):
for key, value in doc.items():
if isinstance(value, ObjectId):
doc[key] = str(value)
elif isinstance(value, datetime):
doc[key] = value.isoformat()
elif isinstance(value, dict):
convert_object_ids_to_str(value)
elif isinstance(value, list):
for i, item in enumerate(value):
if isinstance(item, dict):
convert_object_ids_to_str(item)
Explanation:
Purpose: Recursively converts MongoDB-specific ObjectIds and datetime objects within a document to their string representations, ensuring compatibility with JSON serialization.
Steps:
🔸 Iterates through each key-value pair in the document.
🔸 If the value is an ObjectId, converts it to a string using str(value).
🔸 If the value is a datetime object, converts it to ISO 8601 format using value.isoformat().
🔸 If the value is a dictionary or list, recursively calls itself to handle nested data structures.
def parse_json(data):
if isinstance(data, Cursor) | isinstance(data, CommandCursor):
parsed_data_list = list()
for obj in data:
convert_object_ids_to_str(obj)
parsed_data = json.loads(json_util.dumps(obj))
parsed_data_list.append(parsed_data)
else:
convert_object_ids_to_str(data)
parsed_data = json.loads(json_util.dumps(data))
return parsed_data
return parsed_data_list
convert_object_ids_to_str(data)
parsed_data = json.loads(json_util.dumps(data))
return parsed_data
Purpose: Converts data retrieved from MongoDB (cursors or individual documents) to valid JSON format.
Steps:
🔸 If data is a Cursor or CommandCursor:
🔸 Initializes an empty list parsed_data_list to store parsed results.
🔸 Iterates through each document in the cursor:
🔸 Calls convert_object_ids_to_str to handle ObjectIds and datetimes within the document.
🔸 Converts the document to JSON using json_util.dumps and loads it back into a Python dictionary for proper parsing.
🔸 Appends the parsed document to parsed_data_list.
🔸 Returns the list of parsed documents.
🔸 If data is a single document:
🔸 Calls convert_object_ids_to_str to handle ObjectIds and datetimes.
🔸 Converts the document to JSON using json_util.dumps and loads it back into a Python dictionary.
🔸 Returns the parsed document.
In our Django REST Framework project, seamless user authentication is a cornerstone, and I’ve meticulously designed a suite of APIs to manage user registration, login, and more. Let’s delve into the functionality of each API, unveiling the MongoDB intricacies woven into the fabric of our authentication system.
Endpoint: /api/user/signup/
Method: POST
Description: This API empowers users to register by providing essential details such as first name, last name, email, role, and a password. Upon a successful registration, it returns the user’s unique identifier (id) accompanied by a 201 Created status.
MongoDB Aspect:
The user’s registration data is intelligently stored in MongoDB, capitalizing on its flexibility to adeptly handle diverse data structures.
# customuser/views.py
def create_user(self, user_data):
try:
customuser_collection.create_index([('email', 1)], unique=True)
user_data['password'] = make_password(user_data['password'])
user_data["created_at"] = datetime.now(timezone.utc)
user_data["updated_at"] = datetime.now(timezone.utc)
result = customuser_collection.insert_one(user_data)
return result.inserted_id
except DuplicateKeyError as e:
raise CustomException("User already exists.")
class SignupAPIView(CreateAPIView):
"""
Class to create register new user.
"""
permission_classes = ()
authentication_classes = ()
serializer_class = UserProfileSerializer
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(SignupAPIView, self).__init__(**kwargs)
def post(self, request, *args, **kwargs):
"""
Method to create and register a new user.
"""
user_serializer = self.get_serializer(data=request.data)
if user_serializer.is_valid(raise_exception=True):
user_data = user_serializer.validated_data
user_id = create_user(user_data)
if user_id:
self.response_format["status_code"] = status.HTTP_201_CREATED
self.response_format["data"] = {"id": json.loads(json_util.dumps(user_id))["$oid"]}
self.response_format["error"] = None
self.response_format["message"] = [messages.CREATED.format("User")]
else:
self.response_format["status_code"] = status.HTTP_400_BAD_REQUEST
self.response_format["data"] = None
self.response_format["error"] = "User"
self.response_format["message"] = [messages.UNEXPECTED_ERROR]
return Response(self.response_format)
Explanation:
🔸 The SignupAPIView class inherits from CreateAPIView, streamlining the creation of a new user.
🔸 MongoDB effortlessly handles the diverse user data structures, ensuring a seamless registration process.
🔸 The create_user method integrates MongoDB to store user information securely.
🔸 The response is carefully formatted to include the user’s unique identifier and relevant status codes.
Endpoint: /api/user/login/
Method: POST
Description: Users can log in using their credentials through this API. Upon a successful login, it returns a JSON Web Token (JWT) containing user information, facilitating secure authentication for subsequent requests.
MongoDB Aspect:
User credentials are verified against MongoDB, ensuring a seamless integration between the Django project and the MongoDB database.
# customuser/views.py
def get_user_by_email(self, email):
return customuser_collection.find_one({'email': email})
class LoginAPIView(CreateAPIView):
"""
Class to log in user.
"""
permission_classes = ()
authentication_classes = ()
serializer_class = UserLoginSerializer
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(LoginAPIView, self).__init__(**kwargs)
def post(self, request, *args, **kwargs):
"""
Method to login user and return jwt tokens.
"""
serializer = self.get_serializer(data=request.data)
if serializer.is_valid(raise_exception=True):
user = get_user_by_email(serializer.validated_data['username'])
if user and check_password(serializer.validated_data['password'], user['password']):
user = parse_json(user)
jwt_token = get_tokens_for_user(user)
response_data = {
"first_name": user["first_name"],
"last_name": user["last_name"],
"email_name": user["email"],
"role": user["role"],
"token": jwt_token,
}
self.response_format["status_code"] = status.HTTP_200_OK
self.response_format["data"] = response_data
self.response_format["error"] = None
self.response_format["message"] = [messages.LOGIN_SUCCESS]
else:
self.response_format["status_code"] = status.HTTP_200_OK
self.response_format["data"] = None
self.response_format["error"] = "User"
self.response_format["message"] = [messages.INVALID_CREDENTIALS]
return Response(self.response_format)
Endpoint: /api/user/logout/
Method: POST
Description: Users can log out through this API, which not only invalidates the access token but also blacklists it for enhanced security. The API requires an authentication header with the access token.
MongoDB Aspect:
The blacklisted tokens are stored in a MongoDB collection, providing a centralized mechanism to track and manage token revocation.
# customuser/views.py
class LogoutAPIView(CreateAPIView):
"""
Class for creating API view for user logout.
"""
permission_classes = (IsAuthenticated,)
authentication_classes = (MongoDBAuthentication,)
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(LogoutAPIView, self).__init__(**kwargs)
def post(self, request, *args, **kwargs):
"""
POST Method for logging out the user and blacklisting the access token used.
"""
auth_header = request.META.get('HTTP_AUTHORIZATION')
if auth_header:
key, access_token = auth_header.split(' ')
refresh_token = request.data.get("refresh")
black_list_collection.insert_many(
[{"token": access_token, "timestamp": datetime.now(timezone.utc)},
{"token": refresh_token, "timestamp": datetime.now(timezone.utc)}]
)
self.response_format["status_code"] = status.HTTP_200_OK
self.response_format["data"] = None
self.response_format["error"] = None
self.response_format["message"] = [messages.LOGOUT_SUCCESS]
return Response(self.response_format)
Endpoint: /api/user/user/
Method: GET
Description: Retrieves information about the user currently logged in. Requires a valid access token for authentication.
MongoDB Aspect:
User information is retrieved from MongoDB based on the authenticated user’s data.
# customuser/views.py
class GetUser(RetrieveAPIView):
permission_classes = (IsAuthenticated,)
authentication_classes = (MongoDBAuthentication,)
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(GetUser, self).__init__(**kwargs)
def get(self, request, *args, **kwargs):
self.response_format["data"] = request.user
self.response_format["error"] = None
self.response_format["status_code"] = status.HTTP_200_OK
self.response_format["message"] = [messages.SUCCESS]
return Response(self.response_format)
We’re not done yet! There’s more to discover about building awesome features using Django REST Framework and MongoDB. So, stick around to learn even more cool stuff!
Let’s dive into how we can create, read, update, and delete data (CRUD operations) in our Django REST Framework project, using MongoDB as our database!
Certainly! In our example, imagine you’re building a system for managing clients and projects. Clients are like the companies you work with, and each client can have multiple projects.
Related read: Automating Database Translation in Django REST Framework Using gTranslate Library
Method: POST
Endpoint: /project/createClient/
Description: Allows the creation of a new client.
Permissions: Only authenticated users with admin privileges can create clients.
MongoDB Aspect:
The client data is serialized and stored in the client_collection MongoDB collection. The created_by field is set to the ObjectId of the user creating the client.
# project/views.py
def create_client(self, client_data):
client_data["created_at"] = datetime.now(timezone.utc)
client_data["updated_at"] = datetime.now(timezone.utc)
result = client_collection.insert_one(client_data)
return result.inserted_id
def get_client_by_id(self, client_id):
return client_collection.find_one({"_id": ObjectId(client_id)})
class CreateClientAPIView(CreateAPIView):
"""
Class to create client.
"""
permission_classes = (IsAuthenticated, IsAdmin)
authentication_classes = (MongoDBAuthentication,)
serializer_class = ClientSerializer
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(CreateClientAPIView, self).__init__(**kwargs)
def post(self, request, *args, **kwargs):
"""
Post method to create client.
"""
client_serializer = self.get_serializer(data=request.data)
if client_serializer.is_valid(raise_exception=True):
request_data = client_serializer.data
request_data["created_by"] = ObjectId(request.user["_id"])
client_id = create_client(request_data)
client = get_client_by_id(client_id)
self.response_format["status_code"] = status.HTTP_201_CREATED
self.response_format["data"] = parse_json(client)
self.response_format["error"] = None
self.response_format["message"] = [messages.CREATED.format("Client")]
return Response(self.response_format)
Method: PATCH
Endpoint: /project/updateClient/<client_id>/
Description: Update details of a specific client.
Permissions: Only authenticated admin can update client details.
MongoDB Aspect:
Updates client details in the client_collection based on the provided client_id.
# project/views.py
class UpdateClientAPIView(UpdateAPIView):
"""
Class to create API for updating client.
"""
authentication_classes = (MongoDBAuthentication,)
permission_classes = (IsAuthenticated, IsAdmin)
serializer_class = ClientSerializer
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(UpdateClientAPIView, self).__init__(**kwargs)
def patch(self, request, *args, **kwargs):
"""
Method to update client.
"""
client_id = self.kwargs["pk"]
client_serializer = self.get_serializer(data=request.data)
if client_serializer.is_valid(raise_exception=True):
filter_condition = {"_id": ObjectId(client_id)}
update_fields = {
"$set": {
"first_name": client_serializer.validated_data["first_name"],
"last_name": client_serializer.validated_data["last_name"],
"company_name": client_serializer.validated_data["company_name"],
"company_address": client_serializer.validated_data["company_address"],
"description": client_serializer.validated_data["description"],
"updated_at": datetime.now(timezone.utc),
}
}
result = client_collection.update_one(filter_condition, update_fields)
if result.matched_count == 0:
self.response_format["status_code"] = status.HTTP_400_BAD_REQUEST
self.response_format["data"] = None
self.response_format["error"] = "Client"
self.response_format["message"] = [messages.DOES_NOT_EXIST.format("Client")]
elif result.matched_count == result.modified_count:
self.response_format["status_code"] = status.HTTP_200_OK
self.response_format["data"] = None
self.response_format["error"] = None
self.response_format["message"] = [messages.UPDATED.format("Client")]
else:
self.response_format["status_code"] = status.HTTP_500_INTERNAL_SERVER_ERROR
self.response_format["data"] = None
self.response_format["error"] = "Unexpected error"
self.response_format["message"] = [messages.UNEXPECTED_ERROR]
return Response(self.response_format)
Method: DELETE
Endpoint: /deleteClient/<client_id>/
Description: Delete a specific client and associated projects.
Permissions: Only authenticated admin can delete client details.
MongoDB Aspect:
Deletes the client and its associated projects from client_collection and project_collection, respectively.
# project/views.py
class DeleteClientAPIView(DestroyAPIView):
"""
Class to create API to delete client.
"""
authentication_classes = (MongoDBAuthentication,)
permission_classes = (IsAuthenticated, IsAdmin)
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(DeleteClientAPIView, self).__init__(**kwargs)
def delete(self, request, *args, **kwargs):
"""
Method to delete client.
"""
try:
client_id = self.kwargs["pk"]
# If you want to keep project details and delete only client, set foreignkey to null for client to be deleted.
# projects = project_collection.update_many(
# {"client_id": ObjectId(client_id)},
# {"$set": {"client_id": None}}
# )
# Delete client and all related projects.
projects = project_collection.delete_many({"client_id": ObjectId(client_id)})
client = client_collection.delete_one({"_id": ObjectId(client_id)})
if client.deleted_count > 0:
self.response_format["status_code"] = status.HTTP_204_NO_CONTENT
self.response_format["data"] = None
self.response_format["error"] = None
self.response_format["message"] = [messages.DELETED.format("Client")]
else:
self.response_format["status_code"] = status.HTTP_400_BAD_REQUEST
self.response_format["data"] = None
self.response_format["error"] = "Client"
self.response_format["message"] = [messages.DOES_NOT_EXIST.format("Client")]
except InvalidId:
self.response_format["status_code"] = status.HTTP_400_BAD_REQUEST
self.response_format["data"] = None
self.response_format["error"] = "Client"
self.response_format["message"] = [messages.INVALID_ID.format("Client")]
return Response(self.response_format)
These snippets provide a glimpse into the elegance of combining Django REST Framework with MongoDB, creating a robust foundation for handling client-related operations. In the next segment, we will explore project-related APIs, unraveling the interplay between Django and MongoDB for seamless project management. Stay tuned for more!
Method: POST
Endpoint: /createProject/
Description: Create a new Project.
Permissions: Only authenticated admin can create Project.
MongoDB Aspect:
Serializes and stores project data in project_collection. Associates the project with an existing client based on client_id.
# project/views.py
def create_project(self, project_data):
project_data["created_at"] = datetime.now(timezone.utc)
project_data["updated_at"] = datetime.now(timezone.utc)
result = project_collection.insert_one(project_data)
return result.inserted_id
def get_project_by_id(self, project_id):
return project_collection.find_one({"_id": ObjectId(project_id)})
class CreateProjectAPIView(CreateAPIView):
"""
Class to create Project.
"""
authentication_classes = (MongoDBAuthentication,)
permission_classes = (IsAuthenticated, IsAdmin)
serializer_class = ProjectSerializer
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(CreateProjectAPIView, self).__init__(**kwargs)
def post(self, request, *args, **kwargs):
"""
Post method to create client.
"""
project_serializer = self.get_serializer(data=request.data)
if project_serializer.is_valid(raise_exception=True):
request_data = project_serializer.data
request_data["created_by"] = ObjectId(request.user["_id"])
request_data["client_id"] = ObjectId(request_data["client_id"])
project_id = create_project(request_data)
project = get_project_by_id(project_id)
self.response_format["status_code"] = status.HTTP_201_CREATED
self.response_format["data"] = parse_json(project)
self.response_format["error"] = None
self.response_format["message"] = [messages.CREATED.format("Client")]
return Response(self.response_format)
Method: GET
Endpoint: /getProjectDetails/<project_id>/
Description: Retrieve details of a specific Project.
Permissions: Only authenticated users(admin or manager) can get project details.
MongoDB Aspect:
Retrieves project details from project_collection with client details embedded via a $lookup.
# project/views.py
class GetProjectAPIView(RetrieveAPIView):
"""
Class to create API to get project.
"""
authentication_classes = (MongoDBAuthentication, )
permission_classes = (IsAuthenticated, (IsManager | IsAdmin))
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(GetProjectAPIView, self).__init__(**kwargs)
def get_project(self, project_id):
project = project_collection.aggregate(
[
{
"$match": {"_id": ObjectId(project_id)}
},
{
"$lookup": {
"from": "client",
"localField": "client_id",
"foreignField": "_id",
"as": "client"
}
},
{
"$project": {
"_id": 1,
"name": 1,
"cost": 1,
"description": 1,
"created_at": 1,
"updated_at": 1,
"client": {
"first_name": {"$arrayElemAt": ["$client.first_name", 0]},
"last_name": {"$arrayElemAt": ["$client.last_name", 0]}
}
}
}
]
)
return next(project, None)
def get(self, request, *arg, **kwargs):
"""
Method to get Project details.
"""
project_id = self.kwargs["pk"]
try:
project = self.get_project(project_id)
if project:
self.response_format["status_code"] = status.HTTP_200_OK
self.response_format["data"] = parse_json(project)
self.response_format["error"] = None
self.response_format["message"] = [messages.SUCCESS]
else:
self.response_format["status_code"] = status.HTTP_400_BAD_REQUEST
self.response_format["data"] = None
self.response_format["error"] = "Client"
self.response_format["message"] = [messages.DOES_NOT_EXIST.format("Client")]
except InvalidId:
self.response_format["status_code"] = status.HTTP_400_BAD_REQUEST
self.response_format["data"] = None
self.response_format["error"] = "Client"
self.response_format["message"] = [messages.INVALID_ID.format("Client")]
return Response(self.response_format)
Method: PATCH
Endpoint: /updateProject/<project_id>/
Description: Update details of a specific Project.
Permissions: Only authenticated users(admin or manager) can update Project details.
MongoDB Aspect:
Updates project details in project_collection based on the provided project_id.
# project/views.py
class UpdateProjectAPIView(UpdateAPIView):
"""
Class to create API for updating project.
"""
authentication_classes = (MongoDBAuthentication,)
permission_classes = (IsAuthenticated, (IsManager | IsAdmin))
serializer_class = ProjectSerializer
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(UpdateProjectAPIView, self).__init__(**kwargs)
def patch(self, request, *args, **kwargs):
"""
Method to update project.
"""
project_id = self.kwargs["pk"]
try:
project_serializer = self.get_serializer(data=request.data)
if project_serializer.is_valid(raise_exception=True):
filter_condition = {"_id": ObjectId(project_id)}
update_fields = {
"$set": {
"name": project_serializer.validated_data["name"],
"cost": project_serializer.validated_data["cost"],
"client_id": project_serializer.validated_data["client_id"],
"description": project_serializer.validated_data["description"],
"updated_at": datetime.now(timezone.utc),
}
}
result = project_collection.update_one(filter_condition, update_fields)
if result.matched_count == 0:
self.response_format["status_code"] = status.HTTP_400_BAD_REQUEST
self.response_format["data"] = None
self.response_format["error"] = "Client"
self.response_format["message"] = [messages.DOES_NOT_EXIST.format("Project")]
elif result.matched_count == result.modified_count:
self.response_format["status_code"] = status.HTTP_200_OK
self.response_format["data"] = None
self.response_format["error"] = None
self.response_format["message"] = [messages.UPDATED.format("Project")]
else:
self.response_format["status_code"] = status.HTTP_500_INTERNAL_SERVER_ERROR
self.response_format["data"] = None
self.response_format["error"] = "Unexpected error"
self.response_format["message"] = [messages.UNEXPECTED_ERROR]
except InvalidId:
self.response_format["status_code"] = status.HTTP_400_BAD_REQUEST
self.response_format["data"] = None
self.response_format["error"] = "Project"
self.response_format["message"] = [messages.INVALID_ID.format("Project")]
return Response(self.response_format)
Method: DELETE
Endpoint: /deleteProject/<project_id>/
Description: Delete a specific Project.
Permissions: Only authenticated admin can delete Project details.
MongoDB Aspect:
Deletes the project from project_collection. Associated projects with the client are deleted as well.
# project/views.py
class DeleteProjectAPIView(DestroyAPIView):
"""
Class to create API to delete project.
"""
authentication_classes = (MongoDBAuthentication,)
permission_classes = (IsAuthenticated, IsAdmin)
def __init__(self, **kwargs):
"""
Constructor function for formatting the web response to return.
"""
self.response_format = ResponseInfo().response
super(DeleteProjectAPIView, self).__init__(**kwargs)
def delete(self, request, *args, **kwargs):
"""
Method to delete project.
"""
try:
project_id = self.kwargs["pk"]
client = project_collection.delete_one({"_id": ObjectId(project_id)})
if client.deleted_count > 0:
self.response_format["status_code"] = status.HTTP_204_NO_CONTENT
self.response_format["data"] = None
self.response_format["error"] = None
self.response_format["message"] = [messages.DELETED.format("Project")]
else:
self.response_format["status_code"] = status.HTTP_400_BAD_REQUEST
self.response_format["data"] = None
self.response_format["error"] = "Project"
self.response_format["message"] = [messages.DOES_NOT_EXIST.format("Project")]
except InvalidId:
self.response_format["status_code"] = status.HTTP_400_BAD_REQUEST
self.response_format["data"] = None
self.response_format["error"] = "Project"
self.response_format["message"] = [messages.INVALID_ID.format("Project")]
return Response(self.response_format)
We built a cool app using Django and MongoDB! We made sure only authorized users can access it and added features to manage clients and projects. Everything works smoothly and handles any hiccups. Basically, Django and MongoDB work great together!
How to Effectively Hire and Manage A Remote Team of Developers
Download NowRegister Now for the Masterclass to Epic Integration with SMART on FHIR Webinar on Thursday, 10th April 2025 at: 11:00 AM EDT
Register NowMindbowser played a crucial role in helping us bring everything together into a unified, cohesive product. Their commitment to industry-standard coding practices made an enormous difference, allowing developers to seamlessly transition in and out of the project without any confusion....
CEO, MarketsAI
I'm thrilled to be partnering with Mindbowser on our journey with TravelRite. The collaboration has been exceptional, and I’m truly grateful for the dedication and expertise the team has brought to the development process. Their commitment to our mission is...
Founder & CEO, TravelRite
The Mindbowser team's professionalism consistently impressed me. Their commitment to quality shone through in every aspect of the project. They truly went the extra mile, ensuring they understood our needs perfectly and were always willing to invest the time to...
CTO, New Day Therapeutics
I collaborated with Mindbowser for several years on a complex SaaS platform project. They took over a partially completed project and successfully transformed it into a fully functional and robust platform. Throughout the entire process, the quality of their work...
President, E.B. Carlson
Mindbowser and team are professional, talented and very responsive. They got us through a challenging situation with our IOT product successfully. They will be our go to dev team going forward.
Founder, Cascada
Amazing team to work with. Very responsive and very skilled in both front and backend engineering. Looking forward to our next project together.
Co-Founder, Emerge
The team is great to work with. Very professional, on task, and efficient.
Founder, PeriopMD
I can not express enough how pleased we are with the whole team. From the first call and meeting, they took our vision and ran with it. Communication was easy and everyone was flexible to our schedule. I’m excited to...
Founder, Seeke
Mindbowser has truly been foundational in my journey from concept to design and onto that final launch phase.
CEO, KickSnap
We had very close go live timeline and Mindbowser team got us live a month before.
CEO, BuyNow WorldWide
If you want a team of great developers, I recommend them for the next project.
Founder, Teach Reach
Mindbowser built both iOS and Android apps for Mindworks, that have stood the test of time. 5 years later they still function quite beautifully. Their team always met their objectives and I'm very happy with the end result. Thank you!
Founder, Mindworks
Mindbowser has delivered a much better quality product than our previous tech vendors. Our product is stable and passed Well Architected Framework Review from AWS.
CEO, PurpleAnt
I am happy to share that we got USD 10k in cloud credits courtesy of our friends at Mindbowser. Thank you Pravin and Ayush, this means a lot to us.
CTO, Shortlist
Mindbowser is one of the reasons that our app is successful. These guys have been a great team.
Founder & CEO, MangoMirror
Kudos for all your hard work and diligence on the Telehealth platform project. You made it possible.
CEO, ThriveHealth
Mindbowser helped us build an awesome iOS app to bring balance to people’s lives.
CEO, SMILINGMIND
They were a very responsive team! Extremely easy to communicate and work with!
Founder & CEO, TotTech
We’ve had very little-to-no hiccups at all—it’s been a really pleasurable experience.
Co-Founder, TEAM8s
Mindbowser was very helpful with explaining the development process and started quickly on the project.
Executive Director of Product Development, Innovation Lab
The greatest benefit we got from Mindbowser is the expertise. Their team has developed apps in all different industries with all types of social proofs.
Co-Founder, Vesica
Mindbowser is professional, efficient and thorough.
Consultant, XPRIZE
Very committed, they create beautiful apps and are very benevolent. They have brilliant Ideas.
Founder, S.T.A.R.S of Wellness
Mindbowser was great; they listened to us a lot and helped us hone in on the actual idea of the app. They had put together fantastic wireframes for us.
Co-Founder, Flat Earth
Ayush was responsive and paired me with the best team member possible, to complete my complex vision and project. Could not be happier.
Founder, Child Life On Call
The team from Mindbowser stayed on task, asked the right questions, and completed the required tasks in a timely fashion! Strong work team!
CEO, SDOH2Health LLC
Mindbowser was easy to work with and hit the ground running, immediately feeling like part of our team.
CEO, Stealth Startup
Mindbowser was an excellent partner in developing my fitness app. They were patient, attentive, & understood my business needs. The end product exceeded my expectations. Thrilled to share it globally.
Owner, Phalanx
Mindbowser's expertise in tech, process & mobile development made them our choice for our app. The team was dedicated to the process & delivered high-quality features on time. They also gave valuable industry advice. Highly recommend them for app development...
Co-Founder, Fox&Fork