# Django REST Framework

# REST in Action

REpresentational State Transfer


Uniform Interface and Easy Access


HTTP Methods:

  • POST
  • GET
  • PUT
  • DELETE

These are also known as Http VERBS. The URL and URI are also known as nouns.


CREATE

/employees is called a collection resource, /employees/1 would be an item resource.


GET

GET /employees would get all the employees back, not just the resource.


PUT

To UPDATE we use a PUT Method on the collection resource on the item resource with a unique identification


PUT does not make partial updates, for this we would use PATCH.


PATCH

To DELETE we use the DELETE verb of course.


# Why REST?

Interoperability and Multiple formats


Interoperability

Stateless


The state will be maintained on the client instead on our application.


Scalability


Interoperability

# Why Django REST Framework?

Class Based Views and Function Based Views


Class: CRUD operations

class StudentViewSet(viewsets.ModelViewSet):
    query = Student.objects.all()
    serializer_class = StudentSerializer
1
2
3

Functions: for custom business logic

def student_list(request):

    if request.method == 'GET':
        students = Student.objects.all()
        serializers = StudentSerializers(student, many=True)
        return Response(serializer.data)
        
    elif request.method =='POST':
        serializer = StudentSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
1
2
3
4
5
6
7
8
9
10
11
12
13

Serializers


Serializers are classes that are responsible for converting the data in the varios format into python objects so it can be consumed by the database, and deserialization is converting the python objects into Json, XLM, etc so the client can consume.


Interoperability

ORM


Object Relational Mapping: perform database operations without writing SQL code.


Web Browsable API


Security


  • Authentication

  • Authorization

  • OAuth

Documentation


Django RESt Framework

Quick Start

To execute scripts on windows from the virtual env:

Set-ExecutionPolicy Unrestricted -Scope Process

# Free MySQL Instance on Cloud - CleverCloud

video

# Installation

pip install django

pip install djangorestframework
1
2
3

MySql and MySql Workbench

  • Download
  • Install
  • Configure
  • Workbench
  • Root Password

TIP

For now only need to install MySql Server and Workbench

show databases;
create database mydb;
use mydb;
show tables;
1
2
3
4

MySql Server and Workbench

For Python:./dev

pip install mysqlclient

or

python -m pip install mysqlclient
1
2
3
4
5

# First Project

# Create Virtual Environment

python -m venv env

Set-ExecutionPolicy Unrestricted -Scope Process

env/Scripts/activate
1
2
3
4
5

# Create a Project

mkdir djangorest
cd djangorest/
django-admin startproject firstProject
1
2
3

# Create an App

python manage.py startapp firstApp
1

Add the new App and the REST Framework to the installed apps:

Go to settings.py at the firstProject folder (same level as the firstApp).








 
 


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'firstApp'
]
1
2
3
4
5
6
7
8
9
10

# Create a View

Go to views.py under the firstApp and create a function based view:

from django.http import JsonResponse


def employee_view(request):
    emp = {
        'id': 123,
        'name': 'John',
        'salary': '50000'
    }
    return JsonResponse(emp)
1
2
3
4
5
6
7
8
9
10

With this the dictionary will be serialized as Json when returned to the client.

# Configure the URL and TEST

Go to url.py and add the path:



 



 


from django.contrib import admin
from django.urls import path
from firstApp import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.employee_view)
]
1
2
3
4
5
6
7
8

then:

python manage.py makemigrations

python manage.py migrate

python manage.py runserver
1
2
3
4
5

The server running on the browser:


Server

# Create App level URLs

Copy the urls.py from the project and paste it in the app folder.

we don't need from django.contrib import admin at this level.

So leave only this:

from django.urls import path
from firstApp import views

urlpatterns = [
    path('emps/', views.employee_view)
]
1
2
3
4
5
6

Now go to the urls.py on the project folder and make these alterations: include the include function on the import from the django.urls

Project Level URL

