<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AVLESSI Matchoudi</title>
    <description>The latest articles on DEV Community by AVLESSI Matchoudi (@lionrouge1).</description>
    <link>https://dev.to/lionrouge1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1937447%2F81431a11-1c34-4bbc-8b7a-b5c1c8c025c9.jpeg</url>
      <title>DEV Community: AVLESSI Matchoudi</title>
      <link>https://dev.to/lionrouge1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lionrouge1"/>
    <language>en</language>
    <item>
      <title>Guide to Building a Complete Blog App with Django using TDD Methodology and PostgreSQL (Part 3): Secure User Authentication</title>
      <dc:creator>AVLESSI Matchoudi</dc:creator>
      <pubDate>Fri, 18 Oct 2024 09:04:32 +0000</pubDate>
      <link>https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-part-3-secure-user-authentication-nkc</link>
      <guid>https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-part-3-secure-user-authentication-nkc</guid>
      <description>&lt;p&gt;Welcome back, everyone! In &lt;a href="https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-part-2-13ok"&gt;the previous part&lt;/a&gt;, we established a secure user registration process for our Django blog application. However, after successful registration, we were redirected to the homepage. This behaviour will be modified once we implement user authentication. User authentication ensures that only authorized users can access certain functionalities and protects sensitive information.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6n7t3p1bmopk2luut8dh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6n7t3p1bmopk2luut8dh.png" alt="Entity-Relationship Diagram(ERD)" width="800" height="718"&gt;&lt;/a&gt;&lt;br&gt;
In this series, we are building a complete blog application, guided by the following Entity-Relationship Diagram (ERD). For this time, our focus will be on setting up a secure user authentication process. If you find this content helpful, &lt;strong&gt;please like, comment, and subscribe to stay updated&lt;/strong&gt; when the next part is released.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9myq8tvpbpbkqfxog9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9myq8tvpbpbkqfxog9z.png" alt="login preview" width="564" height="537"&gt;&lt;/a&gt;&lt;br&gt;
This is a preview of how our login page will look after we’ve implemented the login functionality. If you haven’t read the previous parts of the series, I recommend doing so, as this tutorial is a continuation of the previous steps.&lt;/p&gt;

&lt;p&gt;Okay, let’s get started !!&lt;/p&gt;

