Creating Our First App

Accounts for Nounews

For our project, we plan to at least have some people registered even if not as users, as people who can create content for our site. We will therefore implement an accounts app, which will take care of our user management. If later on we want our users to register to read our news, we could do that.


Now, let's get to code. Get back to your command line and do CTRL + C to stop the server running if it's still running.If you need to start your command line again, here are the steps


Windows

 At any point in time, if you want to understand your file structure,type "dir" (without the quotes) in the command line, it will list your folders and files and you will know where you are currently in your directories

At any point in time if you want to open your command line, Type cmd in your start menu and click on the command prompt when it pops up.


Then type:

cd Desktop (cd for change directory, This will take you to your desktop directory in the command prompt)


Then type:

myenv\Scripts\activate (To activate your virtual environment)


Then type:

cd Nounews (This will take you into the actual django project we created)

Now enter the following command to create a new application called accounts

python manage.py startapp accounts


Linux 

At any point in time, if you want to understand your file structure, type "ls" (without the quotes) in the terminal, it will list your folders and you will know where you are currently in your directories.

At any point in time ctrl + alt + t will open your terminal


Then type:

cd Desktop

Then type: 

source myenv/bin/activate (To activate your virtual environment)

Then type:

cd Nounews(This will take you into the actual django project we created)


Now enter the following command to create a new application called accounts

python3 manage.py startapp accounts

Everyone

With just that command, django has generated a new folder within your project root folder called accounts and within this new folder will be the following files and folder:

__init__.py

Another empty file required in our accounts folder to make the accounts folder importable in other parts of the application



admin.py

This will be used to control the admin part of our app on the admin site which currently run on our https://our_domain/admin



apps.py

A file you would not need to worry about, will contain the name of our app for migration purposes which happen under the hood, all taken care of by django.


models.py

This file will be used to define our database table structures all in the form of simple python classes. Django will handle the rest.


tests.py

This file is used to run tests on our application, we might or might not run any unit tests. We will still however test the project by it's use cases.


views.py

This may become our most edited file, we shall use this to control what is displayed to our users. This is where the business logic of our app will happen. Every work load will be done here, calculations, and other stuffs that need to be done before we show anything to our users will be done here.


urls.py

This file may not have been automatically generated by django for you, but we will create it ourselves in this folder to listen to requests that come in specifically to this part of the project. So basically requests that come in on http://domain/accounts/ will be listened to from here. Go ahead and create this file in the folder right now. It is our listener to any request made by our users. Create the file and name it urls.py


forms.py

Go ahead and create this file also, we shall be using it to define our HTML forms especially for our administration site.


Let's Start with User Accounts

Now, to begin with actually editing code, please open the project's folder in your favourite IDE, I will be using Visual Studio Code, you could download it too, it's light-weight and very elegant to work with. Or if you have pycharm, then open your project in it.

Look for the accounts folder, and look for models.py file inside the accounts folder. 

import Django's AbstractUser model so that we can override it. There's already an import in there like this:

from django.db import models

# Create your models here.


Add these lines so that your file now looks like this

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


# Create your models here.
class CustomUser(AbstractUser):
    pass

So, what are we doing here?

We imported django's AbstractUser model and then wrote a new class called CustomUser, we then made Django's AbstractUser model a parent class to our new class. This is because Django's AbstractUser model contains almost everything that a user model needs: username, password, email, first_name, last_name,... and so on. The ones that are not available we can simply add in place of the pass keyword there. We have the ability to even override certain attributes on the go. 

For example, if the email field is not null, meaning the user must always provide an email field for our database to validate and we do not want that, we could simply re-define the email field and set it's null to True.

We however used the keyword pass above to show that we want to retain all the definitions of the AbstractUser model for our CustomUser model for now. We can however customize later on when the need arises.

Changing Django's User Model

Django inherently knows that its own user model is the authentication user model; meaning django's user model is the default user model that is used to authenticate a user, but now that we want to have our own user model called CustomUser model, we would tell the project about it.

Open settings.py file; it is located in the folder named Nounews which is inside the root folder. Now add this line of code which will tell Django about our new user model.

AUTH_USER_MODEL = 'accounts.CustomUser'

Place it at the bottom of the settings.py file, it tells django to make CustomUser from accounts as our new AUTH_USER_MODEL.

What the Django Docs says about Custom User Models

Changing to a custom user model mid-project

Changing AUTH_USER_MODEL after you’ve created database tables is significantly more difficult since it affects foreign keys and many-to-many relationships, for example.

This change can’t be done automatically and requires manually fixing your schema, moving your data from the old user table, and possibly manually reapplying some migrations. See #25313 for an outline of the steps.

