Implement flexible order status management system
Features: - Created OrderStatus model for managing statuses per tenant - Added system-level statuses: draft, new, confirmed, in_assembly, in_delivery, completed, return, cancelled - Implemented CRUD views for managing order statuses - Created OrderStatusService with status transitions and business logic hooks - Updated Order model to use ForeignKey to OrderStatus - Added is_returned flag for tracking returned orders - Updated filters to work with new OrderStatus model - Created management command for status initialization - Added HTML templates for status list, form, and confirmation - Fixed views.py to use OrderStatus instead of removed STATUS_CHOICES 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
129
myproject/orders/templates/orders/status_confirm_delete.html
Normal file
129
myproject/orders/templates/orders/status_confirm_delete.html
Normal file
@@ -0,0 +1,129 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Удалить статус{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid mt-4">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<h1>Удалить статус</h1>
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
<a href="{% url 'orders:status_list' %}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Вернуться к статусам
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card border-danger">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h5 class="mb-0">
|
||||
<i class="fas fa-exclamation-triangle"></i> Подтвердите удаление
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-warning mb-4">
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
<strong>Внимание!</strong> Это действие необратимо.
|
||||
</div>
|
||||
|
||||
<p>Вы собираетесь удалить статус:</p>
|
||||
<div class="mb-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5>
|
||||
<span style="display: inline-block; width: 20px; height: 20px; background-color: {{ object.color }}; border-radius: 3px; margin-right: 10px; vertical-align: middle;"></span>
|
||||
{{ object.name }}
|
||||
</h5>
|
||||
<p class="text-muted mb-2">
|
||||
<strong>Код:</strong> <code>{{ object.code }}</code>
|
||||
</p>
|
||||
{% if object.description %}
|
||||
<p class="mb-0">
|
||||
<strong>Описание:</strong><br>
|
||||
{{ object.description }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if orders_count > 0 %}
|
||||
<div class="alert alert-danger">
|
||||
<i class="fas fa-ban"></i>
|
||||
<strong>Невозможно удалить!</strong>
|
||||
В этом статусе находится {{ orders_count }} заказ(ов).
|
||||
Пожалуйста, измените статус этих заказов перед удалением.
|
||||
</div>
|
||||
|
||||
<a href="{% url 'orders:status_list' %}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Вернуться к статусам
|
||||
</a>
|
||||
{% else %}
|
||||
<form method="post" class="mt-4">
|
||||
{% csrf_token %}
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="fas fa-trash"></i> Да, удалить статус
|
||||
</button>
|
||||
<a href="{% url 'orders:status_list' %}" class="btn btn-secondary">
|
||||
<i class="fas fa-times"></i> Отменить
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-light">
|
||||
<h6 class="mb-0">Информация о статусе</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-5">Название:</dt>
|
||||
<dd class="col-sm-7">{{ object.name }}</dd>
|
||||
|
||||
<dt class="col-sm-5">Код:</dt>
|
||||
<dd class="col-sm-7"><code>{{ object.code }}</code></dd>
|
||||
|
||||
<dt class="col-sm-5">Тип:</dt>
|
||||
<dd class="col-sm-7">
|
||||
{% if object.is_system %}
|
||||
<span class="badge bg-info">Системный</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">Пользовательский</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-5">Статус:</dt>
|
||||
<dd class="col-sm-7">
|
||||
{% if object.is_positive_end %}
|
||||
<span class="badge bg-success">✓ Успешный</span>
|
||||
{% elif object.is_negative_end %}
|
||||
<span class="badge bg-danger">✗ Отрицательный</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Промежуточный</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-5">Заказов:</dt>
|
||||
<dd class="col-sm-7"><span class="badge bg-light text-dark">{{ orders_count }}</span></dd>
|
||||
|
||||
<dt class="col-sm-5">Создано:</dt>
|
||||
<dd class="col-sm-7"><small>{{ object.created_at|date:"d.m.Y H:i" }}</small></dd>
|
||||
|
||||
<dt class="col-sm-5">Изменено:</dt>
|
||||
<dd class="col-sm-7"><small>{{ object.updated_at|date:"d.m.Y H:i" }}</small></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
295
myproject/orders/templates/orders/status_form.html
Normal file
295
myproject/orders/templates/orders/status_form.html
Normal file
@@ -0,0 +1,295 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{% if form.instance.pk %}Редактировать{% else %}Создать{% endif %} статус{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid mt-4">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<h1>
|
||||
{% if form.instance.pk %}
|
||||
Редактировать статус: {{ form.instance.name }}
|
||||
{% else %}
|
||||
Создать новый статус
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% if is_system %}
|
||||
<div class="alert alert-info mt-2">
|
||||
<i class="fas fa-info-circle"></i> Это системный статус. Некоторые поля не могут быть изменены.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
<a href="{% url 'orders:status_list' %}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Вернуться к статусам
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading">Ошибка!</h4>
|
||||
{% for error in form.non_field_errors %}
|
||||
<p>{{ error }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.name.id_for_label }}" class="form-label">
|
||||
{{ form.name.label }}
|
||||
<span class="text-danger">*</span>
|
||||
</label>
|
||||
{{ form.name }}
|
||||
{% if form.name.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.name.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<small class="form-text text-muted">
|
||||
Например: Выполнен, В процессе, Возврат
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.code.id_for_label }}" class="form-label">
|
||||
{{ form.code.label }}
|
||||
<span class="text-danger">*</span>
|
||||
</label>
|
||||
{{ form.code }}
|
||||
{% if form.code.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.code.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<small class="form-text text-muted">
|
||||
{% if form.code.field.help_text %}
|
||||
{{ form.code.field.help_text|safe }}
|
||||
{% else %}
|
||||
Латинские буквы, цифры и подчеркивания
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.label.id_for_label }}" class="form-label">
|
||||
{{ form.label.label }}
|
||||
</label>
|
||||
{{ form.label }}
|
||||
{% if form.label.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.label.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<small class="form-text text-muted">
|
||||
Для отображения в интерфейсе (опционально)
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.color.id_for_label }}" class="form-label">
|
||||
{{ form.color.label }}
|
||||
</label>
|
||||
<div class="input-group">
|
||||
{{ form.color }}
|
||||
<span class="input-group-text" id="color-preview" style="width: 60px; background-color: #808080;"></span>
|
||||
</div>
|
||||
{% if form.color.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.color.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.description.id_for_label }}" class="form-label">
|
||||
{{ form.description.label }}
|
||||
</label>
|
||||
{{ form.description }}
|
||||
{% if form.description.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.description.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<small class="form-text text-muted">
|
||||
Описание для пользователей (опционально)
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-3">
|
||||
{{ form.is_positive_end }}
|
||||
<label class="form-check-label" for="{{ form.is_positive_end.id_for_label }}">
|
||||
{{ form.is_positive_end.label }}
|
||||
</label>
|
||||
<small class="d-block text-muted mt-1">
|
||||
Отметьте, если это успешный финальный статус (Выполнен)
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-3">
|
||||
{{ form.is_negative_end }}
|
||||
<label class="form-check-label" for="{{ form.is_negative_end.id_for_label }}">
|
||||
{{ form.is_negative_end.label }}
|
||||
</label>
|
||||
<small class="d-block text-muted mt-1">
|
||||
Отметьте, если это отрицательный финальный статус (Отменен)
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-4">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i>
|
||||
{% if form.instance.pk %}
|
||||
Сохранить изменения
|
||||
{% else %}
|
||||
Создать статус
|
||||
{% endif %}
|
||||
</button>
|
||||
<a href="{% url 'orders:status_list' %}" class="btn btn-secondary">
|
||||
<i class="fas fa-times"></i> Отменить
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-light">
|
||||
<h6 class="mb-0">Предпросмотр</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Название статуса</label>
|
||||
<p class="lead">
|
||||
<span id="preview-name">{{ form.instance.name|default:"Название" }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Код статуса</label>
|
||||
<code id="preview-code">{{ form.instance.code|default:"код" }}</code>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Внешний вид</label>
|
||||
<div style="padding: 10px; border-radius: 4px; background-color: {{ form.instance.color|default:'#808080' }}; color: white; text-align: center;" id="preview-color">
|
||||
<strong id="preview-color-text">{{ form.instance.name|default:"Статус" }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Тип статуса</label>
|
||||
<p id="preview-type">
|
||||
{% if form.instance.is_system %}
|
||||
<span class="badge bg-info">Системный</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">Пользовательский</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Финальный статус</label>
|
||||
<p id="preview-end">
|
||||
{% if form.instance.is_positive_end %}
|
||||
<span class="badge bg-success">✓ Успешный конец</span>
|
||||
{% elif form.instance.is_negative_end %}
|
||||
<span class="badge bg-danger">✗ Отрицательный конец</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Промежуточный</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const nameInput = document.getElementById('{{ form.name.id_for_label }}');
|
||||
const codeInput = document.getElementById('{{ form.code.id_for_label }}');
|
||||
const colorInput = document.getElementById('{{ form.color.id_for_label }}');
|
||||
const positiveEndCheckbox = document.getElementById('{{ form.is_positive_end.id_for_label }}');
|
||||
const negativeEndCheckbox = document.getElementById('{{ form.is_negative_end.id_for_label }}');
|
||||
|
||||
const previewName = document.getElementById('preview-name');
|
||||
const previewCode = document.getElementById('preview-code');
|
||||
const previewColor = document.getElementById('preview-color');
|
||||
const previewColorText = document.getElementById('preview-color-text');
|
||||
const colorPreview = document.getElementById('color-preview');
|
||||
const previewEnd = document.getElementById('preview-end');
|
||||
|
||||
function updatePreview() {
|
||||
// Обновляем название
|
||||
previewName.textContent = nameInput.value || 'Название';
|
||||
previewColorText.textContent = nameInput.value || 'Статус';
|
||||
|
||||
// Обновляем код
|
||||
previewCode.textContent = codeInput.value || 'код';
|
||||
|
||||
// Обновляем цвет
|
||||
const color = colorInput.value || '#808080';
|
||||
previewColor.style.backgroundColor = color;
|
||||
colorPreview.style.backgroundColor = color;
|
||||
|
||||
// Обновляем тип конца
|
||||
if (positiveEndCheckbox.checked) {
|
||||
previewEnd.innerHTML = '<span class="badge bg-success">✓ Успешный конец</span>';
|
||||
} else if (negativeEndCheckbox.checked) {
|
||||
previewEnd.innerHTML = '<span class="badge bg-danger">✗ Отрицательный конец</span>';
|
||||
} else {
|
||||
previewEnd.innerHTML = '<span class="badge bg-secondary">Промежуточный</span>';
|
||||
}
|
||||
}
|
||||
|
||||
nameInput.addEventListener('input', updatePreview);
|
||||
codeInput.addEventListener('input', updatePreview);
|
||||
colorInput.addEventListener('change', updatePreview);
|
||||
positiveEndCheckbox.addEventListener('change', updatePreview);
|
||||
negativeEndCheckbox.addEventListener('change', updatePreview);
|
||||
|
||||
// Инициальное обновление
|
||||
updatePreview();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
173
myproject/orders/templates/orders/status_list.html
Normal file
173
myproject/orders/templates/orders/status_list.html
Normal file
@@ -0,0 +1,173 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Статусы заказов{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid mt-4">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<h1>Статусы заказов</h1>
|
||||
<p class="text-muted">Управление статусами для заказов вашего магазина</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-end">
|
||||
<a href="{% url 'orders:status_create' %}" class="btn btn-primary">
|
||||
<i class="fas fa-plus"></i> Создать новый статус
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if messages %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width: 50px;">№</th>
|
||||
<th style="width: 200px;">Название</th>
|
||||
<th style="width: 150px;">Код</th>
|
||||
<th style="width: 150px;">Тип</th>
|
||||
<th style="width: 100px;">Конец</th>
|
||||
<th style="width: 80px;">Цвет</th>
|
||||
<th style="width: 80px;">Заказов</th>
|
||||
<th style="width: 150px;">Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for status in statuses %}
|
||||
<tr>
|
||||
<td>
|
||||
<span class="badge bg-secondary">{{ status.order }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<strong>{{ status.name }}</strong>
|
||||
{% if status.description %}
|
||||
<br>
|
||||
<small class="text-muted">{{ status.description|truncatewords:10 }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ status.code }}</code>
|
||||
</td>
|
||||
<td>
|
||||
{% if status.is_system %}
|
||||
<span class="badge bg-info">Системный</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">Пользовательский</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if status.is_positive_end %}
|
||||
<span class="badge bg-success">✓ Успешный</span>
|
||||
{% elif status.is_negative_end %}
|
||||
<span class="badge bg-danger">✗ Отрицательный</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div style="width: 40px; height: 30px; background-color: {{ status.color }}; border-radius: 4px; border: 1px solid #ccc;" title="{{ status.color }}"></div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-light text-dark">{{ status.orders_count }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<a href="{% url 'orders:status_edit' status.pk %}" class="btn btn-outline-primary" title="Редактировать">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
{% if not status.is_system and status.orders_count == 0 %}
|
||||
<a href="{% url 'orders:status_delete' status.pk %}" class="btn btn-outline-danger" title="Удалить">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-outline-secondary" disabled title="{% if status.is_system %}Системный статус{% else %}В статусе есть заказы{% endif %}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if not forloop.first %}
|
||||
<form method="post" action="{% url 'orders:status_move' status.pk %}" class="d-inline" style="display: none;" id="move-up-form-{{ status.pk }}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="direction" value="up">
|
||||
</form>
|
||||
<button class="btn btn-outline-secondary" onclick="document.getElementById('move-up-form-{{ status.pk }}').submit();" title="Вверх">
|
||||
<i class="fas fa-arrow-up"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if not forloop.last %}
|
||||
<form method="post" action="{% url 'orders:status_move' status.pk %}" class="d-inline" style="display: none;" id="move-down-form-{{ status.pk }}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="direction" value="down">
|
||||
</form>
|
||||
<button class="btn btn-outline-secondary" onclick="document.getElementById('move-down-form-{{ status.pk }}').submit();" title="Вниз">
|
||||
<i class="fas fa-arrow-down"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="8" class="text-center text-muted py-4">
|
||||
<i class="fas fa-inbox"></i> Статусы не найдены
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if is_paginated %}
|
||||
<nav aria-label="Page navigation" class="mt-3">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page=1">Первая</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Предыдущая</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for num in page_obj.paginator.page_range %}
|
||||
{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<span class="page-link">{{ num }}</span>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Следующая</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Последняя</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user