Таблица на django, позволяющая редактировать, добавлять и удалять строки
Я только начинаю изучать django, подскажите, пожалуйста, какой способ лучше всего подойдет и будет проще всего в реализации следующей задачи:
Необходимо создать таблицу, которая будет иметь возможность добавлять новые строки, редактировать старые, либо удалять их. Объем данных, помещаемых в таблицу, небольшой, максимальное количество строк 5-7. Буду благодарен за любую помощь, в том числе ссылки на документацию, в которой можно подчерпнуть полезное по данной теме.
Не сердитесь, если данная тема уже была затронута в обсуждении. Ранее я нашел решение своей проблемы по следующей ссылке (https://stackoverflow.com/a/70352509/17641696), но так и не смог понять, зачем автор использует скрипт: разве он не дублирует то, что описано в views.py?
Также прошу Вас, если вы заметите какую-то глупость в моем коде, пожалуйста, укажите мне на это.
На текущий момент у меня имеется следующий код:
Forms.py
class AkpGroupForm(ModelForm):
required_css_class = 'required'
class Meta:
model = AkpGroup
fields = ['Id_Incidents', 'Type_Participation', 'Id_People', 'Competencies']
exclude = ('Id_Incidents',)
widgets = {
"Id_Incidents": Select(attrs={
'class': 'form-select'
}),
"Type_Participation": Select(attrs={
'class': 'form-select',
'style': 'height: 40px'
}),
"Id_People": Select(attrs={
'class': 'form-select',
'style': 'height: 40px'
}),
"Competencies": Textarea(attrs={
'class': 'form-select',
'style': 'height: 40px'
})
}
class AkpCheckResultForm(ModelForm):
required_css_class = 'required'
class Meta:
model = AkpCheckResult
fields = ['Id_Incidents', 'Inspector', 'Company', 'Post', 'Basis_Attraction', 'Check_Area']
exclude = ('Id_Incidents',)
widgets = {
"Id_Incidents": Select(attrs={
'class': 'form-select'
}),
"Inspector": Select(attrs={
'class': 'form-select',
'style': 'height: 60px'
}),
"Company": Textarea(attrs={
'class': 'form-select',
'style': 'height: 60px'
}),
"Post": Textarea(attrs={
'class': 'form-select',
'style': 'height: 60px'
}),
"Basis_Attraction": Textarea(attrs={
'class': 'form-select',
'style': 'height: 60px'
}),
"Check_Area": Textarea(attrs={
'class': 'form-select',
'style': 'height: 60px'
})
}
class AkpMeetingForm(ModelForm):
required_css_class = 'required'
class Meta:
model = AkpMeeting
fields = ['Id_Incidents', 'Date_Time', 'Location', 'Responsible']
exclude = ('Id_Incidents',)
widgets = {
"Id_Incidents": Select(attrs={
'class': 'form-select'
}),
"Date_Time": DateTimeInput(attrs={
'class': 'form-control',
'type': 'Date'
}),
"Location": TextInput(attrs={
'class': 'form-select',
'style': 'height: 40px'
}),
"Responsible": TextInput(attrs={
'class': 'form-select'
})
}
class GroupCheckResultMeetingCreationMultiForm(MultiModelForm):
form_classes = {
'Group': AkpGroupForm,
'CheckResult': AkpCheckResultForm,
'Meeting': AkpMeetingForm
}
Models.py
class AkpGroup(models.Model):
Id_Group = models.AutoField(primary_key=True, verbose_name='Идентификатор участника группы АКП')
Id_Incidents = models.ForeignKey('AkpIncidents', models.DO_NOTHING, verbose_name='Идентификатор инцидента')
# stage 3 fields
Id_People = models.ManyToManyField('AkpPeople', verbose_name='Ф.И.О. участника')
Competencies = models.CharField(max_length=256, verbose_name='Компетенции')
LOAN_TYPE_PARTICIPATION = (
('d', 'Руководитель группы'),
('p', 'Член группы')
)
Type_Participation = models.CharField(max_length=1, choices=LOAN_TYPE_PARTICIPATION, default='p',
verbose_name='Тип участия')
# Вспомогательные
Change_Time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name='Время изменения')
Change_User = models.CharField(max_length=256, verbose_name='Пользователь, внесший изменения')
Del_Date = models.DateTimeField(auto_now=False, blank=True, null=True, verbose_name='Дата удаления')
Del_User = models.CharField(max_length=256, verbose_name='Пользователь, удаливший данные')
Is_Delete = models.BooleanField(default=False, verbose_name='Удален')
Is_Posted = models.BooleanField(default=False, verbose_name='Опубликовано')
objects = models.Manager()
def display_id_people(self):
"""
Creates a string for the Genre. This is required to display genre in Admin.
"""
return ', '.join([Id_People.Name for Id_People in self.Id_People.all()[:10]])
display_id_people.short_description = 'AkpGroup'
def __str__(self):
return self.Competencies
class Meta:
db_table = 'Akp_Group'
verbose_name = 'Группа АКП'
verbose_name_plural = 'Группы АКП'
# hidden_fields = ['stage']
# required_fields = ['Id_People', 'Competencies']
# Основная таблица: 4) Проверка результатов
class AkpCheckResult(models.Model):
Id_CheckResult = models.AutoField(primary_key=True, verbose_name='Идентификатор проверки результатов')
Id_Incidents = models.ForeignKey('AkpIncidents', models.DO_NOTHING, verbose_name='Идентификатор инцидента')
# stage 3 fields
LOAN_INSPECTOR = (
('n', 'Независимый эксперт'),
('z', 'Заинтересованная сторона'),
)
Inspector = models.CharField(max_length=1, choices=LOAN_INSPECTOR, verbose_name='Тип участия')
Company = models.CharField(max_length=256, verbose_name='Организация')
Post = models.CharField(max_length=256, verbose_name='Должность:')
Basis_Attraction = models.CharField(max_length=256, verbose_name='Основание для привлечения:')
Check_Area = models.CharField(max_length=256, verbose_name='Область проверки:')
# Вспомогательные
Change_Time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name='Время изменения')
Change_User = models.CharField(max_length=256, verbose_name='Пользователь, внесший изменения')
Del_Date = models.DateTimeField(auto_now=False, blank=True, null=True, verbose_name='Дата удаления')
Del_User = models.CharField(max_length=256, verbose_name='Пользователь, удаливший данные')
Is_Delete = models.BooleanField(default=False, verbose_name='Удален')
Is_Posted = models.BooleanField(default=False, verbose_name='Опубликовано')
objects = models.Manager()
def __str__(self):
return self.Check_Area
class Meta:
db_table = 'Akp_Check_Result'
verbose_name = 'Проверка результатов'
verbose_name_plural = 'Проверка результатов'
# hidden_fields = ['stage']
# required_fields = ['Id_Inspector', 'Company', 'Post', 'Basis_Attraction', 'Check_Area']
# Основная таблица: 5) Совещание
class AkpMeeting(models.Model):
Id_Meeting = models.AutoField(primary_key=True, verbose_name='Идентификатор совещания')
Id_Incidents = models.ForeignKey('AkpIncidents', models.DO_NOTHING, verbose_name='Идентификатор инцидента')
# stage 3 fields
Date_Time = models.DateTimeField(auto_now=False, blank=True, null=True, verbose_name='Дата/время совещания')
Location = models.CharField(max_length=256, verbose_name='Место проведения совещания:')
Responsible = models.CharField(max_length=256, verbose_name='Ответственный за организацию совещания:')
# Вспомогательные
Change_Time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name='Время изменения')
Change_User = models.CharField(max_length=256, verbose_name='Пользователь, внесший изменения')
Del_Date = models.DateTimeField(auto_now=False, blank=True, null=True, verbose_name='Дата удаления')
Del_User = models.CharField(max_length=256, verbose_name='Пользователь, удаливший данные')
Is_Delete = models.BooleanField(default=False, verbose_name='Удален')
Is_Posted = models.BooleanField(default=False, verbose_name='Опубликовано')
objects = models.Manager()
def __str__(self):
return self.Location
class Meta:
db_table = 'Akp_Meeting'
verbose_name = 'Совещание'
verbose_name_plural = 'Совещания'
# hidden_fields = ['stage']
# required_fields = ['Date_Time', 'Location', 'Responsible']
Urls.py
app_name = "main"
urlpatterns = [
path('', views.start, name="start"),
path('incident', views.incident, name="incident"),
path('victim', views.victim, name="victim"),
path('group', views.Groups.as_view(), name='group'),
path('AdditionalDetail', views.additional_detail, name='AdditionalDetail'),
path('events', views.events, name='events'),
path('monitoring', views.monitoring, name='monitoring'),
path(
'url_to_django/delete-person/<int:Id_Group>',
views.delete_person,
name='delete_person'),
path(
'url_to_django/add-person/',
views.add_person,
name='add_person'),
path(
'url_to_django/load_table/',
views.load_table,
name='load_table')
html
{% block content %}
<!--В цикле перечисленные все скрытые поля-->
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
<!--В цикле перечисленные все видимые поля указанные в AkpGroupForm в forms.py-->
<table class="table align-middle" id="table_person_id">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">#</th>
{% if form.Group.visible_fields %}
{% for field in form.Group.visible_fields %}
<th scope="col">{{ field.label }}</th>
{% endfor %}
{% endif %}
<th>Действие</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row"><div><input class="form-check-input" type="checkbox" id="checkboxNoLabel1" value="" aria-label="..."></div></th>
<th scope="row">1</th>
{% if form.Group.visible_fields %}
{% for field in form.Group.visible_fields %}
<td>{{ field }}</td>
{% endfor %}
{% endif %}
<td>
<button value="{{AkpGroup.Id_Group}}" onclick=delete_person>Удалить</button>
</td>
</tr>
</tbody>
<tfoot>
<form method="POST" onsubmit=add_person>
<tr>
<th scope="row"></th>
<th scope="row"></th>
{% if form.Group.visible_fields %}
{% for field in form.Group.visible_fields %}
<td>{{ field }}</td>
{% endfor %}
{% endif %}
<td>
<button type="submit">Добавить</button>
</td>
</tr>
</form>
</tfoot>
</table>
<div class="container overflow-hidden">
<div class="row gy-5">
<div class="col-md-6">
<div class="p-3 bg-light"><span class="pull-right"></span>
<a href="{% url 'main:start' %}" class="p-3 bg-light"><button type="button" class="btn btn-outline-secondary">Отмена</button></a>
</div>
</div>
<div class="col-md-6">
<div class="p-3 bg-light"><span class="pull-right"></span>
<div class="btn-group" role="group" aria-label="Basic mixed styles example">
<button type="submit" class="btn btn-outline-success" name="back">Предыдущий шаг</button>
<button type="submit" class="btn btn-outline-warning" name="next">Следующий шаг</button>
</div>
</div>
</div>
</div>
</div>
<!--</form>-->
{% endblock %}
Views.py
# Страница 3 из 6
class Groups(CreateView):
error = ''
form_class = GroupCheckResultMeetingCreationMultiForm
success_url = reverse_lazy('group')
template_name = 'main/group.html'
def form_valid(self, form):
if form.is_valid() and 'next':
group_created = form['Group'].save(commit=False)
group_created.Id_Incidents = get_object_or_404(AkpIncidents, Id_Incidents=id_incidents)
group_created.save()
# check_result_created = form['CheckResult'].save(commit=False)
# check_result_created.Id_Incidents = get_object_or_404(AkpIncidents, Id_Incidents=id_incidents)
# check_result_created.save()
#
# meeting_created = form['Meeting'].save(commit=False)
# meeting_created.Id_Incidents = get_object_or_404(AkpIncidents, Id_Incidents=id_incidents)
# meeting_created.save()
else:
error = 'Форма заполнена некорректно'
# elif 'back' in request.POST:
# return redirect('main:incident')
data = {
'form': 'form',
'error': 'error'
}
return redirect(self.get_success_url(), data)
def load_table(request, Id_Group):
if request.method == "GET":
if request.user:
person = AkpGroup.objects.get(id=Id_Group) # запрос в базу данных
return render(request, 'main/group.html', {"person": person})
else:
return HttpResponse(status=403)
else:
return HttpResponse(status=405)
def delete_person(request, Id_Group):
if request.method == "DELETE":
if request.user:
person_to_delete = AkpGroup.objects.get(id=Id_Group)
person_to_delete.delete()
return HttpResponse(status=200)
else:
return HttpResponse(status=403)
else:
return HttpResponse(status=405)
def add_person(request, codorcam):
if request.method == "POST":
if request.user:
form = AkpGroupForm(request.POST) # форма, созданная в forms.py чтобы добавить участника
if form.is_valid():
group_created = form['Group'].save(commit=False)
group_created.Id_Incidents = get_object_or_404(AkpIncidents, Id_Incidents=id_incidents)
group_created.save()
return HttpResponse(status=201)
else:
return HttpResponse(status=400)
else:
return HttpResponse(status=403)
else:
return HttpResponse(status=405)