Due to limitations of Django’s dynamic dependency feature for swappable models, the model referenced by AUTH_USER_MODEL must be created in the first migration of its app (usually called 0001_initial); otherwise, you’ll have dependency issues.

In addition, you may run into a CircularDependencyError when running your migrations as Django won’t be able to automatically break the dependency loop due to the dynamic dependency. If you see this error, you should break the loop by moving the models depended on by your user model into a second migration. (You can try making two normal models that have a ForeignKey to each other and seeing how makemigrations resolves that circular dependency if you want to see how it’s usually done.)


The above is the reason why we need to do this first before we even run our first migrations.

Registering our application

We created a new application, but we need to tell the project about it, it doesn't just know that there is a new application until we register the application. In the file you just edited, settings.py, there is a line called INSTALLED_APPS = ... .Now, we need to add accounts in the list.

This is how it currently looks:

INSTALLED_APPS = [
    'django.contrib.admin',


    'django.contrib.auth',


    'django.contrib.contenttypes',


    'django.contrib.sessions',


    'django.contrib.messages',


    'django.contrib.staticfiles',


]

We will now edit it to look like this:

INSTALLED_APPS = [
    'django.contrib.admin',


    'django.contrib.auth',


    'django.contrib.contenttypes',


    'django.contrib.sessions',


    'django.contrib.messages',


    'django.contrib.staticfiles',


    'accounts',
]

Migrations

We can now peacefully effect this changes to our database. Django comes with a very easy database management tool called migrations. With this, your classes from models.py file are turned into sql tables with class attributes becoming the tables' columns. So, even though our CustomUser model does not have any fields now, we know that it inherited fields from AbstractUser model. The migration process is as follows:

We first makemigrations; this process is used by django to generate the commands it will use to actually execute our sql statements. Django will therefore create a migrations folder in  our application folder and migration files containing the commands to be executed at each point will be in this migration folder.

Secondly and finally we migrate; this process is used by django to actually create, modify, or delete our tables as defined in our models.py file.

So let's do these, go to your command line or terminal and run the following commands:

Windows

Type:

python manage.py makemigrations accounts

After which you will see:

Migrations for 'accounts':
  accounts/migrations/0001_initial.py
    - Create model CustomUser
Type:
python manage.py migrate accounts

After which you will see this:

Operations to perform:
  Apply all migrations: accounts
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0001_initial... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying accounts.0001_initial... OK

Now type:

python manage.py migrate

After which you see:

Operations to perform:
  Apply all migrations: accounts, admin, auth, contenttypes, sessions
Running migrations:
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying sessions.0001_initial... OK


Linux

Type:

python3 manage.py makemigrations accounts

After which you will see:

Migrations for 'accounts':
  accounts/migrations/0001_initial.py
    - Create model CustomUser
Type:
python3 manage.py migrate accounts

After which you will see this:

Operations to perform:
  Apply all migrations: accounts
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0001_initial... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying accounts.0001_initial... OK

Now type:

python3 manage.py migrate

After which you see:

Operations to perform:
  Apply all migrations: accounts, admin, auth, contenttypes, sessions
Running migrations:
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying sessions.0001_initial... OK

This is a special case, we migrated for accounts separately first in order to effect the changes we made to the User model and then we migrated the whole project to take care of any remaining migrations project wide. So, if you want  to migrate for a specific application and not the whole project, you add the name of the application.

Creating our first user

Now, if we run the server and tried going to our administration site, the site will ask for credentials we do not have, we have actually been locked out of our own project. So, we need to create a superuser who can login and then start adding other users and managing the site. To create that first user:

python3 manage.py createsuperuser

You will see the following, please while typing the password, it will not show anything, just know what you are typing

Username: TheoElia
Email address: theoeligh@gmail.com
Password: 
Password (again): 
Superuser created successfully.

It will ask you for a username, email and then password (twice). After successfully creating this user, please run your server like this:

python manage.py runserver

After which you should see something like this:

Watching for file changes with StatReloader
Performing system checks...


System check identified no issues (0 silenced).
December 17, 2019 - 08:55:22
Django version 2.2.3, using settings 'Nounews.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.


By now you should have noticed the command we are using to run everything, python manage.py, manage.py is a file located in the root folder of our project and also you may have noticed that python is called for Windows users but python3 is called for Linux users.

Administration site

Now that we have created the user, open your browser or another tab and go to http://127.0.0.1:8000/admin, login with the credentials you gave while creating the user. You should see a full administration site, ready for you.

CONGRATULATIONS !!!


You have successfully created an app, created a custom user management model, registered it and created yourself as a superuser of the site. These are the harder parts, the rest will be fun.

Next we will be adding our own homepage, looking at source control with git and solving some issues