# CREATING VIRTUAL ENVIRONMENT
# windows 👇
python -m venv env
# linux / Mac OS 👇
vitualenv env
# ACTIVATING ENVIRONMENT
# windows 👇
source env/Scripts/activate
# linux / Mac OS 👇
source env/bin/activate
# PACKAGE INSTALLATION
# if pip does not work try pip3 in linux/Mac OS
pip install djangorestframework
pip freeze > requirements.txt
django-admin startproject main .
# alternatively python -m pip install django
pip install python-decouple
django-admin --version
# 💨 If you already have a requirement.txt file, you can install the packages in the file
# 💨 by entering the following commands respectively in the terminal 👇
1-python -m venv env
2-source env/Scripts/activate
3-pip install -r requirements.txt 🚀
4-python.exe -m pip install --upgrade pip
5-python manage.py migrate
6-python manage.py createsuperuser
7-python manage.py runserver
✔ Add a ".gitignore" file at same level as env folder, and check that it includes ".env" and /env lines.
🔹 Do that before adding your files to staging area, else you will need extra work to unstage files to be able to ignore them.
🔹 On this page you can create "gitignore files" for your projects.
💻 To use python decouple in this project, first install it 👇
pip install python-decouple
💻 Go to terminal to update "requirements.txt" 👇
pip freeze > requirements.txt
✔ Create a new file and name as ".env" at same level as env folder
✔ Copy your SECRET_KEY from settings.py into this .env file. Don't forget to remove quotation marks and blanks from SECRET_KEY
SECRET_KEY=-)=b-%-w+0_^slb(exmy*mfiaj&wz6_fb4m&s=az-zs!1^ui7j
✔ Go to "settings.py", make amendments below 👇
from decouple import config
SECRET_KEY = config('SECRET_KEY')
💻 Go to terminal 👇
python manage.py makemigrations
python manage.py migrate
pip install djangorestframework
✔ Go to "settings.py" and add 'rest_framework' app to INSTALLED_APPS
💻 To get Python working with Postgres, you will need to install the “psycopg2” module👇
pip install psycopg2
💻 Go to terminal to update requirements.txt 👇
pip freeze > requirements.txt
✔ Go to settings.py and add '' app to INSTALLED_APPS
🔹 Explain a sample API reference documentation
🔹 Swagger is an open source project launched by a startup in 2010. The goal is to implement a framework that will allow developers to document and design APIs, while maintaining synchronization with the code.
🔹 Developing an API requires orderly and understandable documentation.
🔹 To document and design APIs with Django rest framework we will use drf-yasg which generate real Swagger/Open-API 2.0 specifications from a Django Rest Framework API.
📜 You can find the documentation here.
pip install drf-yasg
💻 Go to terminal to update requirements.txt 👇
pip freeze > requirements.txt
✔ Go to "settings.py" and add 'drf_yasg' app to INSTALLED_APPS
✔ Here is the updated "urls.py" file for swagger. In swagger documentation, those patterns are not up-to-date 👇
from django.contrib import admin
from django.urls import path
# Three modules for swagger:
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
schema_view = get_schema_view(
openapi.Info(
title="Flight Reservation API",
default_version="v1",
description="Flight Reservation API project provides flight and reservation info",
terms_of_service="#",
contact=openapi.Contact(
email="rafe@clarusway.com"), # Change e-mail on this line!
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=[permissions.AllowAny],
)
urlpatterns = [
path("admin/", admin.site.urls),
# Url paths for swagger:
path("swagger(<format>\.json|\.yaml)",
schema_view.without_ui(cache_timeout=0), name="schema-json"),
path("swagger/", schema_view.with_ui("swagger", cache_timeout=0),
name="schema-swagger-ui"),
path("redoc/", schema_view.with_ui("redoc",
cache_timeout=0), name="schemaredoc"),
]
python manage.py migrate
python manage.py runserver
🔴 If you have this problem 👉 ( return Database.Cursor.execute(self, query, params) sqlite3.OperationalError:) when you create "superuser" you should write this command 👇
python manage.py migrate --run-syncdb
✔ After running the server, go to swagger page and redoc page of your project!
🔹 The Django Debug Toolbar is a configurable set of panels that display various debug information about the current request/response and when clicked, display more details about the panel’s content.
📜 See the Django Debug Toolbar documentation page.
pip install django-debug-toolbar
💻 Go to terminal to update "requirements.txt" 👇
pip freeze > requirements.txt
✔ Go to "settings.py" and add 'debug_toolbar' app to INSTALLED_APPS
from django.urls import include
urlpatterns = [
# ...
path('__debug__/', include('debug_toolbar.urls')),
]
MIDDLEWARE = [
"debug_toolbar.middleware.DebugToolbarMiddleware",
# ...
]
INTERNAL_IPS = [
"127.0.0.1",
]
💻 Go to terminal 👇
python manage.py startapp blog
✔ Go to "settings.py" and add 'blog' app to "INSTALLED_APPS"
💻 INSTALL DJ-REST-AUTH
pip install dj-rest-auth
💻 Go to terminal to update "requirements.txt" 👇
pip freeze > requirements.txt
'rest_framework',
'rest_framework.authtoken',
'dj_rest_auth',
path('auth/', include('user.urls'))
✔ Create "api" folder under "blog" App. 👉 Then create "urls.py", "serializers.py" and "views.py" files under "api" folder 👇
from django.urls import path, include
urlpatterns = [
path('auth/', include('dj_rest_auth.urls')),
]
python manage.py migrate
from django.db import models
from django.conf import settings
User = settings.AUTH_USER_MODEL
class Category(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class BlogPost(models.Model):
STATUS = (
("d", "DRAFT"),
("p", "PUBLISHED"),
)
title = models.CharField(max_length=100)
author = models.ForeignKey(User, related_name="post_user", on_delete=models.CASCADE)
category = models.ForeignKey(Category, related_name="post_category", on_delete=models.CASCADE)
content = models.TextField()
image = models.URLField(max_length=200, blank=True, default="https://robohash.org/9c681a48b0ef374675df3ca8d6b014a5?set=set4&bgset=&size=400x400")
published_date = models.DateTimeField(auto_now_add=True, blank=True)
last_updated_date = models.DateTimeField(auto_now=False, blank=True)
status = models.CharField(max_length=50, choises=STATUS)
#! We use slug for the fields we want to appear instead of ID 👇
slug = models.SlugField()
def __str__(self):
return self.title
class Like(models.Model):
user = models.ForeignKey(User, related_name="like_user", on_delete=models.CASCADE)
post = models.ForeignKey(BlogPost, related_name="like_post", on_delete=models.CASCADE)
def __str__(self):
return self.user.username
class Comment(models.Model):
content = models.TextField()
time_stamp = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, related_name="comment_user", on_delete=models.CASCADE)
post = models.ForeignKey(BlogPost, related_name="comment_post", on_delete=models.CASCADE)
def __str__(self):
return self.user.username
class Post_view(models.Model):
user = models.ForeignKey(User, related_name="post_viewed_user", on_delete=models.CASCADE)
post = models.ForeignKey(BlogPost, related_name="viewed_post", on_delete=models.CASCADE)
viewed_date_time = models.DateTimeField(auto_now_add=True, blank=True)
python manage.py migrate
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
image = models.URLField(max_length=200, blank=True)
bio = models.TextField(blank=True)
from django.contrib import admin
from .models import User
admin.site.register(User)
AUTH_USER_MODEL = 'user.User'
from rest_framework import serializers, validators
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from dj_rest_auth.serializers import TokenSerializer
User = get_user_model()
class RegisterSerializer(serializers.ModelSerializer):
email = serializers.EmailField(
required=True,
validators=[validators.UniqueValidator(queryset=User.objects.all())]
)
password = serializers.CharField(
write_only=True,
required=True,
validators=[validate_password],
style={"input_type": "password"}
)
password1 = serializers.CharField(
write_only=True,
required=True,
validators=[validate_password],
style={"input_type": "password"}
)
class Meta:
model = User
fields = (
'username',
'email',
'first_name',
'last_name',
'password',
'password1',
'image',
'bio'
)
def validate(self, data):
if data['password'] != data['password1']:
raise serializers.ValidationError(
{"password": "Password didn't match..... "}
)
return data
def create(self, validated_data):
password = validated_data.pop("password")
validated_data.pop('password1')
user = User.objects.create(**validated_data)
user.set_password(password)
user.save()
return user
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
'username',
'email'
)
class CustomTokenSerializer(TokenSerializer):
user = UserSerializer(read_only=True)
class Meta(TokenSerializer.Meta):
fields = (
'key',
'user'
)
🔴 SIGNALS 👇
🔹 Django include a “signal dispatcher” which helps decoupled applications get notified when actions occur elsewhere in the framework.
🔹 In nutshell, signals allow certain senders to notify a set of receivers that some action has taken place.
🔹 They’re especially useful when many pieces of code may be interested in the same events.
🔹 receiver: The callback function which will be connected to this signal. See Receiver functions for more information.
🔹 sender: Specifies a particular sender to receive signals from. See Connecting to signals sent by specific senders for more information.
🔹 weak: Django stores signal handlers as weak references by default. Thus, if your receiver is a local function, it may be garbage collected. To prevent this, pass weak=False when you call the signal’s connect() method.
🔹 dispatch_uid: A unique identifier for a signal receiver in cases where duplicate signals may be sent. See Preventing duplicate signals for more information.
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from django.contrib.auth import get_user_model
User = get_user_model()
@receiver(post_save, sender=User)
def create_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
def ready(self):
import user.signals
from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
from .serializers import RegisterSerializer
from django.contrib.auth import get_user_model
User = get_user_model()
class RegisterView(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = RegisterSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
data = serializer.data
if Token.objects.filter(user=user).exists():
token = Token.objects.get(user=user)
data['token'] = token.key
else:
data['error'] = 'User dont have token. Please login'
headers = self.get_success_headers(serializer.data)
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
from django.urls import path, include
from .views import RegisterView
urlpatterns = [
path('', include('dj_rest_auth.urls')),
path('register/', RegisterView.as_view())
]
from rest_framework import serializers, validators
from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password
class RegisterSerializer(serializers.ModelSerializer):
email = serializers.EmailField(
required = True,
validators=[validators.UniqueValidator(queryset=User.objects.all())]
)
password = serializers.CharField(
required = True,
write_only = True,
validators = [validate_password],
style = {"input_type":"password"}
)
password1 = serializers.CharField(
required = True,
write_only = True,
validators = [validate_password],
style = {"input_type":"password"}
)
class Meta:
model = User
fields = (
"username",
"email",
"first_name",
"last_name",
"password",
"password1",
)
def validate(self, data):
if data["password"] != data["password1"]:
raise serializers.ValidationError(
{"password": "Password must be same with above !..."}
)
return data
def create(self, validated_data):
password = validated_data.pop("password")
validated_data.pop("password1")
user = User.objects.create(**validated_data)
user.set_password(password)
user.save()
return user
from rest_framework import generics
from django.contrib.auth.models import User
from .serializers import RegisterSerializer
class RegisterView(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = RegisterSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
data = serializer.data
if Token.objects.filter(user=user):
token = Token.objects.get(user=user)
data['token'] = token.key
else:
data['error'] = 'User does not have token . Try again ...'
headers = self.get_success_headers(serializer.data)
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
from users.api.views import RegisterView
path('register/', RegisterView.as_view()),
class UpdateUserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(
required=True,
validators=[validators.UniqueValidator(queryset=User.objects.all())]
)
class Meta:
model = User
fields = (
"username",
"id",
"email",
"first_name",
"last_name",
"profile_pic",
"biography",
)
class UpdateUserView(generics.RetrieveUpdateAPIView):
#! We used RetrieveUpdateAPIView so that the user can only update. 👆
queryset = User.objects.all()
serializer_class = UpdateUserSerializer
path('update-profile/<int:pk>', UpdateUserView.as_view()),
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.db import models
from django.conf import settings
from django.template.defaultfilters import slugify
from blog.api.utils import get_random_code
User = settings.AUTH_USER_MODEL
class Category(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class BlogPost(models.Model):
STATUS = (
("d", "DRAFT"),
("p", "PUBLISHED"),
)
title = models.CharField(max_length=100)
author = models.ForeignKey(
User, related_name="post_user", on_delete=models.PROTECT, default='Anonymous User')
category = models.ForeignKey(
Category, related_name="post_category", on_delete=models.CASCADE)
content = models.TextField()
# image = models.ImageField(upload_to=None, height_field=None, width_field=None, max_length=None)
image = models.URLField(max_length=200, blank=True,
default="https://gravatar.com/avatar/2074b7945e3c6c493b0b2b94b24c35c2?s=400&d=robohash&r=x")
published_date = models.DateTimeField(auto_now_add=True, blank=True)
last_updated_date = models.DateTimeField(auto_now=True, blank=True)
status = models.CharField(max_length=2, choices=STATUS)
slug = models.SlugField(blank=True, null=True)
def __str__(self):
return self.title
class Like(models.Model):
user = models.ForeignKey(
User, related_name="like_user", on_delete=models.PROTECT)
post = models.ForeignKey(
BlogPost, related_name="like_post", on_delete=models.CASCADE)
def __str__(self):
return self.user
class Comment(models.Model):
content = models.TextField()
time_stamp = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, related_name="comment_user",
on_delete=models.PROTECT, default='Anonymous User')
post = models.ForeignKey(
BlogPost, related_name="comment_post", on_delete=models.CASCADE)
def __str__(self):
return self.user
class Post_view(models.Model):
user = models.ForeignKey(
User, related_name="post_viewed_user", on_delete=models.PROTECT)
post = models.ForeignKey(
BlogPost, related_name="viewed_post", on_delete=models.CASCADE)
viewed_date_time = models.DateTimeField(auto_now_add=True, blank=True)
from django.contrib import admin
from blog.models import BlogPost, Category, Comment, Like, Post_view
admin.site.register(Category)
admin.site.register(Like)
admin.site.register(BlogPost)
admin.site.register(Comment)
admin.site.register(Post_view)
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.template.defaultfilters import slugify
from blog.models import BlogPost
from .utils import get_random_code
@receiver(pre_save, sender=BlogPost)
def pre_save_create_slug(sender, instance,**kwargs):
if not instance.slug:
instance.slug = slugify(instance.title + " " + get_random_code())
import uuid
#! 👆 user uniqe id => Slug field must be uniqe
def get_random_code():
code = str(uuid.uuid4())[:11].replace("-","")
return code
from django.apps import AppConfig
class BlogConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "blog"
def ready(self):
import blog.api.signals
from rest_framework import serializers
from blog.models import BlogPost, Category, Comment, Like, Post_view
from users.api.serializers import UserSerializer
from django.contrib.auth import get_user_model
# User = settings.AUTH_USER_MODEL
User = get_user_model()
# class AllUserSerializer(serializers.ModelSerializer):
# class Meta:
# model = User
# fields = (
# "username",
# "first_name",
# "last_name",
# "profile_pic",
# "biography"
# )
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = (
'id',
'name'
)
class CommentSerializer(serializers.ModelSerializer):
# user = serializers.StringRelatedField(read_only=True)
# user_id = serializers.IntegerField()
# post = serializers.StringRelatedField()
# post_id = serializers.IntegerField()
# class Meta:
# model = Comment
# fields = "__all__"
# kimin yorum yaptığını belirtmek için ilave edildi
user = serializers.StringRelatedField(read_only=True)
class Meta:
model = Comment
fields = (
"id",
"content",
"time_stamp",
"user",
)
class LikeSerializer(serializers.ModelSerializer):
# like_user = AllUserSerializer(many=True, read_only=True)
user = serializers.StringRelatedField()
user_id = serializers.IntegerField()
class Meta:
model = Like
fields = (
"id",
"user",
"user_id",
"post",
# "like_user"
)
class BlogPostSerializer(serializers.ModelSerializer):
comment_post = CommentSerializer(many=True, read_only=True)
like_post = LikeSerializer(many=True, read_only=True)
category = serializers.StringRelatedField(read_only=True)
category_id = serializers.IntegerField()
author = serializers.StringRelatedField(read_only=True)
author_id = serializers.IntegerField(read_only=True)
like_count = serializers.SerializerMethodField()
comment_count = serializers.SerializerMethodField()
post_view_count = serializers.SerializerMethodField()
class Meta:
model = BlogPost
fields = (
"id",
"title",
"author",
"author_id",
"category_id",
"category",
"content",
"image",
"published_date",
"last_updated_date",
"status",
"slug",
"like_count",
"comment_count",
"post_view_count",
"comment_post",
"like_post",
)
read_only_fields = (
"published_date",
"updated_date",
"slug",
)
def get_like_count(self, obj):
return Like.objects.filter(post=obj.id).count()
def get_comment_count(self, obj):
return Comment.objects.filter(post=obj.id).count()
def get_post_view_count(self, obj):
return Post_view.objects.filter(post=obj.id).count()
from rest_framework import permissions
from rest_framework.exceptions import ValidationError
from rest_framework.generics import get_object_or_404
from rest_framework import generics, status
from blog.api.pagination import CustomLimitOffsetPagination
from blog.api.permissions import IsAdminUserOrReadOnly, IsPostOwnerOrReadOnly
from blog.api.serializers import BlogPostSerializer, CategorySerializer, CommentSerializer,LikeSerializer
from rest_framework.response import Response
from blog.models import BlogPost, Category, Post_view, Comment, Like
class CategoryView(generics.ListCreateAPIView):
queryset = Category.objects.all()
serializer_class = CategorySerializer
permission_classes = [IsAdminUserOrReadOnly]
class BlogPostView(generics.ListCreateAPIView):
queryset = BlogPost.objects.all()
serializer_class = BlogPostSerializer
pagination_class = CustomLimitOffsetPagination
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
class BlogPostDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = BlogPost.objects.all()
serializer_class = BlogPostSerializer
lookup_field = "slug"
permission_classes = [IsPostOwnerOrReadOnly]
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
# Post_view.objects.get_or_create(user=request.user, post=instance)
Post_view.objects.create(user=request.user, post=instance)
return Response(serializer.data)
class CommentView(generics.CreateAPIView):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
print(self.kwargs)
slug = self.kwargs.get('slug')
blog = get_object_or_404(BlogPost, slug=slug)
user = self.request.user
comments = Comment.objects.filter(post=blog, user=user)
if comments.exists():
raise ValidationError(
"You can not add another comment, for this Post !")
serializer.save(post=blog, user=user)
class LikeView(generics.ListCreateAPIView):
queryset = Like.objects.all()
serializer_class = LikeSerializer
def create(self, request, *args, **kwargs):
user = request.data.get('user_id')
post = request.data.get('post')
serializer = self.get_serializer(data=request.data)
exists_like = Like.objects.filter(user_id=user, post=post)
serializer.is_valid(raise_exception=True)
if exists_like:
exists_like.delete()
else:
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
from rest_framework import permissions
class IsPostOwnerOrReadOnly(permissions.BasePermission):
#! If the request.user is the same as the author, it can update/delete. Otherwise can view 👇
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return request.user == obj.author
class IsAdminUserOrReadOnly(permissions.IsAdminUser):
#! "Admin" can do any action. If not it can only view 👇
def has_permission(self, request, view):
is_admin = super().has_permission(request, view)
return request.method in permissions.SAFE_METHODS or is_admin
from rest_framework.pagination import LimitOffsetPagination
#! For 6 posts to appear on each page 👇
class CustomLimitOffsetPagination(LimitOffsetPagination):
default_limit = 6
from .views import (
CategoryView,
BlogPostView,
BlogPostDetailView,
CommentView,
LikeView
)
from django.urls import path
from rest_framework import routers
urlpatterns = [
path("category/", CategoryView.as_view()),
path("posts/", BlogPostView.as_view()),
path("like/", LikeView.as_view()),
path("posts/<str:slug>/", BlogPostDetailView.as_view()),
path("posts/<str:slug>/add_comment/", CommentView.as_view()),
]
📢 Do not forget to check the endpoints you wrote in Postman.
git clone https://github.com/githubUserName/projectName.git
cd projectName
python -m venv env
source env/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
echo SECRET_KEY=write_random_chars_to_here > .env
python manage.py migrate
$ python manage.py createsuperuser # optional
pwd
-
"Add New Web App" with ManualConfigration with Python_LastVersion
-
Set "Source Code" with "Main Path" (example: /home/anyWhereUserName/ProjectName)
-
Set "Working Directory" with "Main Path" (example: /home/anyWhereUserName/ProjectName)
-
Set "VirtualEnv" with "Env Path" (example: /home/anyWhereUserName/ProjectName/env)
import os
import sys
# Set: Project Main Path:
path = '/home/anyWhereUserName/ProjectName'
if path not in sys.path:
sys.path.append(path)
# Set: Where is settings.py:
os.environ['DJANGO_SETTINGS_MODULE'] = 'projectFolderName.settings'
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
if error, checking:
settting.py:
ALLOWED_HOSTS = ['*']
# folder -> static-files-path:
STATIC_URL = 'static/'
# root -> static-files-path:
STATIC_ROOT = BASE_DIR / STATIC_URL
# Alternates:
# if in base folder -> STATIC_ROOT = BASE_DIR / 'static/'
# if in app folder -> STATIC_ROOT = BASE_DIR / 'appFolderName/static/'
urls.py:
from django.conf import settings
from django.conf.urls.static import static
# url -> static-files-path:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
🔑 A Django App that adds Cross-Origin Resource Sharing (CORS) headers to responses.
🔑 This allows in-browser requests to your Django application from other origins.
🔑 Adding CORS headers allows your resources to be accessed on other domains.
🔑 It's important you understand the implications before adding the headers, since you could be unintentionally opening up your site's private data to others.
pip install django-cors-headers
MIDDLEWARE = [
...,
"corsheaders.middleware.CorsMiddleware",
...,
]
CORS_ALLOW_ALL_ORIGINS=True
CORS_ALLOW_METHODS = [
"DELETE",
"GET",
"OPTIONS",
"PATCH",
"POST",
"PUT",
]
python manage.py runserver
yarn start