La dernière fois, dans Django Tutorial (Create Blog App) ⑥ --Article Details / Edit / Delete Function, nous avons créé les écrans de détails, d'édition et de suppression de chaque article. ..
Cette fois, nous apporterons des ajustements majeurs au modèle, mais si nous le divisons grossièrement, nous ferons ce qui suit.
Créer un écran commun à toutes les pages
Création d'une barre de navigation
Modifiez chaque modèle
Supprimez les modèles et processus inutiles
Non limité à Django, il y a des endroits sur la page d'accueil qui sont affichés en commun même si l'écran change. Dans Qiita, la barre de navigation verte affichée en haut est un bon exemple.
↓ Ce Cependant, il est difficile de l'écrire dans chaque modèle à chaque fois. Si j'écris le code une fois et que je le termine, mais quand je pense au moment où la correction a été faite ...
Par conséquent, nous utiliserons le ** modèle commun ** comme fonction pratique de Django. En termes simples, les parties communes sont rassemblées dans un seul fichier, La partie qui diffère d'un écran à l'autre est que différents modèles sont appelés et utilisés.
Pour ce faire, créez d'abord un fichier directement sous le dossier du modèle. Cette fois, créons un fichier appelé /template/base.html.
└── templates
├── base.html #ajouter à
└── blog
├── index.html
├── post_confirm_delete.html
├── post_detail.html
├── post_form.html
└── post_list.html
Écrire le traitement commun dans ce fichier et les parties qui diffèrent pour chaque écran J'appellerai un fichier tel que post_detail.html.
Le contenu sera comme ça.
base.html
<!doctype html>
<html lang="ja">
<head>
<title>Le blog de tmasuyama</title>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
</head>
<body>
<div class="container">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{% url 'blog:post_list' %}">Haut</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'blog:post_create' %}">Publier</a>
</li>
</ul>
</div>
</nav>
<!--Le contenu de chaque modèle est appelé dans ce bloc-->
{% block content %} #Attention!
{% endblock %} #Attention!
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
</body>
</html>
Ce tutoriel ne rentre pas dans les détails sur le frontal, Vous pouvez appeler et utiliser Bootstrap à partir du CDN pour le rendre beau avec Bootstrap, La barre de navigation est représentée dans
Ce à quoi je voudrais que vous fassiez attention, c'est la partie où vous avez noté "** # Attention! **".
<!--Le contenu de chaque modèle est appelé dans ce bloc-->
{% block content %} #Attention!
{% endblock %} #Attention!
Lors de l'appel de modèles selon View, chaque modèle sera stocké ici. Pour le dire dans l'autre sens, chaque modèle n'a besoin que d'écrire les parties caractéristiques sur chaque page.
De plus, il est nécessaire de spécifier le modèle parent (base.html) dans le modèle appelé. La méthode d'écriture de base est la suivante.
Chaque modèle
{% extends 'base.html' %} #Spécifier le modèle parent
{% block content %} #Commencez à décrire le contenu
...Description spécifique à chaque modèle...
{% endblock %} #Fin de la description du contenu
Vous pouvez désormais séparer clairement les rôles du modèle parent et du modèle enfant. Ensuite, j'expliquerai la barre de navigation préparée par le modèle parent.
En utilisant le fichier base.html ci-dessus, la barre de navigation suivante sera affichée. Cette fois, je fais référence à Cheat Sheat de Bootstrap4. https://hackerthemes.com/bootstrap-cheatsheet/#navbar
Sélectionnez ** Top ** pour passer à l'écran post_list.html. Si vous sélectionnez ** Publier **, vous serez redirigé vers l'écran post_form.html (nouvel écran de publication).
La partie de base.html pour afficher la barre de navigation était ici.
base.html
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a class="navbar-brand" href="{% url 'blog:post_list' %}">Haut</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'blog:post_create' %}">Publier</a>
</li>
</ul>
</div>
</nav>
La partie importante ici est ** "href =" {% url'blog: post_list '%} "" **.
Jusqu'à présent, le modèle ne décrivait pas le lien, Si vous écrivez une URL inversée au format ci-dessus Vous allez maintenant être redirigé vers l'URL qui correspond à .
urls.py
...
path('post_list', views.PostListView.as_view(), name='post_list'),
...
Merci de donner à chaque URL un nom comme ** name = 'post_list' ** ici En spécifiant le nom du côté du modèle, il sera automatiquement acheminé.
Maintenant, ajustons l'apparence avec Bootstrap tout en spécifiant le modèle parent pour chaque modèle. Je mettrai le formulaire rempli.
post_detail.html
{% extends 'base.html' %}
{% block content %}
<table class="table">
<tr>
<th>Titre</th>
<td>{{ post.title }}</td>
</tr>
<tr>
<th>Texte</th>
<!--Si vous insérez linebreaksbk, il s'affichera correctement avec une balise de saut de ligne.-->
<td>{{ post.text | linebreaksbr}}</td>
</tr>
<tr>
<th>Date</th>
<td>{{ post.date }}</td>
</tr>
</table>
{% endblock %}
post_form.html
{% extends 'base.html' %}
{% block content %}
<p>{{ post.title }}</p>
<!--Envoyer des informations à l'action à quelle URL sur le serveur-->
<!--Si vous laissez l'action vide, l'URL actuellement ouverte= /blog/post_Renvoie une valeur pour créer, donc vues.PostCreateView de py sera appelé à nouveau-->
<form action="" method="POST">
<table class="table">
<tr>
<th>Titre</th>
<td>{{ form.title }}</td>
</tr>
<tr>
<th>Texte</th>
<td>{{ form.text }}</td>
</tr>
</table>
<button type="submit" class="btn btn-primary">Envoyer</button>
{% csrf_token %}
</form>
{% endblock %}
post_confirm_delete.html
{% extends 'base.html' %}
{% block content %}
<form action="" method="POST">
<table class="table">
<tr>
<th>Titre</th>
<td>{{ post.title }}</td>
</tr>
<tr>
<th>Texte</th>
<td>{{ post.text }}</td>
</tr>
<tr>
<th>Date</th>
<td>{{ post.date }}</td>
</tr>
</table>
<p>Supprimez ces données.</p>
<button type="submit">Envoyer</button>
{% csrf_token %}
</form>
{% endblock %}
post_list.html
{% extends 'base.html' %}
{% block content %}
<table class="table">
<thead>
<tr>
<th>Titre</th>
<th>Date</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{% for post in post_list %}
<tr>
<!-- 「url 'nom de l'application:URL inversée'Modèle adopté.Comment dessiner "pk"-->
<td><a href="{% url 'blog:post_detail' post.pk %}">{{ post.title }}</a></td>
<td>{{ post.date }}</td>
<td>
<!--S'affiche uniquement lorsque vous êtes connecté en tant que superutilisateur-->
{% if user.is_superuser %}
<!--Nom de l'application HTML_Nom du modèle_Si vous le modifiez, vous pouvez le modifier tel quel avec l'administrateur-->
<a href="{% url 'blog:post_update' post.pk %}">Éditer</a>
{% endif %}
</td>
<td>
{% if user.is_superuser %}
<a href="{% url 'blog:post_delete' post.pk %}">Effacer</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
Le dernier post_list.html a quelques modifications supplémentaires.
La première consiste à ajouter des détails, à modifier et à supprimer des liens pour chaque article affiché dans la liste. Si la destination du lien est déterminée en fonction de la clé primaire, vous pouvez spécifier la clé primaire sous la forme de ** nom de variable.pk **.
N'oubliez pas que la forme spécifiée par le nom de l'URL inversée est une manière fixe d'écrire.
<a href="{% url 'blog:post_detail' post.pk %}">...
De plus, bien que ce didacticiel n'implémente pas la fonction d'enregistrement des utilisateurs, Limitez l'article afin que personne ne puisse le modifier ou le supprimer.
Cette fois, il n'y a que super-utilisateur, donc Uniquement si vous êtes connecté depuis l'écran d'administration (127.0.0.1:8000/admin) en tant que superutilisateur Affiche des liens pour modifier et supprimer des articles.
{% if user.is_superuser %} #Afficher le contenu de l'instruction if uniquement lorsque vous êtes connecté en tant que superutilisateur
<a href="{% url 'blog:post_update' post.pk %}">Éditer</a>
{% endif %}
Dans ce qui précède, c'était l'heure du superutilisateur, mais l'affichage lors de la connexion en tant qu'autre utilisateur, Il est également possible de l'afficher uniquement lorsque vous êtes connecté en tant qu'utilisateur spécifique.
Eh bien, j'ai laissé une page index.html juste pour afficher Hello pour la pratique, Si vous en laissez plus, cela prendra plus de temps à gérer, alors supprimez-le à ce moment.
└── templates
├── base.html
└── blog
├── index.html #Supprime ça
├── post_confirm_delete.html
├── post_detail.html
├── post_form.html
└── post_list.html
N'oubliez pas de modifier également urls.py test_urls.py ,, views.py, test_views.py.
urls.py
...
urlpatterns = [
path('', views.IndexView.as_view(), name='index'), #Supprimer ici
...
test_urls.py
...
class TestUrls(TestCase):
"""Redirection testée lors de l'accès par URL à la page d'index"""
def test_post_index_url(self): #Supprimer complètement cette méthode
view = resolve('/blog/')
self.assertEqual(view.func.view_class, IndexView)
class TestUrls(TestCase):
"""Redirection testée lors de l'accès par URL à la page d'index"""
def test_post_index_url(self): #Supprimer cette méthode
view = resolve('/blog/')
self.assertEqual(view.func.view_class, IndexView)
...
views.py
...
class IndexView(generic.TemplateView): #Supprimer cette vue générique
template_name = 'blog/index.html'
...
test_views.py
...
class IndexTests(TestCase): #Supprimer cette classe de test
"""Classe de test IndexView"""
def test_get(self):
"""Confirmez que le code d'état 200 est renvoyé en accédant avec la méthode GET."""
response = self.client.get(reverse('blog:index'))
self.assertEqual(response.status_code, 200)
...
Nous avons effectué les modifications en une seule fois, alors exécutons enfin un test unitaire pour voir s'il y a des erreurs.
(blog) bash-3.2$ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..............E
======================================================================
ERROR: blog.tests.test_urls (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: blog.tests.test_urls
Traceback (most recent call last):
File "/usr/local/Cellar/[email protected]/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/unittest/loader.py", line 436, in _find_test_path
module = self._get_module_from_name(name)
File "/usr/local/Cellar/[email protected]/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3.8/unittest/loader.py", line 377, in _get_module_from_name
__import__(name)
File "/Users/masuyama/workspace/MyPython/MyDjango/blog/mysite/blog/tests/test_urls.py", line 3, in <module>
from ..views import IndexView, PostListView
ImportError: cannot import name 'IndexView' from 'blog.views' (/Users/masuyama/workspace/MyPython/MyDjango/blog/mysite/blog/views.py)
----------------------------------------------------------------------
Ran 15 tests in 0.283s
FAILED (errors=1)
Destroying test database for alias 'default'...
Une erreur a été confirmée. Puisque le message d'erreur est affiché à l'aide de délimiteurs, etc., il est facile de placer là où l'erreur se produit.
Si vous suivez le message d'erreur, vous constaterez que certains test_urls.py ne peuvent pas être importés et qu'une erreur s'est produite.
ImportError: cannot import name 'IndexView' from 'blog.views'
Quand j'ai relu test_urls.py, j'ai constaté qu'il laissait l'IndexView chargé au début.
test_urls.py
from django.test import TestCase
from django.urls import reverse, resolve
from ..views import IndexView, PostListView #Cette ligne
Effacez-le et procédez comme suit.
test_urls.py
from django.test import TestCase
from django.urls import reverse, resolve
from ..views import PostListView
Maintenant, exécutons à nouveau le test unitaire.
(blog) bash-3.2$ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...............
----------------------------------------------------------------------
Ran 15 tests in 0.223s
OK
Destroying test database for alias 'default'...
Cette fois, le test s'est terminé sans erreur. De plus, le nombre de tests est de 15, ce qui est cohérent avec la réduction de 2 méthodes de test par rapport aux 17 tests précédents.
Jusqu'à présent, je n'ai montré que le résultat de la réussite du test unitaire, Même lorsque des modifications se produisent dans plusieurs fichiers à la fois comme cette fois J'espère que vous avez découvert que si vous préparez un test unitaire à l'avance, vous pouvez identifier la zone à problème avec une seule commande.
Vous avez terminé avec succès votre application Django locale!
La prochaine fois, faisons de l'application que nous avons créée cette fois un Docker lorsqu'il s'agit d'améliorer l'environnement.
Recommended Posts