from django.contrib import admin
from django.urls import path, include
from firstApp import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('firstApp/', include('firstApp.urls'))
]
1
2
3
4
5
6
7
8

App Level URL: http://127.0.0.1:8000/firstApp/emps/

from django.urls import path
from firstApp import views

urlpatterns = [
    path('emps/', views.employee_view)
]
1
2
3
4
5
6

App Level URLs

# Create a Model Class

Now we are going to fetch the information from the database.

First we go to the models.py and create a new model.

Where thedef __str__ will return the string version.

from django.db import models

# Create your models here.


class Employee(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=20)
    sal = models.DecimalField(max_digits=10, decimal_places=3)

    def __str__(self):
        return self.id+self.name+self.sal
1
2
3
4
5
6
7
8
9
10
11
12

# Configure the database and run migrations

First we go to settings.py and change the database to mysql, as well as the name of the database that is being created,



 






DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'employeedb',
        'USER': 'root',
        'PASSWORD': 'test1234'
    }
}
1
2
3
4
5
6
7
8

then, lunch the mysql workbench and create the database:

create database employeedb;
use employeedb;
show tables
1
2
3

Creating a database

now go to the console, stop the server and python manage.py make migrations an then python manage.py migrate. If we show tables again we should see a lot of tables.

TIP

Remember to install pip install mysqlclient

outcome:

(env) PS C:\Users\Thiago Souto\Documents\DJANGO\Django Rest Framework\01-Django REST\firstProject>p
ython manage.py makemigrations
Migrations for 'firstApp':
  firstApp\migrations\0001_initial.py
    - Create model Employee
(env) PS C:\Users\Thiago Souto\Documents\DJANGO\Django Rest Framework\01-Django REST\firstProject>p
ython manage.py migrate       
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, firstApp, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying firstApp.0001_initial... OK
  Applying sessions.0001_initial... OK
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

And the database now has all the tables:


Creating a database

if we select the firstApp table we can see the id, name and sal that we have created.


Creating a database

# Use the model in the view and test

Now we are going to fetch the data from the database, instead of the static shown below from the views.py file.

#static
def employee_view(request):
    emp = {
        'id': 123,
        'name': 'John',
        'salary': '50000'
    }
    return JsonResponse(emp)
1
2
3
4
5
6
7
8

We start by importing the employee model.

To fetch all the records on the database we use data = Employee.objects.all();. Now, this will be a query set and we cannot return a query set, we need to return a dictionary. So we use ``








 
 



def employee_view(request):
    emp = {
        'id': 123,
        'name': 'John',
        'salary': '50000'
    }

    data = Employee.objects.all();
    response = {'employees': list(data.values('name', 'sal'))}

    return JsonResponse(response)
1
2
3
4
5
6
7
8
9
10
11

Because there is not data on the database lets insert some:


Creating a database

now if we run the server again we have:


Creating a database

# Function Based Views and Serializers

Until now, we have used the Django module to send the data as can be seen below.

 















from django.http import JsonResponse
from firstApp.models import Employee


def employee_view(request):
    emp = {
        'id': 123,
        'name': 'John',
        'salary': '50000'
    }

    data = Employee.objects.all();
    response = {'employees': list(data.values('name', 'sal'))}

    return JsonResponse(response)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Now we will take a look at the Django REST Framework.

# DRF Components

The request class is responsible for handling the incoming request to our restful API, and the response class on the other hand is responsible for handling the response and sending it back to the client.


Request and Response

Status Code:

status.HTTP_201_CREATED

status.HTTP_400_BAD_REQUEST

status.HTTP_204_NO_CONTENT
1
2
3
4
5

Django REST Framework provides 2 wrappers @api_view witch is a decorator used for function based views and APIView which is a class, which should be inherited by the class based views.


We need to mark all our function based views with the @api_view.


Request and Response

A class inheriting the APIView

