Horje
Building an Issue Tracker with Django

In this article, we will guide you through creating an Issue Tracker using Django. A Django issue tracker is a web-based application designed to help software development teams manage and track bugs, tasks, and feature requests efficiently. Leveraging Django’s powerful framework, this project provides a structured approach to issue management through robust models, views, and templates. Users can create and manage projects, log issues, update their statuses, and add comments for better collaboration.

Issue Tracker using Django

Below, is the step-by-step Implementation of a language learning app using Django:

Starting the Project Folder

To start the project use this command

django-admin startproject issue
cd issue

To start the app use this command

python manage.py startapp home

Now add this app to the ‘settings.py’

INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"home",
]

File Structure

Screenshot-(175)

Setting Necessary Files

home/models.py: The models.py file in a Django issue tracker project defines the data structure of the application. It uses Django’s ORM (Object-Relational Mapping) system to create database tables and establish relationships between different data entities.

Python
from django.db import models
from django.contrib.auth.models import User

class Project(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField()

    def __str__(self):
        return self.name

class Issue(models.Model):
    STATUS_CHOICES = [
        ('open', 'Open'),
        ('in_progress', 'In Progress'),
        ('closed', 'Closed'),
    ]

    title = models.CharField(max_length=100)
    description = models.TextField()
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='open')
    project = models.ForeignKey(Project, related_name='issues', 
                                
                                on_delete=models.CASCADE)
    created_by = models.ForeignKey(User, related_name='issues_created', 
                                   on_delete=models.CASCADE)
    assigned_to = models.ForeignKey(User, related_name='issues_assigned',
                                    on_delete=models.SET_NULL, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

class Comment(models.Model):
    issue = models.ForeignKey(Issue, related_name='comments',
                              on_delete=models.CASCADE)
    author = models.ForeignKey(User, related_name='comments', 
                               on_delete=models.CASCADE)
    text = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f'Comment by {self.author} on {self.issue}'

home/forms.py: The forms.py file in a Django issue tracker project defines forms that handle user input for creating and updating projects, issues, and comments. Django’s form classes provide a way to validate and process user data while rendering the form fields as HTML elements.

Python
from django import forms
from .models import Issue,Project

class IssueForm(forms.ModelForm):
    class Meta:
        model = Issue
        fields = ['title', 'description', 'status', 'assigned_to']
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'description': forms.Textarea(attrs={'class': 'form-control'}),
            'status': forms.Select(attrs={'class': 'form-control'}),
            'assigned_to': forms.Select(attrs={'class': 'form-control'}),
        }

from .models import Comment

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['text']
        widgets = {
            'text': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
        }


class ProjectForm(forms.ModelForm):
    class Meta:
        model = Project
        fields = ['name', 'description']
        widgets = {
            'name': forms.TextInput(attrs={'class': 'form-control'}),
            'description': forms.Textarea(attrs={'class': 'form-control'}),
        }

home/views.py: The views.py file in a Django issue tracker project contains the logic that connects the models to the templates and handles HTTP requests and responses.

Python
from django.shortcuts import render, get_object_or_404,redirect
from .models import Project, Issue,Comment
from .forms import IssueForm
from .forms import ProjectForm, CommentForm

def create_project(request):
    if request.method == 'POST':
        form = ProjectForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('project_list')
    else:
        form = ProjectForm()
    return render(request, 'project_form.html', {'form': form})

def project_list(request):
    projects = Project.objects.all()
    return render(request, 'project_list.html', {'projects': projects})

def project_detail(request, pk):
    project = get_object_or_404(Project, pk=pk)
    issues = project.issues.all()
    return render(request, 'project_detail.html', {'project': project, 'issues': issues})

def issue_detail(request, pk):
    issue = get_object_or_404(Issue, pk=pk)
    return render(request, 'issue_detail.html', {'issue': issue})

def create_issue(request, pk):
    project = get_object_or_404(Project, pk=pk)
    if request.method == 'POST':
        form = IssueForm(request.POST)
        if form.is_valid():
            issue = form.save(commit=False)
            issue.project = project
            issue.created_by = request.user
            issue.save()
            return redirect('project_detail', pk=project.pk)
    else:
        form = IssueForm()
    return render(request, 'issue_form.html', {'form': form, 'project': project})

from .forms import CommentForm

def issue_detail(request, pk):
    issue = get_object_or_404(Issue, pk=pk)
    comments = issue.comments.all()
    
    if request.method == 'POST':
        comment_form = CommentForm(request.POST)
        if comment_form.is_valid():
            comment = comment_form.save(commit=False)
            comment.issue = issue
            comment.author = request.user
            comment.save()
            return redirect('issue_detail', pk=issue.pk)
    else:
        comment_form = CommentForm()

    return render(request, 'issue_detail.html', {
        'issue': issue,
        'comments': comments,
        'comment_form': comment_form,
    }
                 )

templates/create_community_post

The templates directory contains HTML files that define the user interface. These templates use Django’s template language and Bootstrap to create a responsive and attractive UI for listing projects, displaying issue details, and handling form submissions.

templates/base.html:

