100 Days of Code - Day 19 - First Backend API project using Django

(set up on mac)

Like always with any new project create a new repository in GitHub

Choose the options:

  • Add read me

  • Add .gitignore (choose python)

Copy URL and clone it on the local machine.

Each project should have it's own virtual environment. This is because technologies like python (plus any other dependancies the project uses) may be updated over time, and we want to preserve those versions created at the time of build.

In the terminal of vs code, or in the same folder as your project in your machine's terminal, type the following command:

pipenv install
pipenv shell

To get the direct location of the project's local virtual environment is being stored.

pipenv --venv

Control + Shift + P in VS Code to get the Python Select Interpreter options, in drop down select 'enter new interpreter.' On a macintosh computer, copy the url, paste as the new path, and add a forward slash to the end + python3. -- /python3

Install Django

pipenv install django

Install mysql client - allows us to connect our django application to our locally running mysql server.

pipenv install mysql-connector-python==8.0.26
pipenv install djangorestframework

At this point, it would be a good idea to make a commit to the repository.

To see the available executables with django type the command below

django-admin

First command with django (with name of project) The space and dot put files generated in this folder open. ## THE SPACE AND DOT AFTER NAME OF PROJECT IS IMPORTANT!

django-admin startproject cars_project .

This generates a folder a files. For now, we only worry about settings.py and urls.py. settings.py - deals with all of our global settings for this django application. Urls.py - define our end points and what functions are executed when those end points are used.

Adding CORS to the backend

pipenv install django-cors-headers

Inside your back-end project settings.py:

i. Add ‘corsheaders’ to INSTALLED_APPS

ii. Add ‘corsheaders.middleware.CorsMiddleware’ to MIDDLEWARE

iii. Add new setting anywhere in settings.py file: CORS_ORIGIN_ALLOW_ALL=True

Connect application to the local mysql database.

In the settings.py, look for the section on DATABASES and change the connection string.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

(set up may be different on a pc)

DATABASES = {
    'default': {
        'ENGINE': 'mysql.connector.django',
        'NAME': 'cars_database',
        'USER': 'root',
        'PASSWORD': 'Apple251#',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'OPTIONS': {
            'autocommit': True
        }
    }
}

Create a new file called local_settings.py. (It is already in the .gitignore).

In the settings.py folder at bottom add the following lines of code:

This will import all from local_settings and if error ignore it (pass)

try:
    from cars_project.local_settings import *
except ImportError:
    pass

Next, cut the 'DATABASES =' and 'SECRET_KEY = ' blocks of code from settings.py and put them in the local_settings.py file.

Next, go into mysql workbench and create a new data base named after current project.

CREATE DATABASE cars_database

Now we run something called MIGRATIONS. Django out of the box comes with predefined tables used for authentication. (migrate and makemigrations are available in django applications)

Notice the manage.py generated in our application. This is a file wrapped around our django admin executable giving us the freedom of not typing 'django-admin' any longer in this project and giving us further actions to commit. (manage.py)

Give the following command in the terminal:

python manage.py migrate

To check out the available commands when you want to build an app type the following command:

python manage.py

Make New App!

Next command: last space in command is name of app (cars in this instance) Notice a new folder is created.

python manage.py startapp cars

(Primary files we will be concerned with is the views.py and modles.py)

Inside of the settings.py file look for the following code and add to it:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

add: the rest frame work and added cars app.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'cars',
]

First table created in models.py file below


class Car(models.Model):
  make = models.CharField(max_length=255)
  model = models.CharField(max_length=255)
  year = models.IntegerField()
  price = models.DecimalField(max_digits=8, decimal_places=2)

Afterwards run one of the the following 2 commands in the terminal to add to the database (second is more specific):


python manage.py makemigrations

python manage.py makemigrations cars

Then run this command to make the migration:

python manage.py migrate

Serializers, writing functions in views.py file, URLs

Serializer helps translate JSON objects so it can be read by python and vise-versa.

  • Create a file in cars app called serializers.py and add the following code:

from rest_framework import serializers
from .models import Car

class CarSerilaizer(serializers.ModelSerializer):
  class Meta: 
    model = Car
    fields = ['id', 'make', 'model', 'year', 'price']

Views/Functions

Next, go to views.py file and delete the render function import at the top thats auto generated.

Add the following in views.py A decorator assigns certain permissions to a function ('get, post, put, delete')

from rest_framework.decorators import api_view
from rest_framework.response import Response


@api_view(['GET'])
def cars_list(request):
  return

URLs

Now let's tie this function to an end point. Create a new file called urls.py in our car app. Path is a function that takes two arguments, the actual endpoint in string format and the function we want to run.


from django.urls import path
from . import views

urlpatterns = [
  path('cars/', views.cars_list)
]

To run the project/end point from the terminal

python manage.py runserver

You can grab the URL shown in the terminal (port 8000) and add /cars to it in the browser.

  • at this point there will be a 404 not found, one more step--register the cars register the cars url file with the project urls file. Line 17 of the cars_project urls.py file add the following:

Before

"""cars_project URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

After

"""cars_project URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/cars/', include('cars.urls')),
]

Then go to Cars folder then urls.py to delete the path '/cars' so the endpoint works well and not a repeated word in the url.

from django.urls import path
from . import views

urlpatterns = [
  path('', views.cars_list)
]

"Djangos browsable API" is the result in the browser at this point.

Admin center app that comes with Django

Type the following command in your directory/terminal

python manage.py createsuperuser

Enter info for the prompts.

Changes should reflect in mysql workbench in the auth_user table.

Now run server

python manage.py runserver

Open link shown in terminal in your browser.

Make sure url is 127.0.0.1:8000/admin/

Log in

In cars app folder > admin.py, import the Car model to access it.

from django.contrib import admin
from .models import Car

# Register your models here.
admin.site.register(Car)

Refresh browser and Cars table should appear. Now we can seed data into the table!

Creating the crud functions in views.py

from django.shortcuts import get_object_or_404
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .serializers import CarSerializer
from .models import Car


@api_view(['GET', 'POST'])
def cars_list(request):

  if request.method == 'GET':
    cars = Car.objects.all()                     
    serializer = CarSerializer(cars, many=True)
    return Response(serializer.data)

  elif request.method == 'POST':   
    serializer = CarSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    serializer.save()
    return Response(serializer.data, status=status.HTTP_201_CREATED)


@api_view(['GET', 'PUT', 'DELETE'])
def cars_detail(request, pk): 
  print(request.data)
  car = get_object_or_404(Car, pk=pk) 
  if request.method == 'GET': 
    serializer = CarSerializer(car)
    return Response(serializer.data) 
  elif request.method == 'PUT':
    serializer = CarSerializer(car, data=request.data)
    serializer.is_valid(raise_exception=True)
    serializer.save()
    print('==========here=========')
    return Response(serializer.data)
  elif request.method == 'DELETE':
      car.delete()
      return Response(status=status.HTTP_204_NO_CONTENT)