class StudentDetail(APIView):
    def get_object(self, pk):
        try:
            return Student.objects.get(pk=pk)
        except Student.DoesNotExist:
            raise Http404
...
1
2
3
4
5
6
7

A decorator

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

    if request.method =='GET':
        students = Student.objects.all()
        serializer = StudentSerializer(students, many=True)
        return Response(serializer.data)
...
1
2
3
4
5
6
7
8

# Function Based Views

So Far we have constructed a very simple restful endpoint that returns a list of employees back, with a restfull READ operation.


Typically, any RESTful API will expose out: CREATE, READ, UPDATE, and DELETE, CRUD; through the appropriate http methods like PUT, DELETE, and GET; and also anu custom methods created. To create these custom methods we will be using function based views and class based views.


Function based views are very easy to create, we will be creating 2 fuctions to support the CRUD based operations.

@api_view(['GET', 'POST'])
    def student_list(request):
    
@api_view(['GET', 'PUT', 'DELETE'])
    def student_detail(request,pk):
1
2
3
4
5

TIP

pk stands for primary key

# Serializers


Serializers

DRF provides two types of classes for serializers: Serializer and ModelSerializer which we can use to create our own serializer.


To implement a serializer, you define the model to start vetting our application, for example a student model with 3 fields, then, if you are using the Serializer class, you will have to redefine all the fields in the model in the serializer as well. Which is a repetition of the fields, you are repetting the model class pretty much.


That's why we will be using ModelSerializer in most cases. You define a class, your own serializer, which will extend from ModelSerializer and within this model serialized you use class Meta: to specify for which model the serializer is, student in this case. Then you define a set of fields that you want to be serialized and de-serialized. If you want all the fields to be serialized just use __all__.


Serializers

# Create the Project

Let's create a project called fbvSerializers (Function Based Views and Serializers):

django-admin startproject fbvSerializers
1

then cd into the fvbSerializer directory and:

python manage.py startapp fbvApp
1

TIP

Remember to initialize the virtual environment:

python -m venv env

Set-ExecutionPolicy Unrestricted -Scope Process

env/Scripts/activate

Now got to the application definition, on settings.py, under APPS and register the fbvApp and the rest_framework










 
 


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'fbvApp',
    'rest_framework',
]
1
2
3
4
5
6
7
8
9
10
11
12

Then, adjust the Database settings on the same file:






 
 
 
 



# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'studentdb',
        'USER': 'root',
        'PASSWORD': 'test1234'
    }
}
1
2
3
4
5
6
7
8
9
10
11

# Create the Model

In the app folder under models create a class called Student:

from django.db import models

# Create your models here.


class Student(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=20)
    score = models.DecimalField(max_digits=10, decimal_places=3)
    
    def __str__(self):
        return self.id+self.name+self.
1
2
3
4
5
6
7
8
9
10
11
12

Now we create the studentdb database.

In the MySQL Workbench app:

create database studentdb;
use studentdb;
show tables
1
2
3

Now we can make the migrations:

python manage.py makemigrations

then

python manage.py migrate
1
2
3
4
5

TIP

if it doesn't find the module check the spell in the APPs register

The outcome:

(env) PS C:\Users\Thiago Souto\Documents\DJANGO\Django Rest Framework\01-Django REST\fbvSerializers> python manage.py makemigrations
Migrations for 'fbvApp':
  fbvApp\migrations\0001_initial.py
    - Create model Student
(env) PS C:\Users\Thiago Souto\Documents\DJANGO\Django Rest Framework\01-Django REST\fbvSerializers> python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, fbvApp, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying fbvApp.0001_initial... OK
  Applying sessions.0001_initial... OK
(env) PS C:\Users\Thiago Souto\Documents\DJANGO\Django Rest Framework\01-Django REST\fbvSerializers>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

Now show tables should a lot of tables:


Migrations

And we should see our table there:


Migrations

# Create the Serializer

To create the serializers we create the file serializers.py inside the fbvApp folder.