HTML
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}Issue Tracker{% endblock %}</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .navbar-custom {
            background-color: #343a40;
        }
        .navbar-custom .navbar-brand, .navbar-custom .nav-link {
            color: #ffffff;
        }
        .navbar-custom .nav-link:hover {
            color: #f8f9fa;
        }
    </style>
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-custom">
        <a class="navbar-brand" href="{% url 'project_list' %}">Issue Tracker</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item">
                    <a class="nav-link" href="{% url 'project_list' %}">Projects</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="{% url 'create_project' %}">Create Project</a>
                </li>
                <!-- Add more nav items here -->
            </ul>
            <form class="form-inline my-2 my-lg-0">
                <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
                <button class="btn btn-outline-light my-2 my-sm-0" type="submit">Search</button>
            </form>
        </div>
    </nav>
    <div class="container mt-4">
        {% block content %}{% endblock %}
    </div>
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

templates/issue_detail.hmtl:

Python
{% extends 'base.html' %}

{% block title %}{{ issue.title }}{% endblock %}

{% block content %}
    <div class="mt-5">
        <h1>{{ issue.title }}</h1>
        <p>{{ issue.description }}</p>
        <p>Status: <span class="badge badge-info">{{ issue.get_status_display }}</span></p>
        <p>Assigned to: {{ issue.assigned_to }}</p>

        <h2 class="mt-4">Comments</h2>
        <ul class="list-group mt-3">
            {% for comment in comments %}
                <li class="list-group-item">
                    <strong>{{ comment.author }}</strong> ({{ comment.created_at }}):
                    <p>{{ comment.text }}</p>
                </li>
            {% empty %}
                <li class="list-group-item">No comments yet.</li>
            {% endfor %}
        </ul>

        <h2 class="mt-4">Add a comment</h2>
        <form method="post" class="mt-3">
            {% csrf_token %}
            <div class="form-group">
                {{ comment_form.text.label_tag }}
                {{ comment_form.text }}
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>
{% endblock %}

templates/issue_form.html:

HTML
{% extends 'base.html' %}

{% block title %}Create Issue{% endblock %}

{% block content %}
    <div class="mt-5">
        <h1>Create Issue for {{ project.name }}</h1>
        <form method="post">
            {% csrf_token %}
            <div class="form-group">
                {{ form.title.label_tag }}
                {{ form.title }}
            </div>
            <div class="form-group">
                {{ form.description.label_tag }}
                {{ form.description }}
            </div>
            <div class="form-group">
                {{ form.status.label_tag }}
                {{ form.status }}
            </div>
            <div class="form-group">
                {{ form.assigned_to.label_tag }}
                {{ form.assigned_to }}
            </div>
            <button type="submit" class="btn btn-primary">Save</button>
        </form>
    </div>
{% endblock %}

templates/project_detail.html:

HTML
<!DOCTYPE html>
<html>
<head>
    <title>{{ project.name }}</title>
</head>
<body>
    <h1>{{ project.name }}</h1>
    <p>{{ project.description }}</p>
    <h2>Issues</h2>
    <ul>
        {% for issue in issues %}
            <li><a href="{% url 'issue_detail' issue.pk %}">{{ issue.title }}</a></li>
        {% endfor %}
    </ul>
    <a href="{% url 'create_issue' project.pk %}">Create new issue</a>
</body>
</html>

templates/project_form.html:

HTML
{% extends 'base.html' %}

{% block title %}Create Project{% endblock %}

{% block content %}
    <div class="mt-5">
        <h1>Create Project</h1>
        <form method="post">
            {% csrf_token %}
            <div class="form-group">
                {{ form.name.label_tag }}
                {{ form.name }}
            </div>
            <div class="form-group">
                {{ form.description.label_tag }}
                {{ form.description }}
            </div>
            <button type="submit" class="btn btn-primary">Save</button>
        </form>
    </div>
{% endblock %}

templates/project_list.html:

HTML
{% extends 'base.html' %}

{% block title %}Projects{% endblock %}

{% block content %}
    <h1 class="mt-5">Projects</h1>
    <a href="{% url 'create_project' %}" class="btn btn-primary mb-3">Create New Project</a>
    <ul class="list-group mt-3">
        {% for project in projects %}
            <li class="list-group-item">
                <a href="{% url 'project_detail' project.pk %}">{{ project.name }}</a>
            </li>
        {% endfor %}
    </ul>
{% endblock %}

home/urls.py: This urlpatterns list defines paths for the Django project: admin/ for the admin interface and ” for URLs defined in the “home” app

Python
from django.urls import path
from . import views

urlpatterns = [
    path('', views.project_list, name='project_list'),
    path('project/<int:pk>/', views.project_detail, name='project_detail'),
    path('issue/<int:pk>/', views.issue_detail, name='issue_detail'),
    path('project/<int:pk>/new_issue/', views.create_issue, name='create_issue'),
    path('project/new/', views.create_project, name='create_project'),
]


issue/urls.py: These urlpatterns in Django project correspond to views.

Python
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include("home.urls"))
] 

admin.py:

Python
from django.contrib import admin
from .models import Project, Issue, Comment

admin.site.register(Project)
admin.site.register(Issue)
admin.site.register(Comment)


Output:

Screenshot-(177)-(1)Screenshot-(178)Screenshot-(179)Screenshot-(180)





Reffered: https://www.geeksforgeeks.org


Python

Related
Text Summarization App with Flask and Spacy Text Summarization App with Flask and Spacy
How To Copy Files From One Server To Another in Python How To Copy Files From One Server To Another in Python
How to Add New Line in Dictionary in Python How to Add New Line in Dictionary in Python
Building APIs using FastAPI with Django Building APIs using FastAPI with Django
Python Falcon - Deployment Python Falcon - Deployment

Type:
Geek
Category:
Coding
Sub Category:
Tutorial
Uploaded by:
Admin
Views:
19