Django Bug追踪系统:自动分配、邮件通知及权限控制需求
Hey there! Let's work through each of your requirements for your Django bug tracking system step by step. I'll share practical, actionable code snippets that you can integrate directly into your project.
1. Auto-Populate Assigned_to When Selecting Project_Name
First, we need a way to store the fixed project-to-leader mapping. Let's create a dedicated Project model to hold this data, then link it to your Bugs model. We'll add frontend logic to auto-fill the assignee when a project is selected.
Step 1: Add the Project Model
Update your models.py to include this model:
from django.db import models class Project(models.Model): name = models.CharField(max_length=100, unique=True) lead = models.CharField(max_length=100) # Stores the assigned person for this project def __str__(self): return self.name # Update your existing Bugs model's Project_Name field to use a ForeignKey class Bugs(models.Model): STATUS_CHOICE = ( ('Work In Progress', 'Work In Progress'), ('Incomplete', 'Incomplete'), ('Completed', 'Completed') ) # Replace the old CharField with a ForeignKey to Project Project_Name = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='bugs') Basic_Description = models.CharField(max_length=100) Detailed_Description = models.TextField(default='The Description, here.') Status = models.CharField(max_length=18, choices=STATUS_CHOICE) Assigned_to = models.CharField(max_length=100) Reported_by = models.CharField(max_length=50, blank=True, null=True) Reporters_Mail_ID = models.CharField(max_length=50, blank=True, null=True) Reported_Date = models.DateTimeField(null=True, blank=True) Created = models.DateTimeField(auto_now_add=True, null=True, blank=True) Updated = models.DateTimeField(auto_now=True, null=True, blank=True) Deadline_Date = models.DateTimeField(null=True, blank=True) image = models.FileField(null=True, blank=True) def __str__(self): return f"{self.Project_Name} [{self.Status}] {self.Basic_Description} [{self.Assigned_to}]" class Meta: verbose_name_plural = "Bugs"
Step 2: Add Admin Logic for Auto-Fill
Create a custom admin form and add JavaScript to handle the auto-population:
# admin.py from django import forms from django.contrib import admin import json from .models import Bugs, Project class BugsAdminForm(forms.ModelForm): class Meta: model = Bugs fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Pass project-lead mappings to the frontend as JSON project_lead_map = {p.id: p.lead for p in Project.objects.all()} self.fields['Project_Name'].widget.attrs['data-project-leads'] = json.dumps(project_lead_map) class BugsAdmin(admin.ModelAdmin): form = BugsAdminForm # Include custom JS for auto-fill class Media: js = ('admin/js/project_lead_auto_fill.js',) # Register both models in the admin admin.site.register(Project) admin.site.register(Bugs, BugsAdmin)
Step 3: Create the Auto-Fill JavaScript
Create a file static/admin/js/project_lead_auto_fill.js (ensure your static files are configured correctly):
document.addEventListener('DOMContentLoaded', function() { const projectSelect = document.getElementById('id_Project_Name'); const assignedToInput = document.getElementById('id_Assigned_to'); if (!projectSelect || !assignedToInput) return; const projectLeadMap = JSON.parse(projectSelect.dataset.projectLeads); // Fill assignee on initial load if a project is selected if (projectSelect.value) { assignedToInput.value = projectLeadMap[projectSelect.value] || ''; } // Update assignee when project selection changes projectSelect.addEventListener('change', function() { assignedToInput.value = projectLeadMap[this.value] || ''; }); });
2. Automatic Email Notifications on Bug Edits
We'll use Django's post_save signal to trigger emails when a bug is updated. First, configure your email settings, then set up the signal logic.
Step 1: Configure Email Settings
Update settings.py with your SMTP details:
# settings.py EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'your-smtp-host' # e.g., 'smtp.gmail.com' for Gmail EMAIL_PORT = 587 EMAIL_USE_TLS = True EMAIL_HOST_USER = 'your-email@example.com' EMAIL_HOST_PASSWORD = 'your-email-password-or-app-specific-password' DEFAULT_FROM_EMAIL = 'your-email@example.com'
Step 2: Set Up the Signal Handler
Create a signals.py file in your app to handle email triggers:
# signals.py from django.db.models.signals import post_save from django.dispatch import receiver from django.contrib.auth.models import User from django.core.mail import send_mail from django.conf import settings from django.utils.deprecation import MiddlewareMixin import threading from .models import Bugs # Thread-local storage to pass the request to the signal _thread_locals = threading.local() class RequestMiddleware(MiddlewareMixin): def process_request(self, request): _thread_locals.request = request def get_current_request(): return getattr(_thread_locals, 'request', None) @receiver(post_save, sender=Bugs) def send_bug_update_email(sender, instance, created, **kwargs): request = get_current_request() if not request: return # Skip if no request (e.g., command-line operations) current_user = request.user # Case 1: Superuser updates the bug (assigns person or changes status) if current_user.is_superuser: # Only send email if Assigned_to or Status was updated updated_fields = kwargs.get('update_fields', set()) if 'Assigned_to' in updated_fields or 'Status' in updated_fields: # Get the assignee's email (adjust this if you use User model for assignees) recipient_email = instance.Reporters_Mail_ID if not recipient_email: # Fallback: Get email from User model if Assigned_to matches username recipient_user = User.objects.filter(username=instance.Assigned_to).first() recipient_email = recipient_user.email if recipient_user else None if recipient_email: subject = f"Bug Updated: {instance.Project_Name} - {instance.Basic_Description}" message = f""" Hello {instance.Assigned_to}, A bug you're assigned to has been updated: - Project: {instance.Project_Name} - Bug Summary: {instance.Basic_Description} - New Status: {instance.Status} - Updated At: {instance.Updated.strftime('%Y-%m-%d %H:%M')} You can view full details in the Django Admin. Best regards, The Bug Tracking Team """ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [recipient_email]) # Case 2: Assigned user updates the bug else: if current_user.username == instance.Assigned_to: # Get all superusers' emails superuser_emails = [user.email for user in User.objects.filter(is_superuser=True) if user.email] if superuser_emails: subject = f"Bug Updated by {current_user.username}: {instance.Project_Name} - {instance.Basic_Description}" message = f""" Hello Admin Team, {current_user.username} has updated a bug they're assigned to: - Project: {instance.Project_Name} - Bug Summary: {instance.Basic_Description} - Current Status: {instance.Status} - Updated At: {instance.Updated.strftime('%Y-%m-%d %H:%M')} You can view full details in the Django Admin. Best regards, The Bug Tracking Team """ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, superuser_emails)
Step 3: Register the Signal and Middleware
Update your apps.py to register the signal:
# apps.py from django.apps import AppConfig class YourAppConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'your_app_name' def ready(self): import your_app_name.signals # Import signals to register them
Update settings.py to use your app config and add the middleware:
# settings.py INSTALLED_APPS = [ # ... other apps 'your_app_name.apps.YourAppConfig', ] MIDDLEWARE = [ # ... other middleware 'your_app_name.signals.RequestMiddleware', ]
Step 4: Pass Request to Signal in Admin
Update BugsAdmin in admin.py to store the request in thread-local storage:
# admin.py class BugsAdmin(admin.ModelAdmin): # ... existing code def save_model(self, request, obj, form, change): # Store request in thread-local for the signal _thread_locals.request = request super().save_model(request, obj, form, change)
3. Optional: Permission Isolation (Assigned Users See Only Their Bugs)
Restrict regular users to only view and edit bugs assigned to them by modifying the admin's queryset and permissions.
Update BugsAdmin in admin.py:
# admin.py class BugsAdmin(admin.ModelAdmin): # ... existing code def get_queryset(self, request): qs = super().get_queryset(request) # Superusers see all bugs; regular users see only their assigned ones if request.user.is_superuser: return qs return qs.filter(Assigned_to=request.user.username) def has_change_permission(self, request, obj=None): # Superusers can edit all; regular users can only edit their assigned bugs if request.user.is_superuser: return True if obj: return obj.Assigned_to == request.user.username return False def has_delete_permission(self, request, obj=None): # Optional: Restrict delete permission similarly if request.user.is_superuser: return True if obj: return obj.Assigned_to == request.user.username return False
内容的提问来源于stack exchange,提问作者user9779509

