steps Flashcards
To create a new project named “djangoappname”
django-admin startproject djangoappname .
Don’t forget to add the . dot at the end of this command. This will actually generate all the files in the current directory
Before starting the server, let’s run the migrations. after creating the CoreRoot project, execute
python manage.py migrate
start the Django server:
python manage.py runserver
set up a postgres database:
> sudo su postgres
> psql
> CREATE DATABASE coredb;
> CREATE USER coreuser WITH PASSWORD ‘wCh29&HE&T83’;
> GRANT ALL PRIVILEGES ON DATABASE coredb TO coreuser;
> ALTER USER core CREATEDB;
how to configure the project to connect to a database.
In the settings.py file:
https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
‘default’: {
‘ENGINE’: ‘django.db.backends.postgresql_psycopg2’,
‘NAME’: ‘coredb’,
‘USER’: ‘coreuser’,
‘PASSWORD’: ‘wCh29&HE&T83’,
‘HOST’: ‘localhost’,
‘PORT’: ‘5342’,
}
}
run the migrations
python manage.py migrate
to see django logo/running
go to
https://www.educative.io/courses/full-stack-django-and-react/configuring-the-database
second black screen (after db conf and connection)
execute first steps (migration/runserver)
When working with Django, To have a clean and well-organized project,
we’ll have to create many apps to handle different parts of a project.
For example, we can have a different application for authentication, and another for payments or articles.
- django-api
- core
- apps.py
…
- apps.py
- CoreRoot
- settings.py
…
…
- settings.py
- core
At the root of the project, inside “djangoappname” folder, run the following command to Create the core application
- RUN django-admin startapp core
- remove all the files in this app except for the apps.py file and the __init__.py file.
Inside apps.py, add the last line (“label” class property) of the following code:
from django.apps import AppConfig
class CoreConfig(AppConfig):
default_auto_field = ‘django.db.models.BigAutoField’
name = ‘core’
label = ‘core’
Register the app in the INSTALLED_APPS list inside the settings.py file of the project:
INSTALLED_APPS = [
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
'core' ]
creating a User application (submodule)
inside”djangoappname” folder
#inside”core”AppFolder
django-admin startapp user
User table structure
public_id: string
last_name: string
first_name: string
username: string
bio: string
avatar: image
email: string
is_active
is_superuser
created: datetime
updated: datetime
in Django ORM:
import uuid
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.http import Http404
class User(AbstractBaseUser, PermissionsMixin):
public_id = models.UUIDField(db_index=True, unique=True, default=uuid.uuid4, editable=False)
username = models.CharField(db_index=True, max_length=255, unique=True)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
email = models.EmailField(db_index=True, unique=True) is_active = models.BooleanField(default=True) is_superuser = models.BooleanField(default=False) created = models.DateTimeField(auto_now=True) updated = models.DateTimeField(auto_now_add=True) USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['username'] objects = UserManager() def \_\_str\_\_(self): return f"{self.email}" @property def name(self): return f"{self.first_name} {self.last_name}"
The models module from Django provides
- field utilities
- model property
- __str__ method to return a string that can help us quickly identify a User object.
Let’s write UserManager so we can have methods to create a user and a superuser:
class UserManager(BaseUserManager):
def get_object_by_public_id(self, public_id):
try: instance = self.get(public_id=public_id) return instance except (ObjectDoesNotExist, ValueError, TypeError): return Http404 def create_user(self, username, email, password=None, **kwargs): """Create and return a `User` with an email, phone number, username and password.""" if username is None: raise TypeError('Users must have a username.') if email is None: raise TypeError('Users must have an email.') if password is None: raise TypeError('User must have an email.') user = self.model(username=username, email=self.normalize_email(email), **kwargs) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, username, email, password, **kwargs): """ Create and return a `User` with superuser (admin) permissions. """ if password is None: raise TypeError('Superusers must have a password.') if email is None: raise TypeError('Superusers must have an email.') if username is None: raise TypeError('Superusers must have an username.') user = self.create_user(username, email, password, **kwargs) user.is_superuser = True user.is_staff = True user.save(using=self._db) return user
Before running the migrations (everytime a new app was added to the project)
we need to register the user application in INSTALLED_APPS in CoreRoot/settings.py.
goto /projectFolder/CoreRoot/settings.py
> add the new app: core.newapp
inside the new app folder in Apps.py:
###
from django.apps import AppConfig
class UserConfig(AppConfig):
default_auto_field = ‘django.db.models.BigAutoField’
name = ‘core.user’
label = ‘core_user’
###
Where name and name show how apps are nested
tell Django to use this User model for the authentication user model. In the settings.py file, add the following line:
goto /projectFolder/CoreRoot/settings.py
> at the end of the file add: AUTH_USER_MODEL = ‘core_user.User’
after add a new model to the project, create the first migration for the new app (the app that contains the Models.py file,
python manage.py makemigrations
Testing the model
use the Django shell to play with the newly created model a little bit
Testing the model with Shell
the data needed to create a user
python manage.py shell
from core.user.models import User
data_user = {
“email”: “testuser@yopmail.com”,
“username”: “john-doe”,
“password”: “12345”,
“first_name”: “John”,
“last_name”: “Doe”
}
user = User.objects.create_user(**data_user)
user.name
user.email
user.password
install the djangorestframework package
add rest_framework to the INSTALLED_APPS setting:
INSTALLED_APPS = [
…
‘rest_framework’,
…
]
Writing UserSerializer (interface between viewset and model)
Playground at https://www.educative.io/courses/full-stack-django-and-react/writing-userserializer-and-userviewset:
In the core/user directory,
create a file called serializers.py:
from rest_framework import serializers
from core.user.models import User
class UserSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(source=’public_id’, read_only=True, format=’hex’)
created = serializers.DateTimeField(read_only=True)
updated = serializers.DateTimeField(read_only=True)
class Meta: model = User fields = ['id', 'username', 'first_name', 'last_name', 'bio', 'avatar', 'email', 'is_active', 'created', 'updated'] read_only_field = ['is_active']
write the viewset.
Inside the user directory,
rename the view file viewsets.py
from rest_framework.permissions import AllowAny
from rest_framework import viewsets
from core.user.serializers import UserSerializer
from core.user.models import User
class UserViewSet(viewsets.ModelViewSet):
http_method_names = (‘patch’, ‘get’)
permission_classes = (IsAuthenticated,) #AllowAny
serializer_class = UserSerializer
// used by the viewset to get a list of all the users. // called when /user/ is hit with a GET request def get_queryset(self): if self.request.user.is_superuser: return User.objects.all() return User.objects.exclude(is_superuser=True) // called when a GET or PUT request is made on the /user/id/ def get_object(self): obj = User.objects.get_object_by_public_id(self.kwargs['pk']) self.check_object_permissions(self.request, obj) return obj
a viewset to which we will be adding a router
Playground at https://www.educative.io/courses/full-stack-django-and-react/adding-a-router:
At the root of the apps project (core),
create a file named routers.py
from rest_framework import routers
from core.user.viewsets import UserViewSet
router = routers.SimpleRouter()
router.register(r’user’, UserViewSet, basename=’user’)
urlpatterns = [
*router.urls,
]
make sure that we have the server running:
Playground at https://www.educative.io/courses/full-stack-django-and-react/adding-a-router:
python manage.py runserwever
(Click the “Run” button to start Insomnia.)
Doing requests with Insomnia
Playground at https://www.educative.io/courses/full-stack-django-and-react/adding-a-router:
make a GET request to http://0.0.0.0:8000/api/user/
make a GET request to retrieve the first user using this URL: /api/user/<id>/</id>
make a PATCH request to update a field in the first user using this URL: /api/user/<id>/
> add a JSON body, setting the last_name value for this user to "Cat"
{"last_name":"Cat}</id>
test authentication issue
Playground at https://www.educative.io/courses/full-stack-django-and-react/adding-a-router:
permission_classes = (IsAuthenticated,) #AllowAny
installed the djangorestframework-simplejwt authentication plugin
for DRF to handle JWT authentication
it facilitates the creation and management of access tokens, as well as refreshing tokens.
A) We need to register the app in INSTALLED_APPS: ‘rest_framework_simplejwt’
B) specify DEFAULT_AUTHENTICATION_CLASSES in the REST_FRAMEWORK dictionary:
REST_FRAMEWORK = {
‘DEFAULT_AUTHENTICATION_CLASSES’: (
‘rest_framework_simplejwt.authentication.JWTAuthentication’,
),
create a new application called auth in the core application:
cd core
django-admin startapp auth
It’ll contain all the logic concerning logging in, registration, logging out, and a lot more.
rewrite the apps.py file and register the application’s name in the INSTALLED_APPS settings:
Playground at https://www.educative.io/courses/full-stack-django-and-react/writing-the-user-registration-feature:
from django.apps import AppConfig
class AuthConfig(AppConfig):
default_auto_field = ‘django.db.models.BigAutoField’
name = ‘core.auth’
label = ‘core_auth’
INSTALLED_APPS:
…
‘core’,
‘core.user’,
‘core.auth’
]
…
For registration and login, we’ll have many serializers and viewsets
Playground at https://www.educative.io/courses/full-stack-django-and-react/writing-the-user-registration-feature:
remove the admin.py and models.py files from the auth directory
Create a Python package called serializers and another one called viewsets
*** Make sure each directory have an __init__.py file.
auth app tree should look like:
- apps.py
- __init__.py
- migrations
- __init__.py
- serializers
- __init__.py
- tests.py
- viewsets
- __init__.py
- views.py
> Inside the serializers directory, create a file called register.py:
from rest_framework import serializers
from core.user.serializers import UserSerializer
from core.user.models import User
class RegisterSerializer(UserSerializer):
password = serializers.CharField(max_length=128, min_length=8, write_only=True, required=True)
class Meta:
model = User
fields = [‘id’, ‘bio’, ‘avatar’, ‘email’, ‘username’, ‘first_name’, ‘last_name’, ‘password’]
def create(self, validated_data): # Use the `create_user` method we wrote earlier for the UserManager to create a new user. return User.objects.create_user(**validated_data)
> > Inside the viewsets directory, create a file called register.py:
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from rest_framework.permissions import AllowAny
from rest_framework import status
from rest_framework_simplejwt.tokens import RefreshToken
from core.auth.serializers import RegisterSerializer
class RegisterViewSet(ViewSet):
serializer_class = RegisterSerializer
permission_classes = (AllowAny,)
http_method_names = [‘post’]
# rewriting the create method to add access and refresh tokens in the body of the response.
def create(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
refresh = RefreshToken.for_user(user)
res = {
“refresh”: str(refresh),
“access”: str(refresh.access_token),
}
return Response({
“user”: serializer.data,
“refresh”: res[“refresh”],
“token”: res[“access”]
}, status=status.HTTP_201_CREATED)
> register the viewset in the routers.py file:
from core.auth.viewsets import RegisterViewSet
router.register(r’auth/register’, RegisterViewSet, basename=’auth-register’)
Click the “Run” button to launch Insomnia and send requests.
Playground at https://www.educative.io/courses/full-stack-django-and-react/writing-the-user-registration-feature:
The URL will be as follows: http://0.0.0.0:8000/api/auth/register/
As a body for the request, we can pass the following:
{
“username”: “mouse21”,
“first_name”: “Mickey”,
“last_name”: “Mouse”,
“password”: “12345678”,
“email”: “mouse@yopmail.com”
}
Inside the core/auth/serializers directory, create a new file called login.py
playground at https://www.educative.io/courses/full-stack-django-and-react/adding-the-login-feature:
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.settings import api_settings
from django.contrib.auth.models import update_last_login
from core.user.serializers import UserSerializer
class LoginSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
data = super().validate(attrs)
refresh = self.get_token(self.user)
data[‘user’] = UserSerializer(self.user).data
data[‘refresh’] = str(refresh)
data[‘access’] = str(refresh.access_token)
if api_settings.UPDATE_LAST_LOGIN:
update_last_login(None, self.user)
return data
import LoginSerializer (login.py) to the (auth/serializers/)_init__.py file:
playground at https://www.educative.io/courses/full-stack-django-and-react/adding-the-login-feature:
from .login import LoginSerializer
Adding the (auth/viewsets) LoginViewset (login.py)
playground at https://www.educative.io/courses/full-stack-django-and-react/adding-the-login-feature:
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from rest_framework.permissions import AllowAny
from rest_framework import status
from rest_framework_simplejwt.exceptions import TokenError, InvalidToken
from core.auth.serializers import LoginSerializer
class LoginViewSet(ViewSet):
serializer_class = LoginSerializer
permission_classes = (AllowAny,)
http_method_names = [‘post’]
def create(self, request, *args, **kwargs): serializer = self.serializer_class(data=request.data) try: serializer.is_valid(raise_exception=True) except TokenError as e: raise InvalidToken(e.args[0]) return Response(serializer.validated_data, status=status.HTTP_200_OK)
Add the viewset to the __init__.py file of the auth/viewsets directory:
playground at https://www.educative.io/courses/full-stack-django-and-react/adding-the-login-feature:
from .login import LoginViewSet
import LoginViewSet and register it in the routers.py
from core.auth.viewsets import LoginViewSet
router.register(r’auth/login’, LoginViewSet, basename=’auth-login’)
try a request with Insomnia at /auth/login
playground at https://www.educative.io/courses/full-stack-django-and-react/adding-the-login-feature:
Here’s the body of the request we’ll use:
{
“password”: “12345678”,
“email”: “mouse2@yopmail.com”
}
refresh logic feature
playground at https://www.educative.io/courses/full-stack-django-and-react/refresh-logic:
a) In auth/viewsets, add a new file called refresh.py:
from rest_framework.response import Response
from rest_framework_simplejwt.views import TokenRefreshView
from rest_framework.permissions import AllowAny
from rest_framework import status
from rest_framework import viewsets
from rest_framework_simplejwt.exceptions import TokenError, InvalidToken
class RefreshViewSet(viewsets.ViewSet, TokenRefreshView):
permission_classes = (AllowAny,)
http_method_names = [‘post’]
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
except TokenError as e:
raise InvalidToken(e.args[0])
return Response(serializer.validated_data, status=status.HTTP_200_OK)
b) add the class in the __init__.py file:
from .refresh import RefreshViewSet
c) register it in the routers.py file:
from core.auth.viewsets import RefreshViewSet
router.register(r’auth/refresh’, RefreshViewSet, basename=’auth-refresh’)
d)
Send a login POST request to the /auth/login endpoint with the following credentials:
{
“password”: “12345678”,
“email”:”mouse2@yopmail.com”
}
e) test the new endpoint at /auth/refresh/ to get a new token:
POST the request with the “refresh” token in the body of the request