Django 1.11 ListView实现仅显示经前一部门审批的请求
Hey Gustavo, let's work through this approval flow challenge for your Django 1.11 municipal app. The goal is to make sure requests only show up in a department's list once all prior departments in the chain have signed off, right? Here's a step-by-step solution tailored to your model setup.
First, let's make sure your models have the necessary fields to track the approval sequence and status. Based on what you mentioned, here's a polished version you can align with:
# models.py from django.db import models from django.contrib.auth.models import User class Secretaria(models.Model): name = models.CharField(max_length=100) # Add any other department-specific fields here class Solicitacao(models.Model): title = models.CharField(max_length=200) description = models.TextField() created_at = models.DateTimeField(auto_now_add=True) # Store approval order as a comma-separated string of Secretaria IDs (e.g., "2,5,7") # This defines the order departments must approve the request approval_sequence = models.CharField(max_length=200, help_text="Comma-separated Secretaria IDs in approval order") class SecretarioAprovacao(models.Model): request = models.ForeignKey(Solicitacao, on_delete=models.CASCADE, related_name='approvals') secretary = models.ForeignKey(Secretaria, on_delete=models.CASCADE) STATUS_CHOICES = ( ('pending', 'Pending'), ('approved', 'Approved'), ('rejected', 'Rejected'), ) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') approved_at = models.DateTimeField(null=True, blank=True) comments = models.TextField(blank=True) class Meta: # Ensure one approval record per department per request unique_together = ('request', 'secretary')
Pro tip: When a new Solicitacao is created, use a signal to auto-generate SecretarioAprovacao records for each department in approval_sequence (status set to pending). This avoids manual setup later.
get_context_data Now, in your ListView, we'll filter the requests to only show those where all prior departments have approved, and the current department's approval is still pending (or not yet started).
Assuming your logged-in user is linked to a Secretaria via a OneToOneField (e.g., User.secretary), here's how to implement the logic:
# views.py from django.db.models import Q, RawSQL from django.views.generic import ListView from .models import Solicitacao, SecretarioAprovacao class DepartmentRequestListView(ListView): model = Solicitacao template_name = 'requests/department_requests.html' context_object_name = 'solicitacoes' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) current_secretary = self.request.user.secretary # Get the logged-in user's department # 1. Start with requests that include the current department in their approval sequence # Use commas to avoid partial matches (e.g., ID 1 vs 11) queryset = Solicitacao.objects.filter( approval_sequence__icontains=f',{current_secretary.id},' ) # 2. Filter out requests where any prior department hasn't approved yet # This uses PostgreSQL functions—adjust for MySQL if needed queryset = queryset.annotate( # Extract all department IDs that come before the current one in the sequence prior_depts=RawSQL( "SELECT array_to_string(array_remove(string_to_array(approval_sequence, ','), %s), ',')", [str(current_secretary.id)] ) ) # Ensure all prior departments have approved the request filtered_queryset = queryset.filter( ~Q( approvals__secretary_id__in=RawSQL("SELECT unnest(string_to_array(prior_depts, ','))::int", []), approvals__status__in=['pending', 'rejected'] ) ) # 3. Only include requests where the current department's approval is pending or not started filtered_queryset = filtered_queryset.filter( Q(approvals__secretary=current_secretary, approvals__status='pending') | Q(approvals__secretary=current_secretary, approvals__isnull=True) ) # Remove duplicate requests caused by joining with approval records filtered_queryset = filtered_queryset.distinct() context['solicitacoes'] = filtered_queryset return context
In your template, you can loop through the filtered requests and show relevant details, including the current approval status (if any):
<!-- templates/requests/department_requests.html --> <h2>Pending Requests for {{ user.secretary.name }}</h2> {% if solicitacoes %} <ul class="request-list"> {% for req in solicitacoes %} <li class="request-item"> <h3>{{ req.title }}</h3> <p>{{ req.description|truncatechars:200 }}</p> <small>Created: {{ req.created_at|date:"M d, Y" }}</small> {% with current_approval=req.approvals.filter(secretary=user.secretary).first %} {% if current_approval %} <p>Current Status: <strong>{{ current_approval.get_status_display }}</strong></p> {% if current_approval.comments %} <p>Previous Comments: {{ current_approval.comments }}</p> {% endif %} {% endif %} {% endwith %} <a href="{% url 'request_approval' req.id %}" class="btn">Review & Approve/Reject</a> </li> {% endfor %} </ul> {% else %} <p>No pending requests for your department at this time.</p> {% endif %}
- Database Compatibility: The RawSQL above uses PostgreSQL functions (
string_to_array,unnest). If you're on MySQL, replace these with equivalent functions likeSUBSTRING_INDEXto split the approval sequence. - Indexing: Add database indexes to
SecretarioAprovacao.request,SecretarioAprovacao.secretary, andSolicitacao.approval_sequenceto speed up filtering. - Rejection Handling: The query above excludes requests where any prior department rejected it—adjust the
~Qclause if you want to handle rejected requests differently (e.g., notify the requester instead of hiding from departments). - Signal for Auto-Approvals: Add a post-save signal to
Solicitacaothat createsSecretarioAprovacaorecords for each department inapproval_sequencewhen a request is created. This ensures every department has a pending approval entry from the start.
内容的提问来源于stack exchange,提问作者Gustavo Cruz