In there, there are two important imports: from rest_framework import serializers and from fbvApp.models import Student

Now we can create the class StudentSerializer:


First we make the imports, serializers from rest_framework, and the Student model from the fbvApp.models.

Then the class created will extend or inherit serializers.ModelSerializer using the Meta class and we define which models should the serializer work on and the fields on that model that should be serialized.

from rest_framework import serializers
from fbvApp.models import Student


class StudentSerializers(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = ['id', 'name', 'sal']
1
2
3
4
5
6
7
8

# GET single student

# Superuser

To create a superuser and password:

PS C:\Users\Thiago Souto\Documents\DJANGO\Django Rest Framework\01-Django REST\fbvSerializers> python manage.py createsuperuser
Username (leave blank to use 'thiagosouto'): admin
Email address: thiago.souto@yahoo.com.br
Password:
Password (again):
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

1
2
3
4
5
6
7
8
9

To change the password of the superusers we can do:

PS C:\Users\Thiago Souto\Documents\DJANGO\Django Rest Framework\01-Django REST\fbvSerializers> python manage.py shell
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)

>>> from django.contrib.auth.models import User
>>> users = User.objects.all()
>>> print(users)
<QuerySet [<User: admin>]>
>>> user = users[0]
>>> user.set_password(password123)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'password123' is not defined
>>> user.set_password('password123')
>>> user.save()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Now you can login to localuser/admin.

# Creating the Sonnet API

python -m venv env

Set-ExecutionPolicy Unrestricted -Scope Process

env/Scripts/activate

pip install djangorestframework

pip install mysqlclient
1
2
3
4
5
6
7
8
9
django-admin startproject sonetos

python manage.py startapp posts
1
2
3

then on settings.py, we include the app and the DRF:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'posts',
    'rest_framework'
]
1
2
3
4
5
6
7
8
9
10

Then we make the model:

from django.db import models
from django.contrib.auth.models import User


class Post(models.Model):
    title = models.CharField(max_length=300)
    body = models.CharField(max_length=1000)
    created = models.DateTimeField()
    poster = models.ForeignKey(User, on_delete=models.CASCADE)
    # is this is ever deleted, how do we want something to
    # referencing this post to happen

    class Meta:
        ordering = ['-title']


class Vote(models.Model):
    voter = models.ForeignKey(User, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Then create and configure the database: go to settings.py and change the database to mysql, as well as the name of the database that is being created.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'sonetosdb',
        'USER': 'root',
        'PASSWORD': 'test1234'
    }
}
1
2
3
4
5
6
7
8

On MySQL Workbench:

create database sonetosdb;
use sonetosdb;
show tables;
1
2
3

Deleting a database

DROP DATABASE sonetosdb;

Then make the migrations:

python manage.py makemigrations

python manage.py migrate
1
2
3

Create the serializers.py file at the posts app folder:

from rest_framework import serializers
from .models import Post


class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'title', 'body', 'created', 'poster']
1
2
3
4
5
6
7
8

Create a view

from rest_framework import generics
from .models import Post
from .serializers import PostSerializer


class PostList(generics.ListAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
1
2
3
4
5
6
7
8

Create the first URL

from django.contrib import admin
from django.urls import path
from posts import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/posts', views.PostList.as_view())
]

1
2
3
4
5
6
7
8
9
10

Create the Superuser

python manage.py createsuperuser
1

Register the class at admin.py

from django.contrib import admin
from .models import Post, Vote

admin.site.register(Post)
admin.site.register(Vote)
1
2
3
4
5

Now we can add, delete, etc posts.


Now we are going to create the models, serializer, view and url for Sonnets, Comments and Votes.


models.py:

from django.db import models
from django.contrib.auth.models import User


class Sonnet(models.Model):
    title = models.CharField(max_length=300)
    body = models.CharField(max_length=1000)
    created = models.DateTimeField()
    poster = models.ForeignKey(User, on_delete=models.CASCADE)
    # is this is ever deleted, how do we want something to
    # referencing this post to happen

    class Meta:
        ordering = ['-title']


class Vote(models.Model):
    voter = models.ForeignKey(User, on_delete=models.CASCADE)
    sonnet = models.ForeignKey(Sonnet, on_delete=models.CASCADE)


class Comment(models.Model):
    commenter = models.ForeignKey(User, on_delete=models.CASCADE)
    comment = models.CharField(max_length=1000)
    sonnet = models.ForeignKey(Sonnet,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

serializer.py

from rest_framework import serializers
from .models import Sonnet, Comment, Vote


class SonnetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Sonnet
        fields = ['id', 'title', 'body', 'created', 'poster']


class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = ['commenter', 'comment', 'sonnet']


class VoteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Vote
        fields = ['voter', 'sonnet']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

view.py:

from rest_framework import generics
from .models import Sonnet, Comment, Vote
from .serializers import SonnetSerializer, CommentSerializer, VoteSerializer


class SonnetList(generics.ListAPIView):
    queryset = Sonnet.objects.all()
    serializer_class = SonnetSerializer


class CommentList(generics.ListAPIView):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer


class VoteList(generics.ListAPIView):
    queryset = Vote.objects.all()
    serializer_class = VoteSerializer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

urls.py

from django.contrib import admin
from django.urls import path
from posts import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/sonnets', views.SonnetList.as_view()),
    path('api/comments', views.CommentList.as_view()),
    path('api/votes', views.VoteList.as_view())
]
1
2
3
4
5
6
7
8
9
10
11

Now let's Create a post, for this we will change the class from ListAPIView to ListCreateAPIView.

and just by doing that we add the functionality:

class CommentList(generics.ListCreateAPIView):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer
1
2
3

API

Now we are going to solve how only the poster can post comments on his name. Otherwise anybody will be able to comment with someone else's name.


First we go to the serializers and set some fields to read only.


serializers.py

from rest_framework import serializers
from .models import Sonnet, Comment, Vote


class SonnetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Sonnet
        fields = ['id', 'title', 'body', 'created', 'poster']


class CommentSerializer(serializers.ModelSerializer):
    commenter = serializers.ReadOnlyField(source='commenter.username')
    commenter_id = serializers.ReadOnlyField(source='commenter.id')

    class Meta:
        model = Comment
        fields = ['commenter', 'commenter_id', 'comment', 'sonnet']


class VoteSerializer(serializers.ModelSerializer):
    voter = serializers.ReadOnlyField(source='voter.username')
    voter_id = serializers.ReadOnlyField(source='voter.id')

    class Meta:
        model = Vote
        fields = ['voter', 'voter_id', 'sonnet']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Now we no longer can choose the commenter:


API

Although if we comment we will get an error, because there is no commenter.

So we have to go the view.py and define the perform_create function. And this function says anytime I'm going to save I'm going to grab who made the request and set as commenter

views.py

from rest_framework import generics
from .models import Sonnet, Comment, Vote
from .serializers import SonnetSerializer, CommentSerializer, VoteSerializer


class SonnetList(generics.ListAPIView):
    queryset = Sonnet.objects.all()
    serializer_class = SonnetSerializer


class CommentList(generics.ListCreateAPIView):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer

    def perform_create(self, serializer):
        serializer.save(commenter=self.request.user)


class VoteList(generics.ListCreateAPIView):
    queryset = Vote.objects.all()
    serializer_class = VoteSerializer

    def perform_create(self, serializer):
        serializer.save(voter=self.request.user)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Now we can add the permissions.

 












 








 




from rest_framework import generics, permissions
from .models import Sonnet, Comment, Vote
from .serializers import SonnetSerializer, CommentSerializer, VoteSerializer


class SonnetList(generics.ListAPIView):
    queryset = Sonnet.objects.all()
    serializer_class = SonnetSerializer


class CommentList(generics.ListCreateAPIView):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer
    permission_classes = [permissions.IsAuthenticated]

    def perform_create(self, serializer):
        serializer.save(commenter=self.request.user)


class VoteList(generics.ListCreateAPIView):
    queryset = Vote.objects.all()
    serializer_class = VoteSerializer
    permission_classes = [permissions.IsAuthenticated]

    def perform_create(self, serializer):
        serializer.save(voter=self.request.user)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

But this way, when they do a get they will not see the comments.

We have to restrict only the posts for authenticated users only. For theat we just change for IsAuthenticatedOrReadOnly.

 












 








 




from rest_framework import generics, permissions
from .models import Sonnet, Comment, Vote
from .serializers import SonnetSerializer, CommentSerializer, VoteSerializer


class SonnetList(generics.ListAPIView):
    queryset = Sonnet.objects.all()
    serializer_class = SonnetSerializer


class CommentList(generics.ListCreateAPIView):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def perform_create(self, serializer):
        serializer.save(commenter=self.request.user)


class VoteList(generics.ListCreateAPIView):
    queryset = Vote.objects.all()
    serializer_class = VoteSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def perform_create(self, serializer):
        serializer.save(voter=self.request.user)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Now let's vote for a Sonnet.

# Deploy mySQL to Heroku

  1. Create the app at heroku

  2. go to resource and look for clearDB mySQL

  3. go to settings->Config Vars and get the mysql:... code

mysql://bssbbb21dfagae3sa4daf3:d526632fdde7cccx8a@eu-cbdbr-west-0115.cleardb.com/heroku_343a7f9f28335c5?reconnect=true

this are the fields:

User: bssbbb21dfagae3sa4daf3
Hostname: eu-cbdbr-west-0115.cleardb.com
Password: d526632fdde7cccx8a
1
2
3
4
5
6
7
  1. Configure a new connection on the mySQL workbench

  2. Change the Database setting at Django

TIP

SHOW databases; will show you the databases and clearDB only allows one database for free

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'heroku_344253add7f19f2fasdfsd8335ac5',
'USER': 'bssbbb21dfagae3sa4daf3',
'PASSWORD': 'd526632fdde7cccx8a',
'HOST': 'eu-cbdbr-west-0115.cleardb.com',
}
}
1
2
3
4
5
6
7
8
9

# Deploying DJANGO to Heroku

  1. Create a procfile at the root with the manage.py folder. with the following inside:
web: gunicorn CCMS.wsgi --log-file -
1

TIP

Change the CCMS to your project name

web: gunicorn sonetos.wsgi --log-file -

  1. Install gunicorn:
pip install gunicorn
1
  1. Update settings.py file
DEBUG = False

ALLOWED_HOSTS = ['127.0.0.1','sampledomain.com']
1
2
3

Updated Middle ware Code

'whitenoise.middleware.WhiteNoiseMiddleware',
1
  1. install whitenoise
pip install whitenoise
1
  1. update urls.py
from django.views.static import serve
from django.conf.urls import url
from django.conf import settings

...

url(r'^media/(?P<path>.*)$', serve,{'document_root':       settings.MEDIA_ROOT}), 
url(r'^static/(?P<path>.*)$', serve,{'document_root': settings.STATIC_ROOT}), 
1
2
3
4
5
6
7
8
  1. Prepare the requirements.txt
pip freeze > requirements.txt
1
  1. Go to heroku and create an app

  2. Go to settings and add a building pack

  3. Go to deploy and choose github and connect to the repository

  4. get the domain name by open app and copy the address and go back to settings.py and add on ALLOWED_HOSTS, without the https://. And commit the changes

  5. Go back to Heroku and deploy a GitHub branch and click Deploy Branch

TIP

Remember to change DEBUG = False

also:

heroku git:remote -a sonetos-api

heroku config:set DISABLE_COLLECTSTATIC=1