DEV Community

Cover image for Make a Simple Employee managing app in Django with Class Based Views (With Login, Logout)
Michael_Maranan
Michael_Maranan

Posted on

Make a Simple Employee managing app in Django with Class Based Views (With Login, Logout)

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 to you.

Setup

Requirements
django - pip intsall django

  1. Make a DIR with virtual environment if you want
  2. Run django-admin startproject projectname .
  3. Run python manage.py startapp appname
  4. Add app dependancy in project

-I choose to name my projectname as my_project and appname to 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',
]
Enter fullscreen mode Exit fullscreen mode
  1. 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 a urls.py inside the_app directory or what we have the_app, and then;
/* 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
]
Enter fullscreen mode Exit fullscreen mode

B. Connect my_project/urls.py to the_app/urls.py

/* my_project/urls.py

...
from django.urls import path, include

urlpatterns = [
    ...,
    path('', include('the_app.urls')),
]
Enter fullscreen mode Exit fullscreen mode

Tip: Try to run it by python manage.py runserver and go to localhost:8000 or 127.0.0.1:8000 to see if it's working.

  1. Make Directory for templates -Make a folder inside the_app named templates, and inside it, make a folder named the_app. That's where we place our templates. If you look its order, it looks like this:
    -/the_app/templates/the_app$

    --OR--

    the_app
        templates
            the_app
    my_project
    manage.py
Enter fullscreen mode Exit fullscreen mode

Making a Model

  • Django Models are used to make tables and what it should contain (data type like string, picture, etc.). -Now we're making a Person() model where every manager can save there the people they were incharge. And each Person has task so we also gonna make a Task() model.
/* 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

Enter fullscreen mode Exit fullscreen mode

-If we look on the first argument of the Task() model we'll see that it takes 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)

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
Enter fullscreen mode Exit fullscreen mode

-This is how you do a simple UserSignup() view. It will work but if you want to add some fucntionalities for it to add another options 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)
Enter fullscreen mode Exit fullscreen mode

-In Django, you can't name the class functions by random. Instead, they are http calls. When you where using your app with that class, it continues the operation untill the requirements you fill in was satisfied, and then it make a call. If you had a class function with the same name of 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 defaulty asks for username and password. Once you fulfill them, the UserCreationForm calls form_valid and there where our form_valid() function will be called.

-What we're gonna add this time is a function which 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)
Enter fullscreen mode Exit fullscreen mode

-Our UserSignup() should looks like this:
UserSignup pic

-Now let's make a page for it. Make signup.html inside the_app/templates/the_app:
signup.html pic

-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'),
]
Enter fullscreen mode Exit fullscreen mode

II. UserLogin (LoginView)

LoginView is used for signning in/logging in 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')
Enter fullscreen mode Exit fullscreen mode

-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 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/.
signin.html pic

-And don't forget to make a link for it in urls.py:

/* the_app/urls.py

...
from .views import UserLogin

urlpatterns = [
    ...,
    path('login/', UserLogin.as_view(), name='login'),
]
Enter fullscreen mode Exit fullscreen mode

III. LogoutView

LogoutView is all about what you think it does. There is a simple way in making a LogoutView using only the urls.py by adding some line of codes on it.

/* the_app/urls.py

...
from django.contrib.auth.views import LogoutView

urlpatterns = [
    ...,
    path('logout/', LogoutView.as_view(next_page='login'), name='logout'),
]
Enter fullscreen mode Exit fullscreen mode

-LogoutView.as_view() has an argument next_page which 'login' and as if we look at the UserLogin() class's url, we may notice that its name is 'login'. That means, 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 chose to use the generic View function than CreateView. View is a regular class-based view without any specials to used in, making it easier to modify and use if you want a super customizable class-view. What are 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)
Enter fullscreen mode Exit fullscreen mode

-If you look on the function names I use in PersonCreate(), they look like an ordinary function-view. Only difference is their name was also an http call.

-Make a person_create_form.html in the_app/templates/the_app.
person_create_form.html pic

Then the urls:

/* the_app/urls.py

...
from .views import PersonCreate

urlpatterns = [
    ...,
    path('person-create/', PersonCreate.as_view(), name='personcreate'),
]
Enter fullscreen mode Exit fullscreen mode

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 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)
Enter fullscreen mode Exit fullscreen mode

-After that, let's make the_app/templates/the_app/home.html for this class-view:
home.html pic

-And then, the theapp/urls.py:


/* the_app/urls.py

...
from .views import PersonList

urlpatterns = [
    ...,
    path('', PersonList.as_view(), name='personlist'),
]
Enter fullscreen mode Exit fullscreen mode

-Let's take a look on the urlpattern of the PersonList(), you may notice that it's 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 DetailView() class allows you to see 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)
Enter fullscreen mode Exit fullscreen mode

-It will works, but I'm about to add some functions on 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)
Enter fullscreen mode Exit fullscreen mode

-The dispatch call allows us to add or modify a Person's task. Look closely on 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 lokks exactly as this one:
PersonDetail pic

-And for our template, let's make person_detail.html.
person_detail.html pic

-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'),
]
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

-This type of class-view updates the field you chosen.

-For the_app/person_update_form.html:
person_update_form.html pic

-For urls:

/* the_app/urls.py

...
from .views import PersonUpdate

urlpatterns = [
    ...,
    path('person-update/pk=<int:pk>', PersonUpdate.as_view(), name='personupdate'),
]
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

-For person_confirm_delete.html:
person_confirm_delete.html pic

-For urls of PersonDelete():

/* the_app/urls.py

...
from .views import PersonDelete

urlpatterns = [
    ...
    path('person-delete/pk=<int:pk>', PersonDelete.as_view(), name='persondelete'),
]
Enter fullscreen mode Exit fullscreen mode

And now, it's done!! you can try it by running python manage.py runserver in CLI and open 127.0.0.1:8000 or localhost:8000 to see if it's working.


Some screenshots of the app:
Login
login

Signup
signup

Home
home

PersonCreate
personcreate

PersonDetail
persondetail

Click here to see the source code.

Good day guys!! You can reach my account for more:
Twitter: Codeit_Michael
Github: Codeit-Michael

Discussion (0)