Github and REST API in Djngo Part 6
Github and REST API in Djngo Part 6
● This will save all installed Python packages into a requirements.txt file.
● Inside the project folder, create a file named .gitignore and add:
# Python
*.pyc
__pycache__/
# Django
db.sqlite3
/staticfiles/
/media/
.env
env/
venv/
# VS Code
.vscode/
# OS Files
.DS_Store
# Git
.git
● This tells Git which files and folders to ignore (not track or upload).
git add .
8. Push to GitHub
9. Verify on GitHub
Note: before pushing the Django project to GitHub, configure your name and email to the
git.
● Django REST Framework (DRF) is a powerful and flexible toolkit for building Web
APIs using Django.
● It is the most widely used framework in the Python world for developing RESTful
APIs.
● DRF is built on top of Django, so it supports:
○ Django models
○ Django views
○ Django authentication system
○ Django ORM
Website: https://www.django-rest-framework.org
What is an API?
● Client application can send the request to these endpoints to get or save products,
orders, shopping carts and so on
● API Provider: The backend application that exposes the API (usually Django)
● API Consumer: The application that uses the API, like a React app or mobile app
Sometimes, one backend can consume APIs of another — e.g., a Django app calling a
3rd-party weather API.
A RESTful API follows these REST principles and allows client applications (like mobile apps
or frontend apps) to interact with backend resources via HTTP methods.
○ Fast
○ Scalable
○ Stateless
○ Reliable
○ Easy to use
○ Easy to evolve and maintain
1. Resources
● A resource is any object or data in the application — such as Product, User, Order, etc.
Each resource is identified by a URL.
● An URL is a way to locate a resource on the web, it is basically a web address
Example:
2. Resource Representations
● When a client accesses a resource via URL, the server responds with a representation
of that resource.
● This representation is usually in:
● JSON ✅ (most common)
● XML
● HTML (in browser views)
● These are not the internal representation of a resource inside the server, the server uses
objects (like Python classes) to manage these resources.
● When building a REST API we expose one or more endpoints for clients, each endpoint
may support various kinds of operations like some endpoints may allow reading data
while other may allow modifying data, here HTTP methods come into the picture.
● REST APIs use HTTP methods to perform actions on resources:
● Using the HTTP methods, the client can tell the server what he wants to do with the
resource.
Request Body:
"price": 90000
}
Server Action: Creates a new product and returns the saved object with its unique ID.
Note: For more information about the Web-services, REST API, please refer to the Flask
REST API related notes.
Example:
cd DjangoRestProject
class Product(models.Model):
CATEGORY_CHOICES = [
("Electronics", "Electronics"),
("Stationary", "Stationary"),
("HomeAppliance", "HomeAppliance")
name = models.CharField(max_length=20)
price = models.IntegerField()
quantity = models.IntegerField()
category = models.CharField(max_length=20,
choices=CATEGORY_CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Review(models.Model):
review_text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
● Register both classes inside the ProductApp/admin.py file to manage them from the
admin interface.
class ProductAdmin(admin.ModelAdmin):
list_filter = ('category',)
class ReviewAdmin(admin.ModelAdmin):
list_filter = ('rating',)
admin.site.register(Product, ProductAdmin)
admin.site.register(Review, ReviewAdmin)
● Create a super user and run the server, after that add some products and their reviews
from the admin interface by login into it.
● Register it inside our project in the settings.py file right after the inbuilt django apps
INSTALLED_APPS = [
—---
'rest_framework',
'ProductApp',
● However, in Django REST Framework (DRF), we use more powerful and flexible
classes:
○ Request
○ Response
Example: FBV
● Define the following function based views inside the ProductApp/views.py file
@api_view()
def product_list_view(request):
return Response('OK')
@api_view()
return Response(id)
● Specify the url patterns for the above views inside the ProductApp/urls.py file
urlpatterns = [
]
● Include the above urls.py file to project level urls.py file.
urlpatterns = [
path('admin/', admin.site.urls),
path('products/', include('ProductApp.urls'))
http://127.0.0.1:8000/products/
http://127.0.0.1:8000/products/10/
● It also enables the Browsable API interface, which allows easy testing via a web
browser.
● When a client app (e.g., mobile app or frontend app) consumes this API, it will receive
only the JSON response — not the browsable interface.
● Serializers in DRF help convert Django model instances (Python objects) into JSON
and vice versa.
● They play the same role as forms in regular Django, but instead of HTML form handling,
serializers handle JSON data.
● Create a new file called serializers.py inside the ProductApp folder. Inside this file
define a serializer class to serialize and deserialize the Product model class object.
class ProductSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField(max_length=20)
price = serializers.IntegerField()
quantity = serializers.IntegerField()
category = serializers.CharField()
Here we can exclude some fields also from the above ProductSerializer class.
@api_view()
product = Product.objects.get(pk=id)
serializer = ProductSerializer(product)
return Response(serializer.data)
Run the server and try to access the following endpoint with a specific product id.
http://127.0.0.1:8000/products/1
@api_view()
try:
product = Product.objects.get(pk=id)
serializer = ProductSerializer(product)
return Response(serializer.data)
except Product.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
@api_view()
serializer = ProductSerializer(product)
return Response(serializer.data)
@api_view()
def product_list_view(request):
products = Product.objects.all()
return Response(serializer.data)
🧠 Note: many=True tells DRF that we are serializing a queryset instead of a single object.
● Test the following endpoint to access all the products:
http://127.0.0.1:8000/products/
● In DRF, your API model (external representation) doesn't have to match your data
model (internal representation).
● This allows you to:
○ Rename fields in the API.
○ Add computed or derived fields.
○ Hide internal fields.
class ProductSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField(max_length=20)
quantity = serializers.IntegerField()
category = serializers.CharField()
unit_price = serializers.IntegerField(source='price')
price_with_tax = serializers.SerializerMethodField(
method_name='calculate_tax')
return product.price * 2
🧠 Explanation:
● source='price': Maps unit_price to the actual price field in the model.
● When serializing a model like Product, you may want to include related models (e.g.
Review) in the output.
Step1: Create a serializer class for Review model class inside the ProductApp/serializers.py
file.
class ReviewSerializer(serializers.Serializer):
id = serializers.IntegerField()
review_text = serializers.CharField()
created_at = serializers.DateTimeField()
class ProductSerializer(serializers.Serializer):
name = serializers.CharField(max_length=20)
quantity = serializers.IntegerField()
category = serializers.CharField()
unit_price = serializers.IntegerField(source='price')
price_with_tax = serializers.SerializerMethodField(
method_name='calculate_tax')
return product.price * 2
● DRF will look for a reverse relation called review_set on the Product model
(auto-generated by Django since Review has product = ForeignKey(...)).
● By default, Django creates the reverse relation with the lowercase model name + _set.
● So, in your Review model:
● This creates an implicit reverse relation called review_set on the Product model.
● So DRF will look for:
product.review_set.all()
● read_only = True:
○ The reviews field is only used when serializing data (i.e., when returning a
Product to the client).
○ It will be ignored during deserialization (i.e., when creating or updating a
Product from client input).
● If you want a cleaner reverse accessor (instead of review_set), you can explicitly set it
in the model:
This is cleaner and more readable in both code and the API output.
ModelSerializer:
Example:
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
Example:
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
Example:
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
Example:
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
unit_price = serializers.IntegerField(
price_with_tax = serializers.SerializerMethodField(
method_name='calculate_tax')
● Deserialization is the process of converting incoming JSON data (from a client) into a
Django model instance.
For Example if the API client will send the product data:
POST /products
And send the product related json data inside the body of the request
"name": "Speaker",
"price": 8000,
"category": "Electronics"
We need to read the data from the request body and Deserialize this data in the
Product object.
@api_view(['GET', 'POST'])
def product_list_view(request):
if request.method == 'GET':
products = Product.objects.all()
return Response(serializer.data)
serializer = ProductSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
print(serializer.validated_data) # To get the validated
data
return Response(serializer.data,
status=status.HTTP_201_CREATED)
● Once POST is added to @api_view, the Browsable API will show an interactive form
to send data.
{
"name": "Printer",
"unit_price": 7000,
"quantity": 12,
"category": "Electronics"
Data Validation:
serializer = ProductSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
● Using raise_exception=True automatically returns a 400 response with error
details if validation fails.
● Custom validations are used when built-in validation isn’t enough. In DRF, we can
validate incoming data using three main approaches:
class ProductSerializer(serializers.ModelSerializer):
—--
return value
2. Object-Level Validation:
● Use this when you need to validate multiple fields together, like comparing them.
● For example, comparing password with confirm_password.
● Use the validate(self, data) method for object-level validations.
class ProductSerializer(serializers.ModelSerializer):
re_enter_price = serializers.IntegerField(write_only=True)
return data
class Meta:
model = Product
● You can write external functions and attach them to specific fields using the
validators=[] argument.
def validate_name_starts_with_letter(value):
if value[0].isdigit():
return value
class ProductSerializer(serializers.ModelSerializer):
name =
serializers.CharField(validators=[validate_name_starts_with_letter])
class Meta:
model = Product
@api_view(['GET', 'PUT'])
if request.method == 'GET':
serializer = ProductSerializer(product)
return Response(serializer.data)
serializer.is_valid(raise_exception=True)
serializer.save()
Sample JSON:
{
"price": 8500,
"quantity": 50
if request.method == 'GET':
serializer = ProductSerializer(product)
return Response(serializer.data)
serializer.save()
return Response(serializer.data)
product.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
URL: http://127.0.0.1:8000/products/3/
Final URLs:
Introduction:
# views.py
class ProductListCreateView(APIView):
return Response(serializer.data)
serializer = ProductSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
class ProductDetailView(APIView):
product = self.get_object(id)
serializer = ProductSerializer(product)
return Response(serializer.data)
product = self.get_object(id)
serializer.save()
return Response(serializer.data)
product = self.get_object(id)
product.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
URL Patterns
# urls.py
urlpatterns = [
● The .as_view() method converts the class into a view function that Django can call
when a request comes.
2. Using GenericAPIView + Mixins (Mid-level abstraction)
● This approach allows combining modular mixins with reusable generic base classes.
Mixin Classes:
● UpdateModelMixin (PUT/PATCH)
● DestroyModelMixin (DELETE)
queryset = Product.objects.all()
serializer_class = ProductSerializer
queryset = Product.objects.all()
serializer_class = ProductSerializer
lookup_field = 'id'
● Most of the time we don't use these mixin classes directly, instead we use some
concrete classes which combine one or more mixin, we call these classes as Generic
Views.
● For Example:
Advantage:
No need to define get, post, put, delete methods explicitly unless you want to customize
them.
Example:
class ProductListCreateView(ListCreateAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
class ProductDetailView(RetrieveUpdateDestroyAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
lookup_field = 'id'
class ProductDetailView(RetrieveUpdateDestroyAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
lookup_field = 'id'
product.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Example:
class ProductViewSet(ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
product.review_set.count() > 0:
product.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Routers:
● When using ViewSets, you don't define URLs manually.
● Instead, you use a Router to automatically generate URLs.
urls.py:
router = SimpleRouter()
router.register('products', ProductViewSet)
urlpatterns = [
path('', include(router.urls)),
]
Calling backend API from python application:
● In Python, to interact with Backend APIs (like REST APIs), we commonly use the
requests library.
What is an API?
● A Backend API typically handles operations like Create, Read, Update, and Delete
(CRUD).
● We can call and test APIs using Postman or any kind of REST client tools for testing
purposes.
● However, if we want to use the API-related data inside our Python application — for
example, to display it, process it, or store it — we need to use the requests library to
call APIs programmatically.
● This allows us to fetch, manage, and incorporate the API data directly into our
Python code and build powerful applications.
● https://jsonplaceholder.typicode.com
● JSONPlaceholder is a free online REST API for testing and prototyping.
● JSONPlaceholder is a safe playground to practice APIs.
● /posts
● /users
● /comments
APITestApp.py
import requests
BASE_URL = "https://jsonplaceholder.typicode.com/posts"
def get_posts():
response = requests.get(BASE_URL)
posts = response.json()
print("Fetched Posts:")
print(post)
else:
get_posts()
● To retrieve the response body in its raw binary format, we use response.content,
whereas to obtain the response body as a parsed JSON object (converted into a
Python dictionary or list), we use the response.json() method.
Assignment: Write a function to get a specific post based on the post id.
import requests
BASE_URL = "https://jsonplaceholder.typicode.com/posts"
def get_single_post(post_id):
response = requests.get(f"{BASE_URL}/{post_id}")
if response.status_code == 200:
else:
print(
get_single_post(2)
import requests
BASE_URL = "https://jsonplaceholder.typicode.com/posts"
def create_post():
new_post = {
if response.status_code == 201:
created_post = response.json()
else:
print(
f"Failed to create a new post Status code:
{response.status_code}")
create_post()
import requests
BASE_URL = "https://jsonplaceholder.typicode.com/posts"
def update_post(post_id):
updated_post = {
"id": post_id,
"userId": 1
if response.status_code == 200:
updated = response.json()
print("Updated Post:")
print(updated)
else:
import requests
BASE_URL = "https://jsonplaceholder.typicode.com/posts"
def delete_post(post_id):
response = requests.delete(f"{BASE_URL}/{post_id}")
if response.status_code == 200:
else:
delete_post(1)
cd DjangoPostApiProject
● Create a new app called : PostApp
import requests
API_URL = "https://jsonplaceholder.typicode.com/posts"
def list_posts_view(request):
response = requests.get(API_URL)
posts = response.json()
● Define the url pattern for the above view function inside the
PostApp/urls.py file.
urlpatterns = [
]
● Register the above urls.py file inside the project level urls.py file
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('PostApp.urls'))
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body bgcolor="cyan">
<ol>
<strong>Title: </strong>
{{post.title}}
<br>
<strong>Body: </strong>
{{post.body}}
<br><br>
</li>
{% endfor %}
</ol>
</body>
</html>
http://127.0.0.1:8000/
http://127.0.0.1:5000
import requests
# API URLs
register_url = "http://localhost:5000/registerapi"
login_url = "http://localhost:5000/loginapi"
products_url = "http://localhost:5000/products"
register_data = {
"name": name,
"email": email,
"mobile": mobile,
"password": password,
if register_response.status_code == 201:
print("User registered successfully.")
return True
else:
return False
login_data = {
"username": username,
"password": password
if login_response.status_code == 200:
jwt_token = login_response.json()['access_token']
return jwt_token
else:
def get_product_list(jwt_token):
headers = {
"Content-Type": "application/json"
if product_response.status_code == 200:
response_data = product_response.json()
print("Product List:")
print(
else:
print(f"Failed to fetch products: {product_response.status_code}")
def main():
if jwt_token:
get_product_list(jwt_token)
if __name__ == "__main__":
main()
Objective:
● Create a Django project that interacts with a Flask-based REST API to perform the
following operations:
○ Allow the user to register by sending data to the Flask API (POST
/registerapi).
○ Allow the user to log in by sending credentials to the Flask API (POST
/loginapi) and receive a JWT token.
○ Allow users to add a new product by sending product data to the Flask API
(POST /products).
Steps:
○ Design the registration and login forms in Django using HTML templates.
○ In the register view, send the registration data as a POST request to the Flask
API (/registerapi).
○ In the login view, send the login credentials to the Flask API (/loginapi) and
retrieve the JWT token.
○ Send the product data as a POST request to the Flask API (/products) to add
the product to the Flask app.
○ Send a GET request to the Flask API (/products) to retrieve the list of
products.
○ Secure all product-related actions (adding a new product, fetching product list)
by passing the JWT token in the request headers as a Bearer token.
Deliverables:
● Django project with integrated forms for user registration, login, and product
management.
● Ability to perform CRUD operations for products by communicating with the Flask API.
● Clear HTML templates for displaying user registration/login forms and product data.
Expected Outcome:
● The project will display the user registration and login forms in Django, interact with
the Flask API to perform registration and login, and securely add or view products via
API calls.