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 • Edited on

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

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

  1. Make a DIR with a virtual environment if you want
  2. Run django-admin startproject projectname .
  3. Run python manage.py startapp appname
  4. 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',
]


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; ```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')),
]


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 a 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 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



Enter fullscreen mode Exit fullscreen mode

-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


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 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)


Enter fullscreen mode Exit fullscreen mode

-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)


Enter fullscreen mode Exit fullscreen mode

-Our UserSignup() should look 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 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')


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

-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'),
]


Enter fullscreen mode Exit fullscreen mode

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'),
]


Enter fullscreen mode Exit fullscreen mode

-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)


Enter fullscreen mode Exit fullscreen mode

-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.
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 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)


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 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)


Enter fullscreen mode Exit fullscreen mode

-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)


Enter fullscreen mode Exit fullscreen mode

-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:
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 then 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

Top comments (0)