Zum Hauptinhalt springen
•••
Sicherheitslücken (CVE)CVE-2025-21065 | Samsung Retail Mode up to 5.59.10 input validation(03.10.2026 um 03:50 Uhr)
•
Sicherheitslücken (CVE)CVE-2025-21060 | Samsung Smart Switch 3.7.64.10 cleartext storage(03.10.2026 um 03:50 Uhr)
•••
Sichere ProgrammierungYour agent's kill switch was never tested. That is the whole bug.(03.10.2026 um 04:02 Uhr)
•
Sichere ProgrammierungMultilingual Ghost CMS Blog: 5. Making Home Links Language Aware(03.10.2026 um 04:02 Uhr)
•
Sichere ProgrammierungUK Hackathon Statistics(03.10.2026 um 04:03 Uhr)
••••
Sicherheitslücken (CVE)CVE-2025-21065 | Samsung Retail Mode up to 5.59.10 input validation(03.10.2026 um 03:50 Uhr)
•
Sicherheitslücken (CVE)CVE-2025-21060 | Samsung Smart Switch 3.7.64.10 cleartext storage(03.10.2026 um 03:50 Uhr)
•••
Sichere ProgrammierungYour agent's kill switch was never tested. That is the whole bug.(03.10.2026 um 04:02 Uhr)
•
Sichere ProgrammierungMultilingual Ghost CMS Blog: 5. Making Home Links Language Aware(03.10.2026 um 04:02 Uhr)
•
Sichere ProgrammierungUK Hackathon Statistics(03.10.2026 um 04:03 Uhr)
•
Intelligence View
⚡ tsecurity.de Intelligence

Django accounts management app (1), registration and activation

What to expect from this article? We have created the project skeletal structure in the previous article, this article will build on it. it will cover Accounts…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!




What to expect from this article?



We have created the project skeletal structure in the previous article, this article will build on it. it will cover




  • Accounts database structure, including users and verification code.

  • Models serializers.

  • Account Views for account registration, activation. next article should cover the rest of the views, such as logging in, refreshing tokens, changing password, forgetting password and re-sending code.



I'll try to cover as many details as possible without boring you, but I still expect you to be familiar with some aspects of Python and Django.



the final version of the source code can be found on https://github.com/saad4software/alive-diary-backend






Series order



Check previous articles if interested!




  1. AI Project from Scratch, The Idea, Alive Diary

  2. Prove it is feasible with Google AI Studio


  3. Django API Project Setup

  4. Django accounts management app (1), registration and activation (You are here 📍)






Accounts app setup



let's create serializers file in the app




from rest_framework import serializers
from app_account.models import *







app_account/serializers.py




and the urls file




from django.urls import path, include
from .views import *

urlpatterns = [

]







app_account/urls.py




finally, let's connect the app urls to the project urls by editing projects urls file as




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

urlpatterns = [
path('admin/', admin.site.urls),
path('api/account/', include('app_account.urls')),

]







alive_diary/urls.py




Now we can call any accounts url with the prefix "api/account/"






The Models



the main model for accounts app is the User model of course




from django.db import models
from django.contrib.auth.models import AbstractUser
from datetime import timedelta, datetime

class User(AbstractUser):
userTypes = (
('A', 'Admin'),
('C', 'Client'),
)

role = models.CharField(max_length=1, choices=userTypes, default="C")

hobbies = models.CharField(max_length=255, null=True, blank=True)
job = models.CharField(max_length=100, null=True, blank=True)
bio = models.TextField(null=True, blank=True)

country_code = models.CharField(max_length=10, null=True, blank=True)
expiration_date = models.DateTimeField(default=datetime.now()+timedelta(days=30))







app_account/models.py




Usually, it is better to keep the User model as simple as possible and move other details to a Profile model with a one-to-one relationship with the User, but to simplify things, I'll add the required user info directly to the User model this time.



We are inheriting from AbstractUser model, AbstractUser includes multiple fields




class AbstractUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(...)
first_name = models.CharField(...)
last_name = models.CharField(...)
email = models.EmailField(...)
is_staff = models.BooleanField(...)
is_active = models.BooleanField(...),
date_joined = models.DateTimeField(...)







the most important ones are:





  • username will be used for logging in.


  • is_active will be used to prevent unverified accounts from logging in.


  • is_staff will distinguish admin (with value true) from normal users.



We have also added multiple fields for this project user which are





  • role to distinguish admin from client accounts, we can use is_staff for this simple project since we only have two roles, but a larger project can have more than 2 roles, making this field essential for permissions handling.


  • hobbies, job, bio Knowing more about the user can help build a better reflection, therefore we are asking for hobbies, job, and how the user describes him/her self.


  • country_code for statistics


  • expiration_date for subscription-based expiration date.



We also need a Verification code model to keep and track verification codes for account activation, forgetting passwords, and resending codes.




import random

