Let's display a list of child's impressions for the parent's book.
In cms / models.py
, the impression defined the book as a foreign key.
class Impression(models.Model):
"""Impressions"""
book = models.ForeignKey(Book, verbose_name='Books', related_name='impressions', on_delete=models.CASCADE)
comment = models.TextField('comment', blank=True)
Therefore, the child's impressions associated with the book can be read out as follows using related_name. Again, I don't write SQL.
impressions = book.impressions.all().order_by('id') #Read children's impressions of books
This time, I will write using ListView of general view
.
With this, paging etc. can be easily realized.
Add the following description to cms / views.py
.
from django.views.generic.list import ListView
:
class ImpressionList(ListView):
"""List of impressions"""
context_object_name='impressions'
template_name='cms/impression_list.html'
paginate_by = 2 #Page up to 2 pages per page
def get(self, request, *args, **kwargs):
book = get_object_or_404(Book, pk=kwargs['book_id']) #Read parents' books
impressions = book.impressions.all().order_by('id') #Read children's impressions of books
self.object_list = impressions
context = self.get_context_data(object_list=self.object_list, book=book)
return self.render_to_response(context)
Applying BootStrap,
I am doing that. It's a little long.
Create mybook / cms / templates / cms / impression_list.html
by inheriting mybook / cms / templates / cms / base_html
.
{% extends "cms/base.html" %}
{% block title %}List of impressions{% endblock title %}
{% block content %}
<h4 class="mt-4 border-bottom">List of impressions<small class="text-muted ml-3">{{ book.name }}</small></h4>
<a href="{% url 'cms:impression_add' book_id=book.id %}" class="btn btn-primary btn-sm my-3">add to</a>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>comment</th>
<th>operation</th>
</tr>
</thead>
<tbody>
{% for impression in impressions %}
<tr>
<td>{{ impression.id }}</td>
<td>{{ impression.comment|linebreaksbr }}</td>
<td>
<a href="{% url 'cms:impression_mod' book_id=book.id impression_id=impression.id %}" class="btn btn-outline-primary btn-sm">Fix</a>
<button class="btn btn-outline-danger btn-sm del_confirm" data-toggle="modal" data-target="#deleteModal" data-pk="{{ impression.id }}" data-url="{% url 'cms:impression_del' book_id=book.id impression_id=impression.id %}">Delete</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if is_paginated %}
<nav aria-label="Page navigation">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous"><span aria-hidden="true">«</span><span class="sr-only">Previous</span></a></li>
{% else %}
<li class="page-item disabled"><a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">«</span><span class="sr-only">Previous</span></a></li>
{% endif %}
{% for linkpage in page_obj.paginator.page_range %}
{% ifequal linkpage page_obj.number %}
<li class="page-item active"><a class="page-link" href="#">{{ linkpage }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ linkpage }}">{{ linkpage }}</a></li>
{% endifequal %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next"><span aria-hidden="true">»</span><span class="sr-only">Next</span></a></li>
{% else %}
<li class="page-item disabled"><a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">»</span><span class="sr-only">Next</span></a></li>
{% endif %}
</ul>
</nav>
{% endif %}
<a href="{% url 'cms:book_list' %}" class="btn btn-secondary btn-sm">Return</a>
{#Modal dialog confirming deletion#}
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">Verification</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
<p>ID: <span id="del_pk"></span>Do you want to delete?</p>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" id="del_url">OK</a>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
{% endblock content %}
{% block extra_js %}
<script>
$(function() {
$('.del_confirm').on('click', function () {
$("#del_pk").text($(this).data("pk"));
$('#del_url').attr('href', $(this).data("url"));
});
});
</script>
{% endblock %}
The completed list page will be as follows. (Still, it will not work unless you do the future, so please think of it as such an image)
Add the following to cms / forms.py
.
Here we will create the underlying form for adding and modifying Impression models in cms / models.py
.
:
from cms.models import Book, Impression
:
class ImpressionForm(ModelForm):
"""Impression form"""
class Meta:
model = Impression
fields = ('comment', )
Add the following to cms / views.py
.
:
from cms.models import Book, Impression
from cms.forms import BookForm, ImpressionForm
:
def impression_edit(request, book_id, impression_id=None):
"""Editing impressions"""
book = get_object_or_404(Book, pk=book_id) #Read parents' books
if impression_id: # impression_id is specified(At the time of correction)
impression = get_object_or_404(Impression, pk=impression_id)
else: # impression_id not specified(At the time of addition)
impression = Impression()
if request.method == 'POST':
form = ImpressionForm(request.POST, instance=impression) #Create a form from POSTed request data
if form.is_valid(): #Form validation
impression = form.save(commit=False)
impression.book = book #Set the parent's book of this impression
impression.save()
return redirect('cms:impression_list', book_id=book_id)
else: #At the time of GET
form = ImpressionForm(instance=impression) #Create a form from an impression instance
return render(request,
'cms/impression_edit.html',
dict(form=form, book_id=book_id, impression_id=impression_id))
Create mybook / cms / templates / cms / impression_edit.html
by inheriting mybook / templates / base_html
.
{% extends "cms/base.html" %}
{% load bootstrap4 %}
{% block title %}Editing impressions{% endblock title %}
{% block content %}
<h4 class="mt-4 mb-5 border-bottom">Editing impressions</h4>
{% if impression_id %}
<form action="{% url 'cms:impression_mod' book_id=book_id impression_id=impression_id %}" method="post">
{% else %}
<form action="{% url 'cms:impression_add' book_id=book_id %}" method="post">
{% endif %}
{% csrf_token %}
{% bootstrap_form form layout='horizontal' %}
<div class="form-group row">
<div class="offset-md-3 col-md-9">
<button type="submit" class="btn btn-primary">Send</button>
</div>
</div>
</form>
<a href="{% url 'cms:impression_list' book_id=book_id %}" class="btn btn-secondary btn-sm">Return</a>
{% endblock content %}
The page for addition and modification is as follows. (Still, it will not work unless you do the future, so please think of it as such an image)
Add the following to cms / views.py
.
This time, instead of erasing it suddenly, I'm displaying a modal dialog in Bootstrap and issuing a confirmation message. However, the contents of the view are the same as those of the parent's book.
def impression_del(request, book_id, impression_id):
"""Delete impressions"""
impression = get_object_or_404(Impression, pk=impression_id)
impression.delete()
return redirect('cms:impression_list', book_id=book_id)
Add the following to cms / urls.py
.
urlpatterns = [
:
#Impressions
path('impression/<int:book_id>/', views.ImpressionList.as_view(), name='impression_list'), #List
path('impression/add/<int:book_id>/', views.impression_edit, name='impression_add'), #Registration
path('impression/mod/<int:book_id>/<int:impression_id>/', views.impression_edit, name='impression_mod'), #Fix
path('impression/del/<int:book_id>/<int:impression_id>/', views.impression_del, name='impression_del'), #Delete
]
Add a link so that you can get an "impression list" of the corresponding book from the list of parent books.
Add a line to mybook / cms / templates / cms / book_list.html
.
<td>
<a href="{% url 'cms:book_mod' book_id=book.id %}" class="btn btn-outline-primary btn-sm">Fix</a>
<a href="{% url 'cms:book_del' book_id=book.id %}" class="btn btn-outline-danger btn-sm">Delete</a>
<a href="{% url 'cms:impression_list' book_id=book.id %}" class="btn btn-outline-info btn-sm">List of impressions</a>
</td>
Now, start the local server, follow the "List of impressions" from the "List of books", and register / modify / delete the impressions.
http://127.0.0.1:8000/cms/book/
If you can CRUD a model with a parent-child relationship as explained so far, the rest is how to design the model, so I think that you can apply it to make various things.
Continue to Introduction to Python Django (6).
Recommended Posts