&lt;p&gt;Django comes with a built-in app called &lt;code&gt;contrib.auth&lt;/code&gt;, which simplifies handling user authentication for us. You can check the &lt;code&gt;blog_env/settings.py&lt;/code&gt; file, under the &lt;code&gt;INSTALLED_APPS&lt;/code&gt;, you’ll see that &lt;code&gt;auth&lt;/code&gt; is already listed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# django_project/settings.py
INSTALLED_APPS = [
    # "django.contrib.admin",
    "django.contrib.auth",  # &amp;lt;-- Auth app
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;auth&lt;/code&gt; app provides us with multiple &lt;a href="https://docs.djangoproject.com/en/5.0/topics/auth/default/#module-django.contrib.auth.views" rel="noopener noreferrer"&gt;authentication views&lt;/a&gt; for handling login, logout, password change, password reset, etc. This means that the essential authentication functionality, such as user login, registration, and permissions, is ready to use without needing to build everything from scratch.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll focus solely on the login and logout views, and cover the rest of the views in later parts of the series.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Create a login form
&lt;/h2&gt;

&lt;p&gt;Following our TDD approach, let’s begin by creating tests for the login form. Since we haven’t created a login form yet, navigate to the &lt;code&gt;users/forms.py&lt;/code&gt; file and create a new class inheriting from &lt;code&gt;AuthenticationForm&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/forms.py
from django.contrib.auth import AuthenticationForm

class LoginForm(AuthenticationForm):


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the form is defined, we can add test cases in &lt;code&gt;users/tests/test_forms.py&lt;/code&gt; to verify its functionality.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/tests/test_forms.py

#   --- other code

class LoginFormTest(TestCase):
  def setUp(self):
    self.user = User.objects.create_user(
      full_name= 'Tester User',
      email= 'tester@gmail.com',
      bio= 'new bio for tester',
      password= 'password12345'
    )

  def test_valid_credentials(self):
    """
    With valid credentials, the form should be valid
    """
    credentials = {
      'email': 'tester@gmail.com',
      'password': 'password12345',
      'remember_me': False
    }

    form = LoginForm(data = credentials)
    self.assertTrue(form.is_valid())

  def test_wrong_credentials(self):
    """
    With wrong credentials, the form should raise Invalid email or password error
    """
    credentials = {
      'email': 'tester@gmail.com',
      'password': 'wrongpassword',
      'remember_me': False
    }
    form = LoginForm(data = credentials)
    self.assertIn('Invalid email or password', str(form.errors['__all__']))

  def test_credentials_with_empty_email(self):
    """
    Should raise an error when the email field is empty
    """
    credentials = {
      'email': '',
      'password': 'password12345',
      'remember_me': False
    }
    form = LoginForm(data = credentials)
    self.assertFalse(form.is_valid())
    self.assertIn('This field is required', str(form.errors['email']))

  def test_credentials_with_empty_password(self):
    """
    Should raise error when the password field is empty
    """
    credentials = {
      'email': 'tester@gmail.com',
      'password': '',
      'remember_me': False
    }
    form = LoginForm(data = credentials)
    self.assertFalse(form.is_valid())
    self.assertIn('This field is required', str(form.errors['password']))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These tests cover scenarios like successful login with valid credentials, failed login with invalid credentials, and handling error messages appropriately.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;AuthenticationForm&lt;/code&gt; class provides some basic validation by default. However, with our &lt;code&gt;LoginForm&lt;/code&gt;, we can tailor its behaviour and add any necessary validation rules to meet our specific requirements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/forms.py

# -- other code
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, AuthenticationForm # new line
from django.contrib.auth import get_user_model, authenticate # new line


# --- other code

class LoginForm(AuthenticationForm):
  email = forms.EmailField(
    required=True,
    widget=forms.EmailInput(attrs={'placeholder': 'Email','class': 'form-control',})
  )
  password = forms.CharField(
    required=True,
    widget=forms.PasswordInput(attrs={
                                'placeholder': 'Password',
                                'class': 'form-control',
                                'data-toggle': 'password',
                                'id': 'password',
                                'name': 'password',
                                })
  )
  remember_me = forms.BooleanField(required=False)

  def __init__(self, *args, **kwargs):
    super(LoginForm, self).__init__(*args, **kwargs)
    # Remove username field

    if 'username' in self.fields:
      del self.fields['username']

  def clean(self):
    email = self.cleaned_data.get('email')
    password = self.cleaned_data.get('password')

    # Authenticate using email and password
    if email and password:
      self.user_cache = authenticate(self.request, email=email, password=password)
      if self.user_cache is None:
        raise forms.ValidationError("Invalid email or password")
      else:
        self.confirm_login_allowed(self.user_cache)
    return self.cleaned_data

  class Meta:
    model = User
    fields = ('email', 'password', 'remember_me')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve created a custom login form that includes the following fields: &lt;strong&gt;email&lt;/strong&gt;, &lt;strong&gt;password&lt;/strong&gt;, and &lt;strong&gt;remember_me&lt;/strong&gt;. The &lt;code&gt;remember_me&lt;/code&gt; checkbox allows users to maintain their login session across browser sessions.&lt;/p&gt;

&lt;p&gt;Since our form extends the &lt;code&gt;AuthenticationForm&lt;/code&gt;, we've overridden some default behaviour:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;** &lt;code&gt;__init__&lt;/code&gt; method**: We've removed the default username field from the form to align with our email-based authentication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;clean()&lt;/code&gt; method&lt;/strong&gt;: This method validates the email and password fields. If the credentials are valid, we authenticate the user using Django's built-in authentication mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;confirm_login_allowed()&lt;/code&gt; method&lt;/strong&gt;: This built-in method provides an opportunity for additional verification before login. You can override this method to implement custom checks if needed.
Now our tests should pass:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(.venv)$ python3 manage.py test users.tests.test_forms
Found 9 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.........
----------------------------------------------------------------------
Ran 9 tests in 3.334s
OK
Destroying test database for alias 'default'...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Create our login view
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Create tests for the login view
&lt;/h3&gt;

&lt;p&gt;Since we do not have the view for the login yet, let's navigate to the &lt;code&gt;users/views.py&lt;/code&gt; file and create a new class inheriting from the &lt;code&gt;auth&lt;/code&gt; app’s LoginView&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# -- other code 
from .forms import CustomUserCreationForm, LoginForm
from django.contrib.auth import get_user_model, views
# -- other code

class CustomLoginView(views.LoginForm):


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the bottom of the &lt;code&gt;users/tests/test_views.py&lt;/code&gt; file add these test cases&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/tests/test_views.py

# -- other code

class LoginTests(TestCase):
  def setUp(self):
    User.objects.create_user(
      full_name= 'Tester User',
      email= 'tester@gmail.com',
      bio= 'new bio for tester',
      password= 'password12345'
    )
    self.valid_credentials = {
      'email': 'tester@gmail.com',
      'password': 'password12345',
      'remember_me': False
    }

  def test_login_url(self):
    """User can navigate to the login page"""
    response = self.client.get(reverse('users:login'))
    self.assertEqual(response.status_code, 200)

  def test_login_template(self):
    """Login page render the correct template"""
    response = self.client.get(reverse('users:login'))
    self.assertTemplateUsed(response, template_name='registration/login.html')
    self.assertContains(response, '&amp;lt;a class="btn btn-outline-dark text-white" href="/users/sign_up/"&amp;gt;Sign Up&amp;lt;/a&amp;gt;')

  def test_login_with_valid_credentials(self):
    """User should be log in when enter valid credentials"""
    response = self.client.post(reverse('users:login'), self.valid_credentials, follow=True)
    self.assertEqual(response.status_code, 200)
    self.assertRedirects(response, reverse('home'))
    self.assertTrue(response.context['user'].is_authenticated)
    self.assertContains(response, '&amp;lt;button type="submit" class="btn btn-danger"&amp;gt;&amp;lt;i class="bi bi-door-open-fill"&amp;gt;&amp;lt;/i&amp;gt; Log out&amp;lt;/button&amp;gt;')

  def test_login_with_wrong_credentials(self):
    """Get error message when enter wrong credentials"""
    credentials = {
      'email': 'tester@gmail.com',
      'password': 'wrongpassword',
      'remember_me': False
    }

    response = self.client.post(reverse('users:login'), credentials, follow=True)
    self.assertEqual(response.status_code, 200)
    self.assertContains(response, 'Invalid email or password')
    self.assertFalse(response.context['user'].is_authenticated)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to ensure that these tests are failing at this stage.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Create a login view
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;users/views.py&lt;/code&gt; file at the bottom of the file add the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# -- other code 
from .forms import CustomUserCreationForm, LoginForm
from django.contrib.auth import get_user_model, views
# -- other code

class CustomLoginView(views.LoginView):
  form_class = LoginForm
  redirect_authenticated_user = True
  authentication_form = LoginForm
  template_name = 'registration/login.html'

  def form_valid(self, form):
    remember_me = form.cleaned_data.get('remember_me')

    if not remember_me:
      # set session expiry to 0 seconds. So it will automatically close the session after the browser is closed.
      self.request.session.set_expiry(0)
      # Set session as modified to force data updates/cookie to be saved.
      self.request.session.modified = True
    return super(CustomLoginView, self).form_valid(form)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we accomplish the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;strong&gt;the &lt;code&gt;form_class&lt;/code&gt; Attribute&lt;/strong&gt;: We specify our custom &lt;code&gt;LoginForm&lt;/code&gt; as the &lt;code&gt;form_class&lt;/code&gt; attribute since we are no longer using the default &lt;code&gt;AuthenticationForm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Override the &lt;code&gt;form_valid&lt;/code&gt; Method&lt;/strong&gt;: We override the &lt;code&gt;form_valid&lt;/code&gt; method, which is called when valid form data has been posted. This allows us to implement custom behaviour after the user has successfully logged in.&lt;/li&gt;
&lt;li&gt;Handle Session Expiration: If the user does not check the &lt;code&gt;remember_me&lt;/code&gt; box, the session will expire automatically when the browser is closed. However, if the &lt;code&gt;remember_me&lt;/code&gt; box is checked, the session will last for the duration defined in &lt;code&gt;settings.py&lt;/code&gt;. The default session length is two weeks, but we can modify this using the &lt;code&gt;SESSION_COOKIE_AGE&lt;/code&gt; variable in &lt;code&gt;settings.py&lt;/code&gt;. For example, to set the cookie age to 7 days, we can add the following line to our settings:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# blog_app/settings.py
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To connect your custom login functionality and allow users to access the login page, we’ll define URL patterns in the &lt;code&gt;users/urls.py&lt;/code&gt; file. This file will map specific URLs (&lt;code&gt;/log_in/&lt;/code&gt; in this case) to the corresponding views (&lt;code&gt;CustomLoginView&lt;/code&gt;). Additionally, we'll include a path for the logout functionality using Django's built-in &lt;code&gt;LogoutView&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/urls.py

# -- other code
from django.contrib.auth import views as auth_views
from . import views

app_name = 'users'
urlpatterns = [
  path('log_in/', views.CustomLoginView.as_view(), name='login' ), # new line
  path('sign_up/', views.SignUpView.as_view(), name='signup'),
  path('log_out/', auth_views.LogoutView.as_view(), name='logout'),# new line
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything seems to be in order, but we should specify where to redirect users upon successful login and logout. To do this, we will use the &lt;code&gt;LOGIN_REDIRECT_URL&lt;/code&gt; and &lt;code&gt;LOGOUT_REDIRECT_URL&lt;/code&gt; settings. At the bottom of your &lt;code&gt;blog_app/settings.py&lt;/code&gt; file, add the following lines to redirect users to the homepage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# django_project/settings.py
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the login URL let’s update our &lt;code&gt;SignUpView&lt;/code&gt; in the &lt;code&gt;users/views.py&lt;/code&gt; file to redirect to the login page when sign-up is successful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/views.py
class SignUpView(CreateView):
  form_class = CustomUserCreationForm
  model = User
  success_url = reverse_lazy('users:login') # Updated line
  template_name = 'registration/signup.html'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also update our  &lt;code&gt;SignUpTexts&lt;/code&gt;, specifically the &lt;code&gt;test_signup_correct_data(self)&lt;/code&gt;, to reflect the new behaviour and ensure that our changes are properly tested.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/tests/test_views.py
  def test_signup_correct_data(self):
    """User should be saved when a correct data is provided"""
    response = self.client.post(reverse('users:signup'), data={
      'full_name': self.full_name,
      'email': self.email,
      'bio': self.bio,
      'password1': self.password,
      'password2': self.password
    })

    self.assertRedirects(response, reverse('users:login')) # Updated line
    users = User.objects.all()
    self.assertEqual(users.count(), 1)
    self.assertNotEqual(users[0].password, self.password)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.3 Create a template for Login
&lt;/h3&gt;

&lt;p&gt;Then create a users/templates/registration/login.html file with your text editor and include the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% extends 'layout.html' %}
{% block page %}
  Login
{% endblock %}
{% block content %}
&amp;lt;div class="container mt-3"&amp;gt;
  &amp;lt;div class="row justify-content-center"&amp;gt;
    &amp;lt;div class="col-lg-5"&amp;gt;
      &amp;lt;div class="card shadow-lg border-0 rounded-lg mt-3 mb-3"&amp;gt;
        &amp;lt;div class="card-header justify-content-center"&amp;gt;
          &amp;lt;h3 class="font-weight-light my-1 text-center"&amp;gt;Sign In&amp;lt;/h3&amp;gt;
        &amp;lt;/div&amp;gt;
        {% if form.errors %}
          {% for field, message in form.errors.items %}
            &amp;lt;div class="alert alert-danger alert-dismissible fade show" role="alert"&amp;gt;
              {{message|first}}
              &amp;lt;button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"&amp;gt;&amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
          {% endfor %}
        {% endif %}
        &amp;lt;form method="POST" class="card-body"&amp;gt;
          &amp;lt;div class="mb-3"&amp;gt;
            &amp;lt;label for="id_email" class="form-label"&amp;gt;Email address&amp;lt;/label&amp;gt;
            {{form.email}}
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="mb-3"&amp;gt;
            &amp;lt;label for="password" class="form-label"&amp;gt;Password&amp;lt;/label&amp;gt;
            {{form.password}}
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="mb-3"&amp;gt;
            {{form.remember_me}}
            &amp;lt;label&amp;gt;Remember me&amp;lt;/label&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="mb-3"&amp;gt;
            &amp;lt;button name="login" class="col-md-12 btn bg-secondary bg-gradient text-white"&amp;gt;Sign in&amp;lt;/button&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/form&amp;gt;
        &amp;lt;div class="card-footer text-center"&amp;gt;
          &amp;lt;div class="small"&amp;gt;
            &amp;lt;a href="{% url 'users:signup' %}"&amp;gt;Don't have an account yet? Go to signup&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;
            &amp;lt;a href="#"&amp;gt;&amp;lt;i&amp;gt;Forgot Password?&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will add the Forgot Password functionality later in this series but now it’s just a dead link.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg81rag1j0gelgpedf4vy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg81rag1j0gelgpedf4vy.png" alt="Login page" width="564" height="537"&gt;&lt;/a&gt;&lt;br&gt;
Now, let us update our layout.html template to include the login, sign-up and logout links.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset="UTF-8"&amp;gt;
  &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
  &amp;lt;title&amp;gt;{% block page %}{% endblock %} | Blog App&amp;lt;/title&amp;gt;
  &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"&amp;gt;
  &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet"&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;nav class="navbar navbar-expand navbar-dark bg-primary bg-gradient"&amp;gt;
    &amp;lt;div class="container-fluid"&amp;gt;
      &amp;lt;a class="navbar-brand" href="{% url 'home' %}"&amp;gt;Blog App&amp;lt;/a&amp;gt;
      &amp;lt;button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarScroll" aria-controls="navbarScroll" aria-expanded="false" aria-label="Toggle navigation"&amp;gt;
        &amp;lt;span class="navbar-toggler-icon"&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;/button&amp;gt;
      &amp;lt;div class="collapse navbar-collapse" id="navbarScroll"&amp;gt;
        &amp;lt;ul class="navbar-nav me-auto my-2 my-lg-0 navbar-nav-scroll" style="--bs-scroll-height: 100px;"&amp;gt;
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;a class="nav-link active" aria-current="page" href="{% url 'home' %}"&amp;gt;Home&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;a class="nav-link" href="{% url 'about' %}"&amp;gt;About&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
        {% if user.is_authenticated %}
        &amp;lt;ul class="navbar-nav"&amp;gt;
          &amp;lt;li class="nav-item me-3" style="cursor: pointer"&amp;gt;
            &amp;lt;i class="bi bi-person-circle fs-2 text-white" title="{{user.full_name}}"&amp;gt;&amp;lt;/i&amp;gt;
          &amp;lt;/li&amp;gt;
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;form class="mt-1" action="{% url 'users:logout' %}" method="post"&amp;gt;
              {% csrf_token %}
              &amp;lt;button type="submit" class="btn btn-danger"&amp;gt;&amp;lt;i class="bi bi-door-open-fill"&amp;gt;&amp;lt;/i&amp;gt; Log out&amp;lt;/button&amp;gt;
            &amp;lt;/form&amp;gt;
          &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
        {% else %}
        &amp;lt;ul class="navbar-nav"&amp;gt;
          &amp;lt;li class="nav-item me-2"&amp;gt;
            &amp;lt;a class="btn btn-outline-dark text-white" href="{% url 'users:signup' %}"&amp;gt;Sign Up&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;a class="btn btn-outline-dark text-white" href="{% url 'users:login' %}"&amp;gt;Sign In&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
        {% endif %}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/nav&amp;gt;
  {% block content %}
  {% endblock %}
  &amp;lt;footer&amp;gt;&amp;lt;/footer&amp;gt;
  &amp;lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our template, we check whether the user is authenticated. If the user is logged in, we display the log-out link and the user's full name. Otherwise, we show the sign-in and sign-up links.&lt;br&gt;
Now let's run all the tests&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(.venv)$ python3 manage.py test
Found 22 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
......................
----------------------------------------------------------------------
Ran 22 tests in 9.942s
OK
Destroying test database for alias 'default'...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Test if everything is working as it should in our browser
&lt;/h3&gt;

&lt;p&gt;Now that we've configured the login and logout functionality, it's time to test everything in our web browser. Let's start the development server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(.venv)$ python3 manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the registration page and enter valid credentials. After a successful registration, you should be redirected to the login page. Enter the user information in the login form, and once logged in, click the logout button. You should then be logged out and redirected to the homepage. Finally, verify that you're no longer logged in and that the sign-up and sign-in links are displayed again.&lt;br&gt;
Everything works perfectly, but I noticed that when a user is logged in and visits the registration page at &lt;a href="http://127.0.0.1:8000/users/sign_up/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/users/sign_up/&lt;/a&gt;, they still have access to the registration form. Ideally, once a user is logged in, they shouldn't be able to access the sign-up page.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75i8xercbbllqbraovul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75i8xercbbllqbraovul.png" alt="Description page" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
This behaviour can introduce several security vulnerabilities into our project. To address this, we need to update the &lt;code&gt;SignUpView&lt;/code&gt; to redirect any logged-in user to the home page.&lt;br&gt;
But first, let's update our LoginTest to add a new test that covers the scenario. So in the users/tests/test_views.py add this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/tests/test_views.py
class LoginTests(TestCase):
  # -- other test cases

  def test_visiting_registration_after_logged_in(self):
    """
    Logged in user should be redirected when visiting the registration page
    """
    response = self.client.post(reverse('users:login'), self.valid_credentials, follow=True)
    self.assertTrue(response.context['user'].is_authenticated)

    sign_up_resp = self.client.get(reverse('users:signup'))
    self.assertRedirects(sign_up_resp, reverse('home'))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can update our SignUpView&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/views.py
from django.conf import settings # new line
...

class SignUpView(CreateView):
  form_class = CustomUserCreationForm
  redirect_authenticated_user = True
  model = User
  success_url = reverse_lazy('users:login')
  template_name = 'registration/signup.html'

  def dispatch(self, request, *args, **kwargs):
    # Check if a user is already authenticated
    if request.user.is_authenticated:
      # Redirect the user to the login redirect url
      return redirect(f'{settings.LOGIN_REDIRECT_URL}')
    return super().dispatch(request, *args, **kwargs)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we override the &lt;code&gt;dispatch()&lt;/code&gt; method of our &lt;code&gt;SignUpView&lt;/code&gt; to redirect any user who is already logged in and tries to access the registration page. This redirect will use the &lt;code&gt;LOGIN_REDIRECT_URL&lt;/code&gt; set in our &lt;code&gt;settings.py&lt;/code&gt; file, which in this case, points to the home page.&lt;br&gt;
Okay! Once again, let's run all our tests to confirm that our updates are working as expected&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(.venv)$ python3 manage.py test
Found 23 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.......................
----------------------------------------------------------------------
Ran 23 tests in 11.215s

OK
Destroying test database for alias 'default'...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know there's much more to accomplish, but let's take a moment to appreciate what we've accomplished so far. Together, we've set up our project environment, connected a PostgreSQL database, and implemented a secure user registration and login system for our Django blog application. In the next part, we'll dive into creating a user profile page, enabling users to edit their information, and password reset! Stay tuned for more exciting developments as we continue our Django blog app journey!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Your feedback is always valued. Please share your thoughts, questions, or suggestions in the comments below. Don't forget to like, leave a comment, and subscribe to stay updated on the latest developments!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>postgressql</category>
      <category>testing</category>
    </item>
    <item>
      <title>Guide to Building a Complete Blog App with Django using TDD Methodology and PostgreSQL (Part 2): User registration</title>
      <dc:creator>AVLESSI Matchoudi</dc:creator>
      <pubDate>Thu, 17 Oct 2024 10:23:04 +0000</pubDate>
      <link>https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-part-2-13ok</link>
      <guid>https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-part-2-13ok</guid>
      <description>&lt;p&gt;Welcome back, everyone! This article is a continuation of the guide on building a complete blog app with Django. If you haven’t read the &lt;a href="https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-database-installation-and-setup-830"&gt;first part of this series&lt;/a&gt; yet, I recommend checking it out before proceeding. In this part, we’ll dive into implementing authentication functionality, focusing on setting up secure user registration in Django. Stay tuned for the next part, where we’ll continue building out the app.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6n7t3p1bmopk2luut8dh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6n7t3p1bmopk2luut8dh.png" alt="Image description" width="800" height="718"&gt;&lt;/a&gt;&lt;br&gt;
In this series, we are building a complete blog application, guided by the following Entity-Relationship Diagram (ERD). For this time, our focus will be on setting up secure user registration. If you find this content helpful, &lt;strong&gt;please like, comment, and subscribe to stay updated when the next part is released&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this article, I’m using &lt;em&gt;Linux&lt;/em&gt;. For &lt;em&gt;Mac&lt;/em&gt; users, the commands will be the same. If you’re using &lt;em&gt;Windows&lt;/em&gt;, I’ll be providing both the &lt;em&gt;&lt;strong&gt;Linux/Mac&lt;/strong&gt;&lt;/em&gt; and &lt;em&gt;&lt;strong&gt;Windows&lt;/strong&gt;&lt;/em&gt; commands where there are differences, so you can follow along easily regardless of your operating system.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Have a basic understanding of Django&lt;/li&gt;
  &lt;li&gt;Have a basic knowledge of Python&lt;/li&gt;
  &lt;li&gt;PostgreSQL installed on your machine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Okay, let’s get started !!&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            ##########
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;For any authentication system, a User model is essential. That’s why Django’s authentication system includes a default built-in User model. which comes with these primary attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;    username&lt;/li&gt;
&lt;li&gt;    password&lt;/li&gt;
&lt;li&gt;    email&lt;/li&gt;
&lt;li&gt;    first_name&lt;/li&gt;
&lt;li&gt;    last_name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, these attributes might not always align with the specific requirements of a project. As in our case, you can notice that they differ from the User attributes in our application’s ERD.&lt;/p&gt;

&lt;p&gt;To address this, Django allows us to override the default User model with a customized one that better suits our application’s needs. This can be achieved through inheritance or by creating a new User model. In this tutorial, we’ll explore the latter approach, where we’ll create a new customised User model that includes attributes specific to our blog application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;While it’s possible to customize Django’s default User model, doing so introduces additional complexity to an already robust system. It’s recommended to stick with the default model whenever possible unless you know what you’re doing or project-specific requirements explicitly demand customization.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we understand what needs to be done, let’s create our first app to implement the custom User model.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Create an app named &lt;code&gt;Users&lt;/code&gt; within our project
&lt;/h2&gt;

&lt;p&gt;Make sure you’re in the same directory as &lt;code&gt;manage.py&lt;/code&gt; and type this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOS/Linux
(.venv)$ python3 manage.py startapp users

# Windows
(.venv)$ python manage.py startapp users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;code&gt;users&lt;/code&gt; directory, which will be structured as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users/
    migrations/
    __init__.py
    admin.py
    apps.py
    models.py
    tests.py
    views.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we will add the newly created Users app to the project’s &lt;code&gt;INSTALLED_APPS&lt;/code&gt; setting located in the &lt;code&gt;blog_app/settings.py&lt;/code&gt; file. This will inform Django that our app is active and ready to be used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# django_project/settings.py
INSTALLED_APPS = [
     # -- other code
    "users.apps.UsersConfig",  # &amp;lt;== new app
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Create test cases for the User model
&lt;/h2&gt;

&lt;p&gt;Django uses the unit test module’s &lt;a href="https://docs.python.org/3/library/unittest.html#unittest-test-discovery" rel="noopener noreferrer"&gt;built-in test discovery&lt;/a&gt; mechanism to automatically find and run tests within your project. It will locate tests in any file named with the pattern &lt;code&gt;test*.py&lt;/code&gt; under the current working directory.&lt;/p&gt;

&lt;p&gt;Unlike the &lt;a href="https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-database-installation-and-setup-830"&gt;first part&lt;/a&gt; of this tutorial, we’ll create a separate &lt;code&gt;tests&lt;/code&gt; module to house our test code as we promised at the beginning of the series to follow the best practices. This organization help to improve code &lt;strong&gt;readability&lt;/strong&gt;, &lt;strong&gt;maintainability&lt;/strong&gt;, and &lt;strong&gt;reusability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We will now create a &lt;code&gt;tests&lt;/code&gt; module in the &lt;code&gt;users&lt;/code&gt; directory for our test cases, with separate files for models, views, forms, and any other components that need testing. These files typically follow the naming &lt;code&gt;conventiontest_&amp;lt;component_name&amp;gt;.py&lt;/code&gt;. Thus, our test directory structure will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users/
  /tests/
    __init__.py
    test_models.py
    test_forms.py
    test_views.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;__init__.py&lt;/code&gt; file should be an empty file. In Python, an empty &lt;code&gt;__init__.py&lt;/code&gt; file signifies that the directory is a package and can contain modules. Next, we'll remove the &lt;code&gt;users/tests.py&lt;/code&gt; file, as we'll be organizing our tests into separate files in the &lt;code&gt;users&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Now that we have a solid understanding of the setup, let’s begin writing our tests for the User model using the TDD methodology. Open the &lt;code&gt;users/tests/test_models.py&lt;/code&gt; file and add the following test cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/tests/test_models.py
from django.test import TestCase
from django.contrib.auth import get_user_model
User = get_user_model()

class UserTest(TestCase):

  def setUp(self):
    self.user_test = {
      'full_name': 'Tester',
      'email': 'test@gmail.com',
      'bio': 'Biography of Tester',
      'password': 'user12345',
    }
    self.saved = self.create_user(self.user_test)

  def create_user(self, data):
    return User.objects.create_user(**data)

  def test_user_creation(self):
    """User creation is successful when all the required field are fill"""
    self.assertTrue(isinstance(self.saved, User))
    self.assertEqual(str(self.saved), self.user_test['email'])

  def test_required_fields(self):
    """Create a user with a empty email or password should raise an Error"""
    fake_user = {
      'full_name': 'fake',
      'email': '',
      'bio': 'fake bio',
      'password': 'user12345'
    }

    with self.assertRaises(TypeError):
      User.objects.create_user()
    with self.assertRaises(TypeError):
      User.objects.create_user(email='tester1@gmail.com')
    with self.assertRaises(ValueError) as context:
      self.create_user(fake_user)
    self.assertEqual(str(context.exception), "The Email must be set")

  def test_password_encripted(self):
    """Saved password in the database should be different from from provided password"""
    self.assertNotEqual(self.saved.password, self.user_test['password'])

  def test_creation_superuser(self):
    super_user = User.objects.create_superuser(email='superuser@gmail.com', password='super12345', bio='User admin')
    self.assertTrue(super_user.is_superuser)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we import the &lt;code&gt;get_user_model&lt;/code&gt; function to retrieve the custom user model that we will define soon. We have also created tests to ensure that a user is created successfully when the correct credentials are provided, check the required fields, verify that the password is encrypted after the user has been saved, and finally, test the creation of a superuser.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;DO NOT apply the migrations. Remember: You must create the custom user model before you apply your first migration.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Create a custom user model
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Create a Model Manager
&lt;/h3&gt;

&lt;p&gt;To ensure email serves as the unique identifier for user authentication, we’ll create a custom &lt;a href="https://docs.djangoproject.com/en/5.1/topics/db/managers/" rel="noopener noreferrer"&gt;Manager&lt;/a&gt;. This can be achieved by subclassing &lt;code&gt;BaseUserManager&lt;/code&gt; from Django. The custom Manager will define the logic for creating and interacting with users in our application.&lt;/p&gt;

&lt;p&gt;Let's create a new file named &lt;code&gt;managers.py&lt;/code&gt; within the &lt;code&gt;users&lt;/code&gt; directory and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/managers.py
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import gettext_lazy as _

class CusmtomUserManager(BaseUserManager):
  """
  Use the email field as the unique identifiers for the authentication
  """
  def create_user(self, email, password, **extra_fields):
    if not email:
      raise ValueError(_("The Email must be set"))
    email = self.normalize_email(email)
    user = self.model(email=email, **extra_fields)
    user.set_password(password)
    user.save()
    return user

  def create_superuser(self, email, password, **extra_fields):
    """
    Create a SuperUser with email and password
    """
    extra_fields.setdefault("is_superuser", True)

    if extra_fields.get("is_superuser") is not True:
      raise ValueError(_("Superuser must have is_superuser=True"))

    return self.create_user(email, password, **extra_fields)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our custom manager model, we have overridden the &lt;code&gt;create_user&lt;/code&gt; and &lt;code&gt;create_superuser&lt;/code&gt; methods. The &lt;code&gt;create_user&lt;/code&gt; method will now accept an email argument instead of a username argument, and the &lt;code&gt;create_superuser&lt;/code&gt; method will also use email instead of a username.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Create a Custom Model
&lt;/h3&gt;

&lt;p&gt;Now, let’s create our User model in the &lt;code&gt;models.py&lt;/code&gt; file within the &lt;code&gt;users&lt;/code&gt; directory to ensure that our previous tests pass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.utils.translation import gettext_lazy as _
from PIL import Image
from .managers import CustomUserManager

class CustomUser(AbstractBaseUser, PermissionsMixin):
  full_name = models.CharField(_('full name'), blank=False, null=False, max=200, verbose_name='Full Name')
  email = models.EmailField(_('email address'), unique=True, max_length=200)
  photo = models.ImageField(upload_to='profiles', blank=True, null=True, verbose_name='Photo')
  bio = models.TextField(blank=True, null=True, verbose_name='Biography')
  posts_counter = models.PositiveIntegerField(default=0, null=False, verbose_name='Posts Counter')
  updated_at = models.DateTimeField(autho_now=True)
  created_at =  models.DateTimeField(auto_now_add=True)

  USERNAME_FIELD = 'email'
  REQUIRED_FIELDS = []

  objects = CustomUserManager()

  def save(self, *args, **kwargs):
    """
    Overwrite the default method save() to resize the profile picture
    before save using Pillow.
    """
    super().save()

    if self.photo:
      img = Image.open(self.photo.path)

      if img.height &amp;gt; 80 or img.width &amp;gt; 80:
        img_size = (80, 80)
        img.thumbnail(img_size)
        img.save(self.photo.path)

  class Meta:
    """
    Set the table name to follow our ERD
    """
    db_table = 'users'

  def __str__(self):
    return self.email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created a new model called &lt;code&gt;CustomUser&lt;/code&gt; that subclasses &lt;code&gt;AbstractBaseUser&lt;/code&gt; which allows us to create our own model attributes&lt;/li&gt;
&lt;li&gt;Added the users' table attributes to our model&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;USERNAME_FIELD&lt;/code&gt; -- which defines the unique identifier for the User model to email&lt;/li&gt;
&lt;li&gt;Specified that all objects for the class come from the &lt;code&gt;CustomUserManager&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resized the profile picture to have 80px as height and 80px as width and saved the pictures in the &lt;code&gt;media/profiles&lt;/code&gt; directory which will be soon set up in the setting.&lt;/li&gt;
&lt;li&gt;Named our table &lt;code&gt;users&lt;/code&gt; to align with the provided ERD.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.3 Settings
&lt;/h3&gt;

&lt;p&gt;We’ll add the following line to our &lt;code&gt;settings.py&lt;/code&gt; file to inform Django to use our custom user model for user authentication instead of the default built-in model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# blog_app/settings.py
AUTH_USER_MODEL = "users.CustomUser"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line sets &lt;code&gt;users.CustomUser&lt;/code&gt; as the active user model within our project. Additionally, we'll configure Django's media storage by adding these lines to settings.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# blog_app/settings.py
import os
...
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MEDIA_ROOT&lt;/strong&gt; specifies the server-side path where uploaded media files in our case our user profile picture will be stored on our computer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MEDIA_URL&lt;/strong&gt; defines the URL prefix that the browser will use to access uploaded media files over HTTP (e.g., &lt;a href="http://yourdomain.com/media/" rel="noopener noreferrer"&gt;http://yourdomain.com/media/&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, to ensure that uploaded pictures are only saved to the media directory during development, we’ll add some code to our &lt;code&gt;blog_app/urls.py&lt;/code&gt; file. This configuration allows the development server to serve media files directly while keeping media serving disabled in production for security and performance reasons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# blog_app/urls.py
# -- other code
from django.conf import settings               # new line
from django.conf.urls.static import static     # new line

urlpatterns = [
    # --  application paths
]

if settings.DEBUG:
  urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # new line
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.4 Run migrations
&lt;/h3&gt;

&lt;p&gt;Now, we can create and apply the migrations, which will create a new database that uses the custom user model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Linux/MacOs
(.venv)$ python3 manage.py makemigrations users

# Windows
(.venv)$ python manage.py makemigrations users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should see something similar to the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Migrations for 'users':
  users/migrations/0001_initial.py
    + Create model CustomUser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run the migration to create a database with the tables but first, let's see what SQL that migration would run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Linux/MacOs
(.venv)$ python3 manage.py sqlmigrate users 0001

# Windows
(.venv)$ python manage.py sqlmigrate users 0001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55q9682ylanif01juu8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55q9682ylanif01juu8v.png" alt="Image description" width="800" height="154"&gt;&lt;/a&gt;&lt;br&gt;
From the output, we can see that Django will create a &lt;code&gt;users&lt;/code&gt; table with all the attributes in our ERD.&lt;br&gt;
Our migration file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Generated by Django 5.1.1 on 2024-10-08 22:54

from django.db import migrations, models

class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ('auth', '0012_alter_user_first_name_max_length'),
    ]

    operations = [
        migrations.CreateModel(
            name='CustomUser',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('password', models.CharField(max_length=128, verbose_name='password')),
                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
                ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
                ('full_name', models.CharField(max_length=200, verbose_name='full name')),
                ('email', models.EmailField(max_length=200, unique=True, verbose_name='email address')),
                ('photo', models.ImageField(blank=True, null=True, upload_to='profiles', verbose_name='Photo')),
                ('bio', models.TextField(blank=True, null=True, verbose_name='Biography')),
                ('posts_counter', models.PositiveIntegerField(default=0, verbose_name='Posts Counter')),
                ('updated_at', models.DateTimeField(auto_now=True)),
                ('created_at', models.DateTimeField(auto_now_add=True)),
                ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
                ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
            ],
            options={
                'db_table': 'users',
            },
        ),
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now, safely run the migration to create our database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Linux/MacOs
(.venv)$ python3 manage.py migrate

# windows
(.venv)$ python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you encounter any errors at this stage, please make sure you've followed all the steps in &lt;a href="https://medium.com/@matchoudiavlessi/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgressql-database-77be39753c1e" rel="noopener noreferrer"&gt;the first part&lt;/a&gt; of this series.&lt;/em&gt;&lt;br&gt;
Now, let's make sure our tests are passing&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Linux/MacOs
(.venv)$ python3 manage.py test

# Windows
(.venv)$ python manage.py test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should be similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Found 6 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
......
----------------------------------------------------------------------
Ran 6 tests in 2.746s

OK
Destroying test database for alias 'default'...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.5 Remove Django built-in admin application (optional)
&lt;/h3&gt;

&lt;p&gt;When we create a new project, Django automatically adds &lt;code&gt;django.contrib.admin&lt;/code&gt; to the &lt;code&gt;INSTALLED_APPS&lt;/code&gt;. While the Admin app is very useful for backend administration, it is not a requirement for our project, and we won't be using it. So to adhere to the YAGNI (You Aren't Gonna Need It) principle in software development, I recommend removing it. This will help keep our project lean and focused on the essential features.&lt;br&gt;
First, remove or comment out &lt;code&gt;django.contrib.admin&lt;/code&gt; from the &lt;code&gt;INSTALLED_APPS&lt;/code&gt; setting in the &lt;code&gt;blog_app/settings.py&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# blog_app/setting.py
# Application definition

INSTALLED_APPS = [
    # 'django.contrib.admin',
    # -- other codes
    'users.apps.UsersConfig',
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, remove or comment out the URL path in the blog_app/urls.py file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# blog_app/urls.py
# from django.contrib import admin
...

urlpatterns = [
    # path('admin/', admin.site.urls),
    ...
]
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Create a Custom form for our model
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Create test
&lt;/h3&gt;

&lt;p&gt;Following our TDD approach, let's create tests for the custom form we'll use for user registration. In the &lt;code&gt;tests&lt;/code&gt; directory, create a new file named &lt;code&gt;test_forms.py&lt;/code&gt; and add these test cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/tests/test_forms.py
from django.test import TestCase
from users.forms import CustomUserCreationForm

class CustomUserCreationFormTest(TestCase):
  def setUp(self):
    self.full_name = 'First Name'
    self.password = 'test12345'
    self.email = 'test@gmail.com'
    self.bio = 'Tester biography'

  def test_email_validation(self):
    """Form should raise error when email is not a valid email"""
    data = {
      'full_name': self.full_name,
      'password1': self.password,
      'password2': self.password,
      'email': 'testegmail.com',
      'bio': self.bio,
    }
    form = CustomUserCreationForm(data)
    self.assertFalse(form.is_valid())
    self.assertEqual(form.errors['email'], ["Enter a valid email address."])

  def test_name_validation(self):
    """Form should raise error when the full name is not first and last name"""
    data = {
      'full_name': 'Fake',
      'password1': self.password,
      'password2': self.password,
      'email': 'testegmail',
      'bio': self.bio,
    }
    form = CustomUserCreationForm(data)
    self.assertFalse(form.is_valid())
    self.assertEqual(form.errors['full_name'], ["Enter a first and last name."])

  def test_password_confirmation(self):
    """Form should raise error when password do not match"""
    data = {
      'full_name': self.full_name,
      'password1': self.password,
      'password2': 'newowkdkdkd',
      'email': self.email,
      'bio': self.bio,
    }
    form = CustomUserCreationForm(data)
    self.assertFalse(form.is_valid())
    self.assertEqual(form.errors['password2'], ["The two password fields didn't match."])

  def test_bio_validation(self):
    """Form should raise error when the bio is less than 4 characters"""
    data = {
      'full_name': self.full_name,
      'password1': self.password,
      'password2': self.password,
      'email': self.email,
      'bio': 'th',
    }
    form = CustomUserCreationForm(data)
    self.assertFalse(form.is_valid())
    self.assertEqual(form.errors['bio'], ["The bio must be at least 4 characters long. Please provide more details."])

  def test_form_validation(self):
    """Form should be valid"""
    data = {
      'full_name': self.full_name,
      'password1': self.password,
      'password2': self.password,
      'email': self.email,
      'bio': self.bio,
    }
    form = CustomUserCreationForm(data)
    self.assertTrue(form.is_valid())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may notice a few error flags in your text editor; that's perfectly normal since we haven't created the form yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Create forms
&lt;/h3&gt;

&lt;p&gt;Django provides default forms &lt;code&gt;UserCreationForm&lt;/code&gt; and &lt;code&gt;UserChangeForm&lt;/code&gt;, for creating and updating user models. Since we're using a custom &lt;code&gt;CustomUser&lt;/code&gt; model, we need to create custom forms that specifically work with our model. To achieve this, we'll create subclasses of &lt;code&gt;UserCreationForm&lt;/code&gt; and &lt;code&gt;UserChangeForm&lt;/code&gt; in a new file called &lt;code&gt;forms.py&lt;/code&gt; within the &lt;code&gt;users&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from django.contrib.auth import get_user_model
from .models import CustomUser
from django import forms
User = get_user_model()
import re

class CustomUserCreationForm(UserCreationForm):
  """
  Set the password fields to the default messages
  """
  full_name = forms.CharField(
    widget=forms.TextInput(attrs={'placeholder': 'Enter Full name', 'class': 'form-control'})
  )
  email = forms.CharField(
    widget=forms.TextInput(attrs={'placeholder': 'Enter email', 'class': 'form-control'})
  )
  bio = forms.CharField(
    widget=forms.Textarea(attrs={'placeholder': 'Enter author biography', 'class': 'form-control', 'rows': 5})
  )
  password1 = forms.CharField(
    label='Password', 
    widget=forms.PasswordInput(attrs={'placeholder': 'Enter Password', 'class': 'form-control'})
  )
  password2 = forms.CharField(
    label='Confirm Password', 
    widget=forms.PasswordInput(attrs={'placeholder': 'Confirm Password', 'class': 'form-control'})
  )

  class Meta:
    model = CustomUser
    fields = ('full_name', 'email', 'bio',)

  def clean_full_name(self):
    full_name = self.cleaned_data.get('full_name')
    regex = r"^[a-zA-Z]{2,}(?:\s[a-zA-Z]{2,}(?:-[a-zA-Z]{2,})*)+$"
    if not re.match(regex, full_name):
      raise forms.ValidationError("Enter a first and last name.")
    return full_name

  def clean_email(self):  
    email = self.cleaned_data.get('email') 
    user = User.objects.filter(email=email)
    if user.exists():  
      raise forms.ValidationError("Email Already Exist")  
    return email

  def clean_bio(self):
    bio = self.cleaned_data.get('bio')
    if not len(bio) &amp;gt;= 4:
      raise forms.ValidationError("The bio must be at least 4 characters long. Please provide more details.")
    return bio

class CustomUserChangeForm(UserChangeForm):
  class Meta:
    model = CustomUser
    fields = ('full_name', 'photo', 'bio',)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our custom user creation form, we implement several validation checks to ensure data integrity and prevent invalid user data from being entered.&lt;br&gt;
The &lt;code&gt;clean_full_name&lt;/code&gt; method verifies that a correct full name with both first and last names is entered.&lt;br&gt;
The &lt;code&gt;clean_bio&lt;/code&gt; method ensures that the user's biography is at least 4 characters long.&lt;br&gt;
Finally, the &lt;code&gt;clean_email&lt;/code&gt; method checks if the provided email address is not already present in our database to prevent duplicate email addresses from being registered.&lt;/p&gt;

&lt;p&gt;Now, When we run our test we should see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(.venv)$ python3 manager.py test
Found 11 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...........
----------------------------------------------------------------------
Ran 11 tests in 2.918s

OK
Destroying test database for alias 'default'...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Create views for the model
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Create test cases for the views
&lt;/h3&gt;

&lt;p&gt;Navigate to the &lt;code&gt;users/tests&lt;/code&gt; directory and create a new file named &lt;code&gt;test_views.py&lt;/code&gt;. This file will house the tests for our custom user views.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/tests/test_views.py
from django.test import TestCase
from django.urls import reverse
from django.contrib.auth import get_user_model
User = get_user_model()

class SignUpPageTests(TestCase):
  def setUp(self) -&amp;gt; None:
    self.full_name = 'test user'
    self.email = 'testuser@email.com'
    self.bio = 'test user bio'
    self.password = 'fake12345'

  def test_signup_page_view(self):
    """The Signup url should render the 'registration/signup.html' template"""
    response = self.client.get(reverse('users:signup'))
    self.assertEqual(response.status_code, 200)
    self.assertTemplateUsed(response, template_name='registration/signup.html')

  def test_signup_correct_data(self):
    """User should be saved when a correct data is provided"""
    response = self.client.post(reverse('users:signup'), data={
      'name': self.full_name,
      'email': self.email,
      'bio': self.bio,
      'password1': self.password,
      'password2': self.password
    })

    users = User.objects.all()
    self.assertEqual(users.count(), 1)
    self.assertNotEqual(users[0].password, self.password)

  def test_signup_fake_data(self):
    """User shouldn't be save with missing email field"""
    response = self.client.post(reverse('users:signup'), data={
      'name': self.full_name,
      'email': '',
      'bio': self.bio,
      'password1': self.password,
      'password2': self.password
    })

    self.assertEqual(response.status_code, 200)
    users = User.objects.all()
    self.assertEqual(users.count(), 0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the tests above will fail; we should see that 14 tests are run, with 3 of them failing.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Create a view for sign-up
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;users&lt;/code&gt; directory open the file named &lt;code&gt;views.py&lt;/code&gt; and add this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/views.py
from django.views.generic import CreateView
from django.urls import reverse_lazy
from .forms import CustomUserCreationForm
from django.contrib.auth import get_user_model
User = get_user_model()

class SignUpView(CreateView):
  form_class = CustomUserCreationForm
  model = User
  success_url = reverse_lazy('home')
  template_name = 'registration/signup.html'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;_ &lt;strong&gt;Model&lt;/strong&gt; is to tell our sign-up view to use our custom model&lt;br&gt;
_ &lt;strong&gt;Success_url&lt;/strong&gt; is to redirect after the operation is successful. We set it to the home page for now&lt;br&gt;
_ &lt;strong&gt;template_name&lt;/strong&gt; as you might already guess is to tell which template should be used&lt;/p&gt;
&lt;h3&gt;
  
  
  6. Create a URL for our view
&lt;/h3&gt;

&lt;p&gt;Create a new file named urls.py in the &lt;code&gt;users&lt;/code&gt; directory to define URLs for user-related functionalities. In this file, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# users/urls.py
from django.urls import path
from . import views # Import views from the current users app

app_name = 'users'
urlpatterns = [
  path('sign_up/', views.SignUpView.as_view(), name='signup'),
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code defines a URL pattern for the signup view (SignUpView). The &lt;code&gt;app_name&lt;/code&gt; is set to 'users' for easier namespacing of URLs within our application.&lt;br&gt;
Then, include the users' URLs in the project's main URLs file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# blog_app/urls.py
from django.urls import path, include # Import the include function

# ... other imports

urlpatterns = [
    # ... other URL patterns
    path('users/', include('users.urls')), # Include users app URLs
]
# ... other code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Create a Signup template
&lt;/h2&gt;

&lt;p&gt;First, create a directory called templates in your &lt;code&gt;users&lt;/code&gt; directory. Within the &lt;code&gt;templates&lt;/code&gt; directory we have just created, create another directory called &lt;code&gt;registration&lt;/code&gt;, and within that create a file called &lt;code&gt;signup.html&lt;/code&gt;.&lt;br&gt;
In other words, our template should be at &lt;code&gt;users/templates/registration/signup.html&lt;/code&gt; and add this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% extends 'layout.html' %}

{% block page %} Register {% endblock page%}
{% block content %}
&amp;lt;div class="container my-3 p-3"&amp;gt;
  &amp;lt;div class="row justify-content-center"&amp;gt;
    &amp;lt;div class="col-lg-6"&amp;gt;
      &amp;lt;div class="card shadow-lg border-0 rounded-lg mt-0 mb-5"&amp;gt;
        &amp;lt;div class="card-header"&amp;gt;
          &amp;lt;h3 class="font-weight-light my-4 text-center"&amp;gt;Create Account&amp;lt;/h3&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;form method="POST" class="row g-3 card-body"&amp;gt;
          {% csrf_token %}
          {% if form.errors %}
            {% for field, message in form.errors.items %}
              &amp;lt;div class="alert alert-danger alert-dismissible fade show" role="alert"&amp;gt;
                &amp;lt;strong&amp;gt;{{field}}:&amp;lt;/strong&amp;gt; {{message|first}}
                &amp;lt;button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"&amp;gt;&amp;lt;/button&amp;gt;
              &amp;lt;/div&amp;gt;
            {% endfor %}
          {% endif %}
          &amp;lt;div class="col-md-6"&amp;gt;
            &amp;lt;label class="form-label"&amp;gt;Email Address&amp;lt;/label&amp;gt;
            {{ form.email }}
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="col-md-6"&amp;gt;
            &amp;lt;label class="form-label"&amp;gt;Full Name&amp;lt;/label&amp;gt;
            {{ form.full_name }}
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="col-12"&amp;gt;
            &amp;lt;label class="form-label"&amp;gt;Biography&amp;lt;/label&amp;gt;
            {{ form.bio }}
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="form-group col-md-6"&amp;gt;
            &amp;lt;label for="password1"&amp;gt;Password&amp;lt;/label&amp;gt;
            &amp;lt;div class="input-group mt-2"&amp;gt;
              {{ form.password1 }}
              &amp;lt;div class="input-group-append"&amp;gt;
                &amp;lt;span class="input-group-text"&amp;gt;
                  &amp;lt;i class="bi bi-eye-slash" id="togglePassword1" style="cursor: pointer;"&amp;gt;&amp;lt;/i&amp;gt;
                &amp;lt;/span&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="form-group col-md-6"&amp;gt;
            &amp;lt;label for="password2"&amp;gt;Confirm Password&amp;lt;/label&amp;gt;
            &amp;lt;div class="input-group mt-2"&amp;gt;
              {{ form.password2 }}
              &amp;lt;div class="input-group-append"&amp;gt;
                &amp;lt;span class="input-group-text"&amp;gt;
                  &amp;lt;i class="bi bi-eye-slash" id="togglePassword2" style="cursor: pointer;"&amp;gt;&amp;lt;/i&amp;gt;
                &amp;lt;/span&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div class="form-group mt-4 mb-0"&amp;gt;
              &amp;lt;button type="submit" class="col-md-12 btn bg-secondary bg-gradient text-white"&amp;gt;Sign Up&amp;lt;/button&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/form&amp;gt;
        &amp;lt;div class="card-footer text-center"&amp;gt;
          &amp;lt;a href="#" class="small"&amp;gt;Have an account? Go to Sign in&amp;lt;/a&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
{% endblock content %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's add Sign up link to our layout in the &lt;code&gt;templates/layout.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset="UTF-8"&amp;gt;
  &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
  &amp;lt;title&amp;gt;{% block page %}{% endblock %} | Blog App&amp;lt;/title&amp;gt;
  &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"&amp;gt;
  &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet"&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;nav class="navbar navbar-expand-lg navbar-dark bg-primary"&amp;gt;
    &amp;lt;div class="container-fluid"&amp;gt;
      &amp;lt;a class="navbar-brand" href="{% url 'home' %}"&amp;gt;Blog App&amp;lt;/a&amp;gt;
      &amp;lt;button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarScroll" aria-controls="navbarScroll" aria-expanded="false" aria-label="Toggle navigation"&amp;gt;
        &amp;lt;span class="navbar-toggler-icon"&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;/button&amp;gt;
      &amp;lt;div class="collapse navbar-collapse" id="navbarScroll"&amp;gt;
        &amp;lt;ul class="navbar-nav me-auto my-2 my-lg-0 navbar-nav-scroll" style="--bs-scroll-height: 100px;"&amp;gt;
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;a class="nav-link active" aria-current="page" href="{% url 'home' %}"&amp;gt;Home&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;a class="nav-link" href="{% url 'about' %}"&amp;gt;About&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;a class="nav-link" href="{% url 'users:signup' %}"&amp;gt;Register&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
        &amp;lt;form class="d-flex"&amp;gt;
          &amp;lt;input class="form-control me-2" type="search" placeholder="Search" aria-label="Search"&amp;gt;
          &amp;lt;button class="btn btn-outline-light" type="submit"&amp;gt;Search&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/nav&amp;gt;
  {% block content %}
  {% endblock %}
  &amp;lt;footer&amp;gt;&amp;lt;/footer&amp;gt;
  &amp;lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We include the bootstrap Icon link in the &lt;code&gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&lt;/code&gt; tag of our &lt;code&gt;layout.hml&lt;/code&gt; file to have the eye icon in our sign-up form.&lt;br&gt;
Now, let's run our tests to make sure their passing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(.venv)$ python3 manager.py test
Found 14 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..............
----------------------------------------------------------------------
Ran 14 tests in 3.644s

OK
Destroying test database for alias 'default'...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's run the server to check what our sign-up form looks like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(.venv)$ python3 manager.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, navigate to the &lt;a href="http://127.0.0.1:8000/users/sign_up/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/users/sign_up/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwow86zs81cudrwr6n8a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwow86zs81cudrwr6n8a.png" alt="Image description" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This looks pretty good, right!! Now, when we register a new user, we are successfully redirected to the homepage. However, this behaviour is not very practical, as you typically want to either automatically log in to the user upon registration or redirect them to the login page. We will discover this in the next part of this tutorial, where we'll delve into implementing the login functionality, as I feel this tutorial is becoming a bit too long.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I haven't gone into much detail for some code snippets, as I believe the code is self-explanatory. I've added comments to explain each line where necessary. However, if you need further clarification or have any questions, please don't hesitate to comment. I'm happy to provide more detailed explanations. Your feedback is always appreciated! Don't forget to like and leave a comment.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-part-3-secure-user-authentication-nkc"&gt;READ NEXT PART&lt;/a&gt;&lt;/p&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>postgressql</category>
      <category>testing</category>
    </item>
    <item>
      <title>Guide to Building a Complete Blog App with Django using TDD methodology and PostgreSQL database: Installation and Setup</title>
      <dc:creator>AVLESSI Matchoudi</dc:creator>
      <pubDate>Sat, 05 Oct 2024 19:02:25 +0000</pubDate>
      <link>https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-database-installation-and-setup-830</link>
      <guid>https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-database-installation-and-setup-830</guid>
      <description>&lt;p&gt;One of the reasons that we love Django is because it comes with many built-in modules that make web development faster than other frameworks. Django takes care of much of the hassle of web development, so we can focus on writing our app without reinventing the wheel. However, some project requirements differ from the default model that Django provides. In this project, we’ll explore how to use a customized user model in Django to better fit specific needs while using the TDD(Test-Driven Development) methodology to ensure our code is reliable and bug-free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qcozfqtbhaxq1b5hap5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qcozfqtbhaxq1b5hap5.png" alt="Image description" width="800" height="702"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this series, we will build a complete blog application following this ERD. In this tutorial, our focus will be on setting up the development environment and installing the required packages. If you find this content helpful, please like, comment, and subscribe to stay updated when the next part is released.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this article, I’m using Linux. For Mac users, the commands will be the same. If you’re using Windows, I’ll be providing both the Linux/Mac and Windows commands where there are differences,so you can follow along easily regardless of your operating system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Have a basic understanding of Django&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Have a basic Knowledge of Python&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PostgreSQL installed on your machine&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Okay, let’s get started !!&lt;/p&gt;

&lt;p&gt;First, let's create a project directory and move to the directory where we want our code to be. I named my directory blog_app_series. You can name yours whatever you want&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ mkdir blog_app_series &amp;amp;&amp;amp; cd blog_app_series&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I- Create a Virtual Environment&lt;/p&gt;

&lt;p&gt;To follow Django's best practices we will create a virtual environment to isolate any dependencies and modules.&lt;/p&gt;

&lt;p&gt;Many tools are available to create a virtual environment in Python, such as &lt;strong&gt;&lt;em&gt;conda&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;virtualenv&lt;/em&gt;&lt;/strong&gt;, and Python’s &lt;em&gt;&lt;strong&gt;venv&lt;/strong&gt;&lt;/em&gt; module. We will create and activate our virtual environment using ‘&lt;strong&gt;venv&lt;/strong&gt;’ since it’s included in Python’s standard library from Python 3.3; therefore no need to install any external tools.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOS/Linux
$ python3 -m venv .venv
$ source .venv/bin/activate

# Windows
$ python -m venv .venv
$ .venv\Scripts\Activate.ps1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above we have created and activated a virtual environment named “.venv”, the naming doesn’t matter it can be named anything (“blog_env”, “myenv”, “venv”); however, there is growing consensus in Python community that “.venv” is the clearest approach.&lt;/p&gt;

&lt;p&gt;If you see (&lt;strong&gt;&lt;em&gt;.venv&lt;/em&gt;&lt;/strong&gt;) prefixed to your command prompt, the virtual environment is active.&lt;/p&gt;

&lt;p&gt;II- Install Django&lt;/p&gt;

&lt;p&gt;Now, let's install Django in the virtual environment that we created.&lt;/p&gt;

&lt;p&gt;Make sure you have the (.venv) prefixed to your command prompt and enter the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOS/Linux
(.venv) $ python3 -m pip install django

# windows
(.venv) $ python -m pip install django
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will install the last version of Django. We can run ‘pip freeze’, to check the Django’s version installed in our virtual environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(.venv)$ pip freeze
asgiref==3.8.1
Django==5.1.1
sqlparse==0.5.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;III- Create our project&lt;/p&gt;

&lt;p&gt;Now that Django is installed, the next step is to create our project using the management command &lt;code&gt;django-admin startproject &amp;lt;project_name&amp;gt; .&lt;/code&gt;. This creates the skeleton of a new Django project.&lt;/p&gt;

&lt;p&gt;Including the period ., at the end of the command, it is optional but tells Django to set up the new project in the current directory instead of creating a new one.&lt;/p&gt;

&lt;p&gt;(.venv)$ django-admin startproject blog_app .&lt;/p&gt;

&lt;p&gt;With the above command, we create a new project called ‘blog_app’. Our file structure will then look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.venv/
blog_app/
    __init__.py
    asgi.py
    settings.py
    urls.py
    wsgi.py
manage.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check if everything worked successfully, let’s run the &lt;strong&gt;runserver&lt;/strong&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOS/Linux
(.venv) $ python3 manage.py runserver
(.venv) $ python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you open your browser to 127.0.0.1:8000 you should see the following screen:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1oz6i4nc01vex7036uhj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1oz6i4nc01vex7036uhj.png" alt="Image description" width="800" height="518"&gt;&lt;/a&gt;&lt;br&gt;
That means we’ve set up everything correctly and can move on to the next step.&lt;/p&gt;
&lt;h2&gt;
  
  
  IV- Setting up Postgres database
&lt;/h2&gt;

&lt;p&gt;By default, Django uses SQLite as the local database. However, this can be easily switched to any of the &lt;a href="https://docs.djangoproject.com/en/5.0/ref/databases/" rel="noopener noreferrer"&gt;supported databases&lt;/a&gt;, such as &lt;strong&gt;PostgreSQL&lt;/strong&gt;, &lt;strong&gt;MariaDB&lt;/strong&gt;, &lt;strong&gt;MySQL&lt;/strong&gt;, or &lt;strong&gt;Oracle&lt;/strong&gt;. For this project, we will be using PostgreSQL, though you are welcome to choose any database that suits your needs. Django’s ORM (Object-Relational Mapping) ensures that the code remains consistent across different database backends, offering flexibility without requiring changes to your application code.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Create a PostgreSQL database for our project
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;let's open our Vs Code (VisualStudio Code). You can use any IDE or Text Editor of your desire&lt;/li&gt;
&lt;li&gt;Then open the Vs Code terminal, because it’s more convenient to run the code in the same terminal&lt;/li&gt;
&lt;li&gt;Activate the virtual environment in Vs Code, if it has not been activated yet.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOS/Linux
$ source .venv/bin/activate

# Windows
env/Scripts/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;let’s create our database on the local machine. For macOS and Linux, you can use the following command:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOs/Linux
$ sudo -i -u postgres
postgres@user:~$ psql
postgres=# CREATE DATABSE blog_app_db;
postgres=# \q # to exit or quit the session
postgres@user:~$ exit # to logout from the postgres user```


In the code above, we switch to the Postgres user and connect to the PostgreSQL database to create a new database named `blog_app_db`
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pu19sjl9tw16aqvltrtv.png)
for Windows users, you can use pgAdmin to create your database.

### 2. Connect the database to our Django application
- Navigate to the `blog_app/settings.py` file and scroll down to the DATABASE configurations.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c7hayvgoppegd3scebt6.png)
- Replace the default SQLite3 database configuration with PostgreSQL settings.



```DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'blog_app_db', 
        'USER': 'postgres',
        'PASSWORD': 'your_db_password',
        'HOST': '127.0.0.1', 
        'PORT': '5432',
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;_NAME&lt;/strong&gt;: The name of the database we previously created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_USER&lt;/strong&gt;: The database username (default is Postgres).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_PASSWORD&lt;/strong&gt;: The password for the database. This can be omitted if no password is set for your local database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_ HOST&lt;/strong&gt;: The database host. This can be omitted on your local machine, as Django will default to localhost or the IP address 127.0.0.1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_ PORT&lt;/strong&gt;: The PostgreSQL port. This can also be omitted on your local machine, as Django will default to port &lt;code&gt;5432&lt;/code&gt; which is the default port for the Postgres database.&lt;br&gt;
Here is my database configuration.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtzujbxj4fhgc5x1yg09.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtzujbxj4fhgc5x1yg09.png" alt="Image description" width="732" height="192"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Finally, we will need to install the PostgreSQL database connector library called “**psycopg2**”, which allows Django to establish a connection with the database.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;(.venv) $ pip install psycopg2&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start the server to make sure everything is working correctly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let’s start the server using the &lt;code&gt;runserver&lt;/code&gt; command to ensure everything is working correctly (please do not run migrations at this stage). If you still see the ‘The install worked successfully! Congratulations!’ page, means everything is set up correctly, and we are good to go.&lt;br&gt;
V- Add a home page and an about page to our application&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a templates directory to store our templates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s create a &lt;code&gt;templates&lt;/code&gt; directory at the root of our project to store our templates.&lt;br&gt;
&lt;code&gt;(.venv) blog_app_series$ mkdir templates&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and then tell Django where to find our templates by updating the TEMPLATES configuration in &lt;code&gt;settings.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# blog_app/settings.py
TEMPLATES = [
    {
        ...
        "DIRS": [BASE_DIR/"templates"],  # new
        ...
    }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create tests for our templates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As stated at the beginning of this tutorial, we will be following the Test-Driven Development (TDD) methodology to ensure our code is reliable and free of bugs. To get started, let’s create the ‘tests.py’ file within the ‘blog_app’ directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# blog_app/tests.py

from django.test import TestCase
from django.urls import reverse

class AppTemplateTests(TestCase):
  def test_home_template(self):
    """Render home.html and response status is 200"""
    response = self.client.get(reverse('home'))
    # Test if response status is ok
    self.assertEqual(response.status_code, 200)
    # Test if the correct template is used
    self.assertTemplateUsed(response, 'home.html')
    # Test if we have an h1 tags with a text
    seff.assertContains(response, '&amp;lt;h1&amp;gt;This the home page&amp;lt;/h1&amp;gt;')

  def test_about_template(self):
    """Render about.html and response status is 200"""
    response = self.client.get(reverse('about'))
    # Test if response status is ok
    self.assertEqual(response.status_code, 200)
    # Test if the correct template is used
    self.assertTemplateUsed(response, 'about.html')
    # Test if we have an h1 tags with a text
    self.assertContains(response, '&amp;lt;h1&amp;gt;This the about page&amp;lt;/h1&amp;gt;')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;let’s run the tests with this command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOs/Linux
(.venv) $ python3 manage.py test

# Windows
(.venv) $ python manage.py test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1680mppg4eul9pxuve3l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1680mppg4eul9pxuve3l.png" alt="Image description" width="662" height="296"&gt;&lt;/a&gt;&lt;br&gt;
From the terminal output, we can see that all our tests are failing, which is what we are expecting at this stage since we have not created the templates yet.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Create the URLS for the home, and about pages
&lt;/h2&gt;

&lt;p&gt;At this stage, we will include both the views and URLs in the &lt;code&gt;blog_app/urls.py&lt;/code&gt; file for simplicity. Let's start by importing &lt;code&gt;TemplateView&lt;/code&gt; at the top of the file and defining a path that renders the appropriate template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# blog_app/urls.py
from django.contrib import admin
from django.urls import path
from django.views.generic import TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html'), name='home'),
    path('about/', TemplateView.as_view(template_name='about.html'), name='about'),
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we’ll create the &lt;code&gt;home.html&lt;/code&gt; and &lt;code&gt;about.html&lt;/code&gt; templates as referenced in the code above. However, to adhere to the DRY (Don't Repeat Yourself) principle, we'll first create a layout template to serve as a wrapper for these pages&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a layout for our application ‘templates/layout.html’ file
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- templates/layout.html --&amp;gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset="UTF-8"&amp;gt;
  &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
  &amp;lt;title&amp;gt;{% block page %}{% endblock %} | Blog App&amp;lt;/title&amp;gt;
  &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;nav class="navbar navbar-expand-lg navbar-dark bg-primary"&amp;gt;
    &amp;lt;div class="container-fluid"&amp;gt;
      &amp;lt;a class="navbar-brand" href="{% url 'home' %}"&amp;gt;Blog App&amp;lt;/a&amp;gt;
      &amp;lt;button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarScroll" aria-controls="navbarScroll" aria-expanded="false" aria-label="Toggle navigation"&amp;gt;
        &amp;lt;span class="navbar-toggler-icon"&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;/button&amp;gt;
      &amp;lt;div class="collapse navbar-collapse" id="navbarScroll"&amp;gt;
        &amp;lt;ul class="navbar-nav me-auto my-2 my-lg-0 navbar-nav-scroll" style="--bs-scroll-height: 100px;"&amp;gt;
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;a class="nav-link active" aria-current="page" href="{% url 'home' %}"&amp;gt;Home&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;a class="nav-link" href="{% url 'about' %}"&amp;gt;About&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
        &amp;lt;form class="d-flex"&amp;gt;
          &amp;lt;input class="form-control me-2" type="search" placeholder="Search" aria-label="Search"&amp;gt;
          &amp;lt;button class="btn btn-outline-light" type="submit"&amp;gt;Search&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/nav&amp;gt;
  {% block content %}
  {% endblock %}
  &amp;lt;footer&amp;gt;&amp;lt;/footer&amp;gt;
  &amp;lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This template defines the structure and the styling of our web application. We use Bootstrap for responsive design and include a navigation bar with two navigation links: ‘Home’ and ‘About’. The links are generated dynamically using Django’s &lt;code&gt;{% url 'home' %}&lt;/code&gt; and &lt;code&gt;{% url 'about' %}&lt;/code&gt; template tags, which refer to the name of the URL patterns defined in the ‘blog_app/urls.py’ file.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;{% block page %}{% endblock %}&lt;/code&gt; is a placeholder for the page name of each page that uses the layout, and the &lt;code&gt;{% block content %}{% endblock %}&lt;/code&gt; is a placeholder for the main content of each page that extends this layout.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Next, navigate to the ‘templates’ directory and create two new files: ‘home.html’ and ‘about.html’.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- templates/home.html --&amp;gt;

{% extends 'layout.html' %}

{% block page %}
  Home
{% endblock %}

{% block content %}
  &amp;lt;h1&amp;gt;This the home page&amp;lt;/h1&amp;gt;
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- templates/about.html --&amp;gt;

{% extends 'layout.html' %}

{% block page %}
  About
{% endblock %}

{% block content %}
  &amp;lt;h1&amp;gt;This the about page&amp;lt;/h1&amp;gt;
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both ‘home.html’, and ‘about.html’ templates inherit from the ‘layout.html’ template and customize only the page title and content block.&lt;/p&gt;

&lt;p&gt;Now let’s run the tests we created previously to confirm that all our pages are rendering correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOs/Linux
(.venv) $ python3 manage.py test

# Windows
(.venv) $ python manage.py test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ke01te50yxufoiog30q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ke01te50yxufoiog30q.png" alt="Image description" width="708" height="198"&gt;&lt;/a&gt;&lt;br&gt;
All the tests are successful, but we can still check the results in our browser by running our server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# macOS/Linux
(.venv) $ python manage.py runserver

# Windows
(.venv) $ python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if you open your browser and navigate to 127.0.0.1:8000, you should see the text “This the home page”&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv0igsdv30c3sckg6a9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv0igsdv30c3sckg6a9q.png" alt="Image description" width="800" height="173"&gt;&lt;/a&gt;&lt;br&gt;
and when you navigate to the About page, you should see the text “This the about page”&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkem2vhlk0kgnooyavh6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkem2vhlk0kgnooyavh6x.png" alt="Image description" width="800" height="173"&gt;&lt;/a&gt;&lt;br&gt;
This article has covered a lot of ground, so we’ll wrap it up for now to keep things concise. In the next part, we’ll continue by implementing the authentication functionality, where I’ll guide you through setting up secure user login and registration with Django. Stay tuned for the next part.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;** If you found this helpful, don’t forget to like and leave a comment. Your feedback is always appreciated!**&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/lionrouge1/guide-to-building-a-complete-blog-app-with-django-using-tdd-methodology-and-postgresql-database-installation-and-setup-830"&gt;READ THE NEXT PART HERE&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>django</category>
      <category>postgres</category>
      <category>tdd</category>
    </item>
  </channel>
</rss>