class VerificationCode(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
code = models.CharField(max_length=6, default=random.randint(111111, 999999))
email = models.EmailField()
expiration_date = models.DateTimeField(default=datetime.now()+timedelta(days=1))

def __str__(self):
return self.user.username







app_account/models.py




It connects with the User model and generates a random value of a 6-digit code number. it also has an expiration time of 24 hours. we have also email filed in case the user wants to validate multiple email addresses, it is rare and can be removed for this app. Let's move to serializers next.






The Registration API



let's start with the serializer




from rest_framework import serializers
from app_account.models import *
from django.contrib.auth import get_user_model
from common.utils import is_valid_email


class RegisterSerializer(serializers.ModelSerializer):
password1 = serializers.CharField(write_only=True)
password2 = serializers.CharField(write_only=True)

def validate_username(self, value):
if not is_valid_email(value):
raise serializers.ValidationError("invalid_email")
return value

def validate(self, data):
if data['password1'] != data['password2']:
raise serializers.ValidationError("password_dont_match")

data['password'] = data['password1']

data.pop('password1', None)
data.pop('password2', None)

return data

class Meta:
model = get_user_model()
fields = [
'id',
'email',
'password1',
'password2',
'first_name',
'last_name',
'country_code',
'username',
]
read_only_fields = ['id',]








app_account/serializers.py




We are using ModelSerializer, from Django rest framework. we selected the user model get_user_model() in class Meta and a list of serialized fields.



We have added two additional fields to the model serializer, password1, and password2. To validate they have the same value, we have overwritten the validate method. and to enforce using valid email as a username, we have added a field validator for username field.

the is_valid_email function should look somewhat like this




import re

def is_valid_email(email):
regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b'
return email and isinstance(email, str) and re.fullmatch(regex, email)








common/utils.py




Personally, I don't like regular expressions, I've never got the grasp of them, but they seem to be the best way to validate emails. If you got a better way, please share it with us.



Since our new fields password1 and password2 don't belong to the original user model, we removed them from the data dictionary and added the password field in order to use serializer data directly to create a new user.






Serializer's best practices dilemma




  • Should serializers use database queries and edit databases?

  • or should they only make basic field type validation (string, integer, ...)?



There is no clear answer actually, for instance, Django Rest framework model serializers seem to make queries for unique fields, like the serializer error we got when tried to create a use with the same name, it was generated by the serializer, not the view.

The Create, Save, Update methods writes values to the database.

Yet, accessing the database only in views seems to align more with Separation of Concerns and Flexibility.



What do you think is better?



I've read a lot about keeping things separated, even separating database queries from database updating methods. so let's try doing that. creating the AccountActivateView in the views.py file should look like.



In our case, we can overwrite the create method for RegisterSerializer in order to create a new user and validation code instants, and even sending the verification code from the serializer.



But instead, I'll keep the model related operations in the views file



Let's move to the registration view




from django.shortcuts import render
from rest_framework import generics, status
from .serializers import *
from rest_framework.renderers import BrowsableAPIRenderer, JSONRenderer
from django.core.mail import send_mail
from django.conf import settings

class AccountRegisterView(generics.CreateAPIView):
permission_classes = ()

queryset = get_user_model().objects.all()
serializer_class = RegisterSerializer
renderer_classes = [BrowsableAPIRenderer, CustomRenderer]

def perform_create(self, serializer):
user = get_user_model().objects.create_user(**serializer.validated_data, is_active=False)

# email verification code, assumed username is email!
code = VerificationCode(user=user, email=user.username)
code.save()

send_mail(
'Welcome to Alive Diary! 🚀',

f"""
Dear
{user.first_name} {user.last_name},

Welcome aboard! 🎉

Your activation code is
{code.code}

Best regards,
Alive Diary team with ❤️

"""
,
f'AliveDiary<{settings.EMAIL_SENDER}>',
[user.username],
fail_silently=False,
)










app_account/views.py




We are using CreatAPIView from the rest framework, it accepts POST requests with the schema of serializer_class, the BrowsableAPIRenderer build a web interface for this API and the JSONRenderer is responsible for building the JSON response.



Overwriting the perform_create method allows us control the user creation mechanism, we are creating the user instant, making sure the is_active field is set to False, then creating the verification code instant that is connected to the new user model, and finally sending an email with the verification code to the user.



Sending the email requires the correct configuration for email fields in the setting file, please let me know if you have issues in this particular point to create a separate article for it



Finally, let's add the API url




from django.urls import path, include
from .views import *

urlpatterns = [
path('register/', AccountRegisterView.as_view()),
]







app_account/urls.py




Nice, let's try it out




python manage.py makemigrations
python manage.py migrate
python manage.py runserver 0.0.0.0:8555






opening http://localhost:8555/api/account/register, we should be able to see something like this (it is due to BrowsableAPIRenderer)



register BrowsableAPIRenderer



the required fields are username, password1 and password2, we expect the email to be used as username.

it looks okay, it created a user model with a verification code model connected to it (used SqlBrowser to open the SQLite db file). But the default response looks like this with the status 201.




{
"country_code": null,
"username": "[email protected]"
}






I Prefer all responses to have this schema




{
"status": "success",
"code": 200,
"data": {},
"message": None
}








  • status should be either "success" or "error"


  • code is the response status code


  • data is the actual response data


  • message should contain error text or any other message



But how to do so?

The best way is by implementing a custom JSON renderer function. let's do it




from rest_framework.renderers import JSONRenderer

class CustomRenderer(JSONRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
status_code = renderer_context['response'].status_code
response = {
"status": "success",
"code": status_code,
"data": data,
"message": None
}
if not str(status_code).startswith('2'):
response["status"] = "error"
response["data"] = None

if 'detail' in data:
response["message"] = data["detail"]
else:
response['message'] = dict2string(data)

return super(CustomRenderer, self).render(response, accepted_media_type, renderer_context)

def dict2string(data):
msg = ""
for key in data.keys():
if key == "non_field_errors":
msg += str(data[key][0]) + "\n\r"
else:
msg += key + ": " + str(data[key][0]) + "\n\r"
return msg








common/utils.py




We inherited from JSONRenderer and overwritten the render method. serializer errors.




  • started with reading the response status code, and putting it in the code field

  • and we moved the actual response data to the data field in our response schema

  • to distinguish errors from successful responses, we check the status code. If the status code is in the 200 family, it is a successful response

  • if not, it is an error. so we change the status to "error", and extract the error message from the response details field (if available).

  • serializer's validation errors don't have details fields, it is a dictionary indicating an error message (value) for each field name (key), so we created a small function dict2string to convert it to a simple string. I think it can be further improved, can you help with it?



that is it for the response schema, let's try using it now!

in views.py add our custom renderer to the register view class




from common.utils import CustomRenderer


class AccountRegisterView(generics.CreateAPIView):
permission_classes = ()

queryset = get_user_model().objects.all()
serializer_class = RegisterSerializer
renderer_classes = [BrowsableAPIRenderer, CustomRenderer]

def perform_create(self, serializer):
serializer.save()







app_account/views.py




running the server, and opening http://localhost:8555/api/account/register/ shows the difference directly



Method not allowed error



we can see our schema in the error message 🤩, cool, let's try registering a new user, I'll call it "[email protected]"



Register success response



looking great, now let's test the serializer validation error, we will try to register the same user again



Serializer error response



Wonderful, this is a validation error response, it was serialized as a field:message

what comes after registration? it is validation

register -> confirm email -> login -> whatever





The Activation API



We want to check if the user got the activation code we sent during registration or not, if the user sends the right code, we will activate their account, if not, we will ask them to check for it again, or maybe resend the code (another API for later)

Similar to the registration API-creating process, let's start with the serializer




class ActivateSerializer(serializers.Serializer):
username = serializers.CharField(required=True)
code = serializers.CharField(required=True)







This is not related to a certain database model, so we are inheriting from the generic Serializer, notice that serializers are similar to forms, so we set the fields and their validation rules.

We are using two string fields (CharField), both are required, username which is the user email address, and code.




from rest_framework.views import APIView
from rest_framework.exceptions import APIException
from rest_framework.response import Response


class AccountActivateView(APIView):
permission_classes = ()
renderer_classes = [CustomRenderer, BrowsableAPIRenderer]

def post(self, request, *args, **kwargs):
serializer = ActivateSerializer(data=request.data)

if not serializer.is_valid():
raise APIException(serializer.errors)

verification_query = VerificationCode.objects.filter(
user__username=serializer.validated_data.get("username"),
email=serializer.validated_data.get("username"),
code=serializer.validated_data.get("code"),
).order_by('-id')

if not verification_query.exists():
raise APIException("invalid_code")

user = get_user_model().objects.filter(username=serializer.validated_data.get("username")).first()
user.is_active=True
user.save()

verification_query.delete()

return Response("success")








app_account/views.py




Since we are using a custom API view, we are inheriting from APIView, it offers 5 functions (get, post, put, delete, and patch). We are de-serializing request data from POST requests, and validating its type, then making a query to find if the provided data exists or not, if it exists, we activate the user and remove the code object from its table. if not we send an error message saying it is an "invalid_code". finally, the URLs file should be updated to include this view's URL




from django.urls import path, include
from .views import *

urlpatterns = [
path('register/', AccountRegisterView.as_view()),
path('activate/', AccountActivateView.as_view()),

]







app_account/urls.py




Now we can open the URL http://localhost:8555/api/account/activate/, we are using a custom API view, so it doesn't get the required field



Invalid code exception



We can get the code from the database (for testing purposes). The request should look like




{
"username":"[email protected]",
"code":"123456"
}






If everything went successfully, the response should look like




{
"status": "success",
"code": 200,
"data": "success",
"message": null
}






That's it

Let's wrap it up! I know we haven't log in yet, but it became a very long article indeed, let's continue in the next article



Stay tuned 😎

🔍 CTI & Forensik

Cyber Threat Intelligence & Forensik

ATT&CK-Navigator · IoC-Radar · Exploit-Belege
CTI Threat Relationship Graph
Akteure · Techniken · Beziehungen
4 Knoten · 3 Relationen
CVE / Incident Threat Actor Software MITRE ATT&CK CWE Weakness IoC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Django accounts management app (1), registration and activation

Thematisch verwandte Begriffe: Django, accounts, management, registration · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag