Project inspired by this Medium Article
I wanted to gain experience with creating a full-stack, so I made a website that could:
- track the sleep periods of users and visually display the hours using a line chart
- track their dreams and visually display the types of dreams they've had using a pie chart
Basically, I wanted to create a database that I could perform CRUD operations to and have those results be displayed and updated in a front-end User Interface.
I chose to use Django because I had not worked with Backend tools before and already have Python experience. Because this is a simple web app I'm creating, I thought it would be a good fit.
In comparison, Node.js(though more popular) is for more complex client-side web apps and uses JavaScript.
venv
: used to create a separate, isolated instance of the Python runtime for a project, with its own complement of packages
Example: python3 -m venv [name of Virtual Enviorment]
deactivate
: leave virtual environment
In a nutshell, Python virtual environments help decouple and isolate Python installs and associated pip packages. This allows end-users to install and manage their own set of packages that are independent of those provided by the system or used by other projects.
From Towards Science:
I like to consider virtual environments as package bookshelves for each project. If I’m working on a cooking project, I do not need to have a book on surfing. Similarly, if I’m working on a machine learning project, there is no need for me to have a library for the front-end. Having only the packages I need on my “bookshelf” eliminates all chances for me to possibly experience gross global installation and package collision errors and allows me to focus on what really matters — my code.
python3 -m venv [name of Virtual Enviorment]
cd [name of Virtual Enviroment]
source bin/activate //activates enviorment
pip install django
pip install djangorestframework
pip install django-cors-headers
pip install --upgrade djangorestframework-simplejwt
git clone https://github.com/RachaelSMathew/sleepTrack.git
cd backendSleep
cd backend
python manage.py makemigrations //if this asks you about having default values, choose option 1 and default value "null"
python manage.py migrate
python manage.py runserver
In another terminal go to sleepTrack folder
cd frontendSleep
npm install
npm start
Front end view: localhost
Back end view(you can see all the users and DB): localhost
Django has multiple microservices/apps (i.e., smaller features of a larger web application)
Start by creating a microservice: python manage.py startapp [microservice]
I made two microservices, one for tracking sleep and one for dreams.
Django models: an instruction manuel that tells Django how to make a database(i.e., the type of coloumns a DB should have and the coloumn types)
Calling the migrations command will convert Django models into a DB
Migrations folder inside app: will contain the code for migrating Django models into Database
Generate migrations script for your app: python manage.py makemigrations students
Run migration(actually creating the DB): python manage.py migrate
Integration logic for handling the HTTP requests e.g. GET, POST, PUT, DELETE etc.
What is APIView?
Mixins are used to add specific functionality to views and can be used with APIView and GenericAPIView to create more complex views
APIView is the basic class for handling request and response for views and can be used for simple views.
When you make new app/microservice-add to settings.py
Migration creates the models from Django INSTALLED_APPS inside settings.py
- Add to INSTALLED_APPS: 'corsheaders','rest_framework'
- add
[microservice].apps.DailydreamConfig
to INSTALLED_APPS
python manage.py createsuperuser
python manage.py runserver
Models will be registered in this file so that these can be managed from Django admin panel.
from .models import Student
models_list = [Student]
admin.site.register(models_list)
Validates data being inputed into Django databases, makes sure correct all the fields and field types are present.
Serializer.save will convert the data into model object and save this object into database table.
Converts complex data such as querysets and model instances into native Python datatypes that can then be easily rendered into JSON, XML or other content types.
Parsed data converted back into complex types, after first validating Serializer provides a function to validate the data provided by user.
Code: serializer = StudentSerializer(instance=student_to_update, data=request.data, partial=True)
partial=True
to allow the user to pass some or all fields into DB. When user isn't updating all fields.
To call the view(POST, GET etc), we need to map it to a URL
from .views import StudentView
urlpatterns = [
path('students/', StudentView.as_view())
path('students/int:pk/', StudentView.as_view())
]
Add to backend/urls.py:
To make it accessible to Django we will need to include students/urls.py into root URLconf(i.e., the other urls.py that's not in the microservice folder) in urlpatterns
path('', include('students.urls'))
Example of code:
import axios from 'axios';
export function getStudents() { // do the same for delete(i.e., DELETE), update(i.e., PUT), create(i.e., POST)
return axios.get('http://127.0.0.1:8000/students/') .then(response => response.data)
}
How to call from front end React
import { getStudents } from '../services/studentServices';
...
useEffect(() => {
...
getStudents()
.then(data => {
if(mounted) { //mounted is a true boolean after the page has just finished loading
setStudents(data);
}
})
...
}, [students])
Two Methods: Session Auth and Json Web Token
Session Auth(Default with Django): If the front and back end of the site are hosted on one domain
Token-based auth: If the backend is Django and front end is react, then front end is probably on a different domain.
You can still use Session Auth if diff domains, but you need to use X-CSRFToken
in the request header, esp for POST/DELETE where BackEnd data will change!
What is CSRFToken? From here
This type of attack occurs when a malicious website contains a link, a form button, or some JavaScript that is intended to perform some action on your website, using the credentials of a logged-in user who visits the malicious site in their browser. A related type of attack, ‘login CSRF’, where an attacking site tricks a user’s browser into logging into a site with someone else’s credentials, is also covered.
Session auth using this tutorial
- User sends login credentials
- Server validates and makes sessions
- Sessions stored in the backend
- Session id stored in browser as cookie
- Cookie is used every time the user sends requests to the server
pip install Django-cors-headers
pip install djangorestframework
Add to MIDDLEWARE: corsheaders.middleware.CorsMiddleware
CORS_ALLOWED_ORIGINS to true to allow interaction with React
CORS_ALLOWED_CREDENTIALS to true
So, I ended up following this JWT Token tutorial
In terminal: pip install --upgrade djangorestframework-simplejwt
How localStorage is used and how to add headers to axios calls
How curly brackets on post request are structured:
axios.post('http://localhost:8000/logout/',{
refresh_token:localStorage.getItem('refresh_token')
} ,{headers: {'Content-Type': 'application/json'}},
{withCredentials: true});
Errors I ran into | Solutions |
---|---|
app_users.AppUser.groups Reverse accessor ‘Group.user_set’ for ‘app_users.AppUser.group’ clashes with reverse accessor for ‘auth.User.groups’. HINT: Add of change a related_name argument to the definition for ‘app_users.AppUser.groups’ or ‘auth.User.groups’ |
solution |
django.db.migrations.exceptions.InconsistentMigrationHistory:Migration admin.0001_initial is applied before its dependency app_users.0001_ initial on database 'default' |
solution |
authentication.CustomUser: (auth.E003) 'CustomUser .username' must be unique because it is named as the 'USERNAME_FIELD'. |
Add unique=True to the models.py in the username field. Once a value is in a field, the same value can not be entered in any other instance of that model in any manner |
django.db.utils.OperationalError: no such table: authentication_customuser |
solution |
Foreign Key constraint failed |
Need to add a foreign key. Username field exists in all three models, so there's conflict |
Foreign key [not iterable](https://stackoverflow.com/questions/60605587/django-error-argument-of-type-foreignkey-is-not-iterable) |
Instead of Username in all three models, I did Username in User model, UserAddS (i.e., username + 'sss') in Sleep model, UsernameAddS in Dream model |