Make a Simple Employee managing app in Django with Class Based Views (With Login, Logout)
Michael_Maranan
Posted on April 23, 2022
A few months ago when I'm learning about Django class-based views, I made an app where I practice my Django skills using classes. Now, I'm here to share it in you.
Setup
Requirements
django - pip intsall django
- Make a DIR with a virtual environment if you want
- Run
django-admin startproject projectname .
- Run
python manage.py startapp appname
- Add app dependency in project
-I choose to name my projectname
as my_project
and appname
as the_app
. my_project
and the_app
is what we're gonna use for this one.
# /my_project/settings.py
INSTALLED_APPS = [
...,
'the_app.apps.TheAppconfig',
]
- Making app's urls.py (optional)
-You can use the project's
urls.py
but you can make another for the app to separate it from the URLs of other apps you may add in the future. A. Create aurls.py
insidethe_app
directory or what we havethe_app
, and then; ```python
/the_app/urls.py
from django.urls import path
urlpatterns = [
path('chosen url', function(), name=None), # Function-based syntax
path('chosen url', class.as_view(), name=None), # Class based syntax
]
B. Connect `my_project/urls.py` to `the_app/urls.py`
```python
# /my_project/urls.py
...
from django.urls import path, include
urlpatterns = [
...,
path('', include('the_app.urls')),
]
Tip: Try to run it by
python manage.py runserver
and go tolocalhost:8000
or127.0.0.1:8000
to see if it's working.
-
Make a Directory for templates
-Make a folder insidethe_app
namedtemplates
, and inside it, make a folder namedthe_app
. That's where we place our templates. If you look at its order, it looks like this:-/the_app/templates/the_app$ --OR-- the_app templates the_app my_project manage.py
***
## Making a Model
- Django Models are used to make tables and what they should contain (data types like string, picture, etc.).
-Now we're making a _Person()_ model where every manager can save there the people they were in charge of. And each `Person` has a task so we also gonna make a _Task()_ model.
```python
# /the_app/models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Person(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=200, null=True)
def __str__(self):
return self.name
class Task(models.Model):
peep = models.ForeignKey(Person, on_delete=models.CASCADE)
task = models.CharField(max_length=200, null=True)
is_complete = models.BooleanField(default=False)
def __str__(self):
return self.task
-If we look at the first argument of the Task() model we'll see that it takes the People() model as its ForeignKey, while on the other hand, the ForeignKey of People() model is the User() model. It means for every Task
created will be saved to the selected People
which will be saved on whoseever Manager/User
is logged in.
Working with the Views
In making a Django app, you can choose what to use between Function-Based Views and Class-Based Views, and we're gonna use the Class-Based Views (CBV).
I. UserSignUp() (FormView)
The FormView
class allows you to make new users. We're gonna make a UserSignup() view using the Formview
class by inheriting it.
# /the_app/views.py
from django.views.generic.edit import FormView
from django.contrib.auth.forms import UserCreationForm
class UserSignup(FormView):
template_name = 'the_app/signup.html'
form_class = UserCreationForm
redirect_authenticated_user = True
-This is how you do a simple UserSignup() view. It will work but if you want to add some functionalities for it to add another option to make the job done, it's totally fine to do it. Now let's add form_valid() for automatic logged in once an account is created.
# /the_app/views.py
class UserSignup(FormView):
...
success_url = reverse_lazy('login')
def form_valid(self, form):
user = form.save()
if user is not None:
login(self.request, user)
return super(UserSignup, self).form_valid(form)
-In Django, you can't name the class functions by random. Instead, they are HTTP calls. When you were using your app with that class, it continues the operation until the requirements you fill in was satisfied, and then it makes a call. If you had a class function with the same name as that call, it will use that function. In our situation here in UserSignup(), we just need to provide the information the form_class
wants. The form_class
contains the UserCreationForm
which default asks for a username and password. Once you fulfill them, the UserCreationForm
calls form_valid
, and there is where our form_valid() function will be called.
-What we're gonna add this time is a function that disallows you to register a new account while one is still logged in.
# /the_app/views.py
...
from django.shortcuts import redirect
class UserSignup(FormView):
...
def form_valid(self, form):
...
def get(self, *args, **kwargs):
if self.request.user.is_authenticated:
return redirect('personlist')
return super(UserSignup, self).get(*args, **kwargs)
-Our UserSignup() should look like this:
-Now let's make a page for it. Make signup.html
inside the_app/templates/the_app:
-After that, let's make a URL for UserSignup() in urls.py:
# /the_app/urls.py
...
from .views import UserSignup
urlpatterns = [
path('signup/', UserSignup.as_view(), name='signup'),
]
II. UserLogin (LoginView)
LoginView
is used for signing in/logging in to the User accounts.
# /the_app/views.py
...
from django.contrib.auth.views import LoginView
from django.urls import reverse_lazy
class UserLogin(LoginView):
template_name = 'the_app/signin.html'
fields = '__all__'
redirect_authenticated_user = True
def get_success_url(self):
return reverse_lazy('personlist')
-As if you notice, the get_success_url()
function says reverse_lazy
to a URL named 'personlist'
. That means when logged in successfully (get_success_url call)
, head to the personlist
URL name to access the PersonList() class. For now, we don't have a PersonList() class but we'll make one later.
-We can make a signin.html
now. Remember to place it inside the the_app/templates/the_app/
.
-And don't forget to link it in urls.py
:
# /the_app/urls.py
...
from .views import UserLogin
urlpatterns = [
...,
path('login/', UserLogin.as_view(), name='login'),
]
III. LogoutView
LogoutView
is all about what you think it does. There is a simple way of making a LogoutView
using only the urls.py
by adding some lines of code.
# /the_app/urls.py
...
from django.contrib.auth.views import LogoutView
urlpatterns = [
...,
path('logout/', LogoutView.as_view(next_page='login'), name='logout'),
]
-LogoutView.as_view() has an argument next_page
which 'login'
and if we look at the UserLogin() class's URL, we may notice that its name is 'login'
. That means, that after we logged out, our app will automatically redirect to the login page.
IV. PersonCreate (View)
Django has a built-in class-view for this job which is the CreateView
. But for some reason like wanting to add some functionalities, I'd rather choose to use the generic View
function than CreateView
. View
is a regular class-based view without any specials to use, making it easier to modify and use if you want a super customizable class view. What is class PersonCreate() do is add an employee
or a person
on the User's employee list.
# /the_app/views.py
...
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import View
from django.shortcuts import render,redirect
class PersonCreate(LoginRequiredMixin,View):
def get(self,request):
return render(request,'the_app/person_create_form.html')
def post(self,request):
user = User.objects.get(username=request.user)
my_name = request.POST.get('name')
user.person_set.create(name=my_name)
my_object = user.person_set.get(name=my_name).id
return redirect('persondetail',my_object)
-If you look at the function names I use in PersonCreate(), they look like an ordinary function view. The only difference is their name was also an HTTP call.
-Make a person_create_form.html
in the_app/templates/the_app
.
Then the urls:
# /the_app/urls.py
...
from .views import PersonCreate
urlpatterns = [
...,
path('person-create/', PersonCreate.as_view(), name='personcreate'),
]
V. PersonList (View)
PersonList() class allows us to see the employee list the User has. Django has a specific class-view for this type of work too, which is called ListView
. But for the same reason as I have earlier in PersonCreate() class, I use the generic views than the ListView
.
# /the_app/views.py
...
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import View
from django.shortcuts import render
class PersonList(LoginRequiredMixin,View):
def get(self,request):
account = User.objects.get(username=request.user)
context = {'person_list':account}
return render(request,'the_app/home.html',context)
-After that, let's make the_app/templates/the_app/home.html
for this class view:
-And then, the theapp/urls.py
:
# /the_app/urls.py
...
from .views import PersonList
urlpatterns = [
...,
path('', PersonList.as_view(), name='personlist'),
]
-Let's take a look at the urlpattern
of the PersonList(), you may notice that its path name is personlist
. If we remember, we configure it in UserLogin() before. That means when the UserLogin() is successful, we'll be directed to the PersonList() which allows us to see the listed employees there.
VI. PersonDetail (DetailView)
Django's DetailView()
class allows you to see the details (tasks) of a specific object (person/employee).
# /the_app/views.py
...
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.detail import DetailView
class PersonDetail(LoginRequiredMixin,DetailView):
model = Person
context_object_name = 'person'
template_name = 'the_app/person_detail.html'
def get(self,*args,**kwargs):
return super(PersonDetail, self).get(*args,**kwargs)
-It will work, but I'm about to add some functions to it for adding, modifying, or deleting tasks for every employee.
# /the_app/views.py
class PersonDetail(LoginRequiredMixin,DetailView):
...
def post(self,*args,**kwargs):
return super(PersonDetail, self).get(*args,**kwargs)
def dispatch(self,request,pk,*args,**kwargs):
peep = self.model.objects.get(id=pk)
if self.request.POST.get('save'):
for task in peep.task_set.all():
if request.POST.get(f't{task.id}') == 'clicked':
task.is_complete = True
else:
task.is_complete = False
task.save()
elif self.request.POST.get('add_item'):
new = request.POST.get('new_item')
peep.task_set.create(task=new, is_complete=False)
elif request.POST.get('delete_this'):
task_index = request.POST.get('delete_this')
peep.task_set.get(id=task_index).delete()
return super(PersonDetail, self).dispatch(request,*args,**kwargs)
-The dispatch
call allows us to add or modify a Person
's task. Look closely at dispatch() functions if-statements, those if-statements are the actions we can do on PersonDetail's page on run. If we make any of those actions, it calls the post() and now we can access the data/changes the User made using dispatch() and saving them.
-The PersonDetail() code looks exactly as this one:
-And for our template, let's make person_detail.html
.
-Don't forget the urls.py
:
# /the_app.urls.py
...
from .views import PersonDetail
urlpatterns = [
...,
path('person-detail/pk=<int:pk>', PersonDetail.as_view(), name='persondetail'),
]
VII. PersonUpdate (UpdateView)
# /the_app/views.py
...
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import UpdateView
class PersonUpdate(LoginRequiredMixin,UpdateView):
model = Person
fields = ['name']
template_name = 'the_app/person_update_form.html'
success_url = reverse_lazy('personlist')
-This type of class-view updates the field you chosen.
-For the_app/person_update_form.html
:
-For urls:
# /the_app/urls.py
...
from .views import PersonUpdate
urlpatterns = [
...,
path('person-update/pk=<int:pk>', PersonUpdate.as_view(), name='personupdate'),
]
VIII. PersonDelete (DeleteView)
DeleteView
does what exactly it sounds; it delete objects (person/employees.
# /the_app/views.py
...
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import DeleteView
class PersonDelete(LoginRequiredMixin,DeleteView):
model = Person
success_url = reverse_lazy('personlist')
template_name = 'the_app/person_confirm_delete.html'
-For person_confirm_delete.html
:
-For urls of PersonDelete():
# /the_app/urls.py
...
from .views import PersonDelete
urlpatterns = [
...
path('person-delete/pk=<int:pk>', PersonDelete.as_view(), name='persondelete'),
]
And now, it's done!! you can try it by running python manage.py runserver
in CLI and then open 127.0.0.1:8000
or localhost:8000
to see if it's working.
Some screenshots of the app:
Login
Click here to see the source code.
Good day, guys!! You can reach my account for more:
Twitter: Codeit_Michael
Github: Codeit-Michael
Posted on April 23, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.