Introduction
This series of posts intends to develop a flutter signup/login app working with API calls to Django backend. The idea is to use the DRF to create APIs which can be called by the flutter application. Most of the tutorials I have seen only talk about doing it with Firebase. Since I already will be using PostgreSQL for the Django, I intend on using that as my server DB and using SQLite with the Flutter Application to store just the Auth token and Username.
Django Rest Framework
I assume people reading this post already know about it but still just an introduction:
- DRF allows for making powerful and flexible Web APIs.
- DRF provides a lot of authentication schemes out of the box and even allows us to implement custom schemes.
- DRF provides an easy way to serialize and deserialize JSON according to your DB Schema. (Model Serializers)
Flutter
Google Flutter is just awesome when it comes to cross-platform app development. The fact that it doesn't convert your code to machine compatible but instead is capable of directly rendering it on the machine is the best.
BloC design pattern
I intend to use BloC (Business Logic Component) which was announced by Google in GOOGLE I/O' 18. BloC is simply a state management pattern that works on sinks and streams. The widgets monitor the state changes and send them to the BloC using sinks and other widgets monitor those by subscribing to those streams where they expect the relevant data to arrive. The required sections are rebuilt per the state change. Thus Bloc stands in between data and UI and asynchronously maps events to states which rebuild the UI when required.
Let us Start
We will start by creating a new Django project to provide the required APIs for Login and Signup.
Go to your desired folder and activate your virtual environment. (You can refer here for the same)
Step 1
- Create the project by:
django-admin startproject HOME
Note that I named my project as HOME, you can name it anything just be consistent in all the places where we use the names in the commands/code that follows.
- cd into the project directory and create an app called api by:
python manage.py startapp api
I plan on using the auth system that Django provides by default. The User model has almost all the required fields that we may need for the Login and Signup. In case you need extra fields like phone number, address, etc. you can always define a new profile model that will have a one to one relationship with the User model. But for the sake of simplicity here, we are going to go with the User model itself. We have the fields first_name, last_name, username, email and password which we can use to create a new user.
Step 2
- Install DRF by
pip install djangorestframework
- and also add it to your installed apps
INSTALLED_APPS = [
...
'rest_framework',
]
- There are many auth systems available but I am going to go with Token Auth. We need to add that too in our installed apps:
INSTALLED_APPS = [
...
'rest_framework',
'rest_framework.authtoken',
]
- We also need to add the following in our settings for us to be able to use the token auth:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
- Migrate your changes by:
python manage.py migrate
Run it from HOME/
Step 3
Now that we are all set up and we have the default User model ready for us, we need to start creating our API
- Create a file named
serializers.py
inHOME/api/
and the following code to it:
from django.contrib.auth.models import User
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
return user
class Meta:
model = User
fields = (
'username',
'first_name',
'last_name',
'email',
'password',
)
validators = [
UniqueTogetherValidator(
queryset=User.objects.all(),
fields=['username', 'email']
)
]
What we did here is we created a serializer in order to map JSON to our model and vice-versa. The unique together validator will allow us to verify that the combination of username and email is unique for any particular user.
- edit your
HOME/api/views.py
and add the following code to it:
from .serializers import UserSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAdminUser
from django.contrib.auth.models import User
class UserRecordView(APIView):
"""
API View to create or get a list of all the registered
users. GET request returns the registered users whereas
a POST request allows to create a new user.
"""
permission_classes = [IsAdminUser]
def get(self, format=None):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid(raise_exception=ValueError):
serializer.create(validated_data=request.data)
return Response(
serializer.data,
status=status.HTTP_201_CREATED
)
return Response(
{
"error": True,
"error_msg": serializer.error_messages,
},
status=status.HTTP_400_BAD_REQUEST
)
What we are doing here is creating a get and a post method, the get method allows us to get a list of all the registered users with our application and the post method allows us to create a new user with the required info as specified in our serializer.
Also, I added IsAdminUser as the permission class, the reason is that we do not want anyone to spam our server by sending multiple POST requests and creating unnecessary accounts.
- Last but not least we will add routes for access to the application. let us create the file
HOME/api/urls.py
and add the following code to it:
from django.urls import path
from .views import UserRecordView
app_name = 'api'
urlpatterns = [
path('user/', UserRecordView.as_view(), name='users'),
]
- We need to include the route for this and the token system in our main urls i.e. edit the file
HOME/urls.py
and add the following code to it:
from django.contrib import admin
from django.urls import path, include
from rest_framework.authtoken import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls', namespace='api')),
path('api-token-auth/', views.obtain_auth_token, name='api-token-auth'),
]
- We would need to create an admin user by:
python manage.py createsuperuser
Now enter the details asked and use them below.
Accessing our API
Thus, we have successfully created the required endpoint for the API.
We can now test it by the following:
I suggest you use POSTMAN which will make it easier for you.
Get authentication token
- Endpoint:
http://localhost:8000/api-token-auth/
Parameters required :
username, password
Sample body:
{
"username": "admin",
"password": "password"
}
Get / Post user
- Endpoint :
http://localhost/api/user/
-
GET
- parameters:
auth token as header
- sample header:
- parameters:
{
"Authorization": "TOKEN <token>"
}
- sample response:
{
"username": "<username>",
"first_name": "<first_name>",
"last_name": "<last_name>",
"email": "<email>",
"password": "<encrypted string>"
}
-
POST
- parameters:
- header:
auth token as header
- sample:
{ "Authorization" : "TOKEN <token>" }
- body: username, email, first_name, last_name, password
- sample:
{ "username": "<username>", "first_name": "<first_name>", "last_name": "<last_name>", "email": "<email>", "password": "<encrypted string>" }
- response (on succesful):
{ "username": "<username>", "first_name": "<first_name>", "last_name": "<last_name>", "email": "<email>", "password": "<encrypted string>" }
I will continue the project and write posts for each part. The end result will be a flutter app working with this API and SQLite DB.
Next: Flutter signup/login application with Django backend #2
Final Post: Flutter signup/login application with Django backend #3
Please feel free to drop a message at Ohuru in order to avail various development services offered by us.
Top comments (12)
i'm not getting whats wrong here
all my end points including
this localhost:8000/api-token-auth/
always return 404.
i can only acess localhost:8000/admin/
can you make available any sourcecode for comparisson?
check whether you have url for api in urls.py
This must be the issue
try using 127.0.0.1:8000 or sometimes u need to add your android emulator to allowed hosts. Add 10.0.2.2 in your allowed hosts of your django project
You'll get the following error is you try to open 127.0.0.1:8000/api/user
{
"detail": "Authentication credentials were not provided."
}
Solved it by adding the following line
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
...
),
}
I was having the same issue, thank you this solved my problem, but for the api-token-auth/ :
{
"detail": "Method \"GET\" not allowed."
},
I'm still getting
{
"detail": "Method \"GET\" not allowed."
}
Hi Amartya,
I am having difficulties to understand creating new user.
SOLVED:
So, I was thinking always to send users own token. But at the and need superuser's auth token for creating new user :))
below was my question:
When I request : POST, localhost:8000/api/user/ with username, password, I got {
"detail": "Authentication credentials were not provided."
}
So then do I need auth token first so. I tried also :
1- POST, localhost:8000/api-token-auth/ with newusername, newpasword and I got:
{
"non_field_errors": [
"Unable to log in with provided credentials."
]
}
At the end I can not create new user. So, for creating new user, how to on?
I am sorry for this stupid question but I am newbie and stacked here
Waiting for the next ones
Next post (#2) is out: dev.to/amartyadev/flutter-signup-l...
Great tutorial ! For the next steps and to go faster I use DjangoModel2Dart to transform my Django models to Dart classes to transform from the json of the REST API from Django or to json.
127.0.0.1:8000/api-token-auth/
{
"username": "foo",
"password": "password"
}
{"detail":"Method \"GET\" not allowed."}
Is "Method \"GET\" not allowed." expected? Or I screw up?
part 2-3?