Commit 9b72aa6b authored by Bonnegent Sebastien's avatar Bonnegent Sebastien
Browse files

cours 6 ok

parent 6610b84e
all: cours1.html cours2.html cours3.html cours4.html cours5.html
all: cours1.html cours2.html cours3.html cours4.html cours5.html cours6.html
%.html: %.md
pandoc -s -t revealjs -V revealjs-url=./reveal.js -V theme=beige -o $@ $<
......@@ -37,12 +37,16 @@ séance.
- supprimer
## Cours 6
- utilisation de black
- tests
- afficher les règles
- modifier les règles
- supprimer les règles
- utilisation avancée des templates
## Cours 7
- utilisation de black
- tests
- configuration en production
- QCM
- API Rest
- utilisation de docker
# Comment démarrer ?
......
This diff is collapsed.
---
author:
- Sébastien Bonnegent
title: Django par la pratique 6/7
---
# Départ
## Support
- cours6.html
## Préparation
### L'ancien
~~~
$ git pull
$ cd fwm5
$ pipenv --rm
~~~
### Le nouveau
~~~
$ cd fwm6
$ pipenv install
~~~
## Au programme
- afficher les règles
- modifier les règles
- supprimer les règles
- utilisation avancée des templates
# Règle
## Liste #1
* https://fontawesome.com/
* liste des règles du parefeu
* liste des règles du modèle
* factorisation du template
## Liste #2
### webui/templates/webui/regle_list.html
~~~python
{% block content %}
<table class="table table-striped">
<thead>
<tr>
<th>priorite</th>
<th>groupe</th>
<th>services</th>
<th>interface</th>
<th>actif</th>
<th>sens</th>
</tr>
</thead><tbody>
~~~
## Liste #3
### webui/templates/webui/regle_list.html
~~~python
{% for regle in regle_list %}
<tr {% if not regle.actif %}class="text-muted"{% endif %}>
<td>{{regle.priorite}}</td>
<td>{% if regle.groupe %}{{regle.groupe}}{% endif %}</td>
<td>{{regle.liste_des_services}}</td>
<td>{{regle.interface}}</td>
<td>
{% if regle.actif %}
<i class="far fa-check-circle"></i>
{% else %}
<i class="fas fa-ban"></i>
{% endif %}
</td>
<td>{{regle.get_sens_display}}</td>
</tr>
~~~
## Liste #3
### webui/templates/webui/regle_list.html
~~~python
{% empty %}
<tr>
<td colspan=6>aucune règle</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
~~~
## Utilisation
### webui/templates/webui/parefeu_detail.html
~~~python
{% if parefeu.modele %}
<h3>Règles issues du modèle:
<a href="{% url 'parefeu-detail' parefeu.modele_id %}">
{{parefeu.modele}}</a></h3>
{% include "webui/regle_list.html"
with regle_list=parefeu.modele.regle_set.all only %}
{% endif %}
<h3>Règles propre au parefeu</h3>
{% include "webui/regle_list.html"
with regle_list=parefeu.regle_set.all %}
~~~
# Activer une règle
## En bref
* une seule fonction
* vérifier les permissions
## Parefeu
### webui/models.py
~~~python
class Parefeu(AvecNom):
...
def is_editable_by(self, user):
"L'utilisateur a-t-il un droit d'édition sur le parefeu"
if user == self.admin or user.is_superuser:
return True
return False
~~~
## Regle
### webui/models.py
~~~python
class Regle(models.Model):
...
def toggle_actif(self):
self.actif = not self.actif
self.save()
# mettre à jour la date de dernière modification du Parefeu
self.parefeu.save()
~~~
## Regle
### webui/views.py
~~~python
from webui.models import Regle
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
def regle_activable(request, pk):
"Modifie l'état d'une règle"
regle = get_object_or_404(Regle, pk=pk)
if not regle.parefeu.is_editable_by(request.user):
messages.error(request, "Accès non autorisé")
return redirect("home")
regle.toggle_actif()
return redirect("parefeu-detail", regle.parefeu_id)
~~~
## Routage
### conf/urls.py
~~~python
path('regle/<int:pk>/actif/',
login_required(views.regle_activable),
name="regle-actif",
),
~~~
## Template
### webui/templates/webui/regle_list.html
~~~python
...
<td>
<a href="{% url 'regle-actif' regle.id %}">
{% if regle.actif %}
...
{% endif %}
</a>
</td>
~~~
# Priorité
## En bref
* faire un +1/-1 sur une règle
## Règle
### webui/models.py
~~~python
class Regle(models.Model):
...
def set_priorite(self, nb):
try:
tmp = self.priorite + int(nb)
except Exception:
return False
if tmp < 0:
# la priorite est négative
tmp = 0
self.priorite = tmp
self.save()
self.parefeu.save()
return True
~~~
## Règle
### webui/views.py
~~~python
def regle_priorite(request, pk, remove, nombre):
"""Modifie la priorite d'une règle
:param pk: la clé primaire d'une règle
:param remove: faut-il additionner ou
soustraire le nombre (booléen) ?
:param nombre: le nombre à ajouter à la priorité (str)
"""
~~~
## Règle
### webui/views.py
~~~python
regle = get_object_or_404(Regle, pk=pk)
if not regle.parefeu.is_editable_by(request.user):
messages.error(request, "Accès non autorisé")
return redirect("home")
if remove:
tmp = f"-{nombre}"
else:
tmp = nombre
if not regle.set_priorite(tmp):
messages.warning(request, "Le nombre n'est pas valide")
return redirect("parefeu-detail", regle.parefeu_id)
~~~
## Règle
### conf/urls.py
~~~python
path("regle/<int:pk>/priorite/+/<int:nombre>/",
login_required(views.regle_priorite),
{"remove": False},
name="regle-priorite-plus",
),
path("regle/<int:pk>/priorite/-/<int:nombre>/",
login_required(views.regle_priorite),
{"remove": True},
name="regle-priorite-moins",
),
~~~
## Règle
### webui/templates/webui/regle_list.html
~~~python
<a href="{% url 'regle-priorite-moins' regle.id 1 %}">
<i class="fas fa-minus"></i>
</a>
{{regle.priorite}}
<a href="{% url 'regle-priorite-plus' regle.id 1 %}">
<i class="fas fa-plus"></i>
</a>
~~~
# Nouvelle règle
## Formulaire
### webui/forms.py
~~~python
from django.forms import ModelForm
from webui.models import Regle
class RegleForm(ModelForm):
class Meta:
model = Regle
fields = ["priorite", "groupe", "services",
"interface", "sens"]
~~~
## Vue
### webui/views.py
~~~python
from webui.forms import RegleForm
def regle_create(request, parefeu_id):
parefeu = get_object_or_404(Parefeu, pk=parefeu_id)
if not parefeu.is_editable_by(request.user):
messages.error(request, "Accès non autorisé")
else:
if request.method == "POST":
# dans le slide suivant !
else:
messages.warning(request,
"Les données ne sont pas valides")
return redirect("parefeu-detail", parefeu_id)
~~~
## Vue
### webui/views.py
~~~python
...
# Create a form instance with POST data.
form = RegleForm(request.POST)
if form.is_valid():
# creation de la règle en mémoire, pas encore enregistrée
regle = form.save(commit=False)
# modification
regle.parefeu = parefeu
# enregistrement
regle.save()
# force la mise à jour de la date de dernière modification
regle.parefeu.save()
# enregistre les relations ManyToMany
form.save_m2m()
...
~~~
## Vue
### webui/views.py
~~~python
class ParefeuDetail(ParefeuMenu, DetailView):
...
def get_context_data(self, **kwargs):
context = super(ParefeuDetail, self).get_context_data(**kwargs)
context["form"] = RegleForm()
return context
~~~
## Routage
### conf/urls.py
~~~python
path("parefeu/<int:parefeu_id>/regle/add/",
login_required(views.regle_create),
name="regle-create",
),
~~~
## Templates
### webui/templates/webui/regle_list.html
~~~
...
<h4>Ajout d'une règle</h4>
<form method="post" action="{% url 'regle-create' parefeu.id %}">
{% csrf_token %}
<table class="table table-striped">
{{form.as_table}}
<tr>
<td colspan=2>
<button class="btn btn-success"
type="submit"/>Créer</button>
</td>
</tr>
</table>
</form>
~~~
## Templates
* manipuler un parefeu (à la place de regle_list)
* webui/templates/webui/regle_list.html
* manipuler un parefeu (à la place de regle_list)
* webui/templates/webui/parefeu_detail.html
## Problème
* la page 'parefeu-detail' devient moins lisible
* on peut éditer le parefeu et le modèle
### Solution
* ajouter un mode édition pour le template
# Règle
## Mode édition
### webui/views.py
~~~python
class ParefeuDetail(ParefeuMenu, DetailView):
...
def get_context_data(self, **kwargs):
context = super(ParefeuDetail, self).get_context_data(**kwargs)
context["form"] = RegleForm()
if context["parefeu"].is_editable_by(self.request.user):
context["can_edit"] = True
return context
~~~
## Mode édition
### webui/templates/webui/regle_list.html
~~~python
...
<td>{% if can_edit %}
<a href="{% url 'regle-priorite-moins' regle.id 1 %}">
<i class="fas fa-minus"></i>
</a>
{% endif %}
~~~
* modifier tous les endroits avec des liens dans le fichier
# Suppression
## Règle
* suppression d'une règle inactive seulement
## Règle
### webui/views.py
~~~python
def regle_delete(request, pk):
regle = get_object_or_404(Regle, pk=pk)
if not regle.parefeu.is_editable_by(request.user):
messages.error(request, "Accès non autorisé")
return redirect("home")
if regle.actif:
messages.warning(request, "Une règle active ne peut être supprimée")
else:
regle.delete()
messages.success(request, "La règle a été supprimée")
return redirect("parefeu-detail", regle.parefeu_id)
~~~
## Routage
### conf/urls.py
~~~python
path("regle/<int:pk>/delete/",
login_required(views.regle_delete),
name="regle-delete",
),
~~~
## Templates
### webui/templates/webui/regle_list.html
* ajouter une colonne en mode édition
* ajouter une icone de suppression
## Templates
### webui/templates/webui/regle_list.html
~~~python
{% if can_edit %}
<td>
{% if not regle.actif %}
<a href="{% url 'regle-delete' regle.id %}">
<i class="fas fa-trash"></i></a>
{% endif %}
</td>
{% endif %}
~~~
# FIN !
......@@ -19,7 +19,7 @@
<tr>
<th>Parefeu(x) utilisant ce parefeu comme modèle</th>
<td>{% for fw in parefeu.parefeu_set.all %}
<a href="{% url 'parefeu' fw.id %}">{{fw}}</a>
<a href="{% url 'parefeu-detail' fw.id %}">{{fw}}</a>
{% endfor %}
</td>
</tr>
......
No preview for this file type
......@@ -17,7 +17,10 @@
</tr>
<tr>
<th>Modèle</th>
<td>{% if parefeu.modele %}<a href="{% url 'parefeu-detail' parefeu.modele.id %}">{{parefeu.modele}}</a>{% endif %}</td>
<td>{% if parefeu.modele %}
<a href="{% url 'parefeu-detail' parefeu.modele.id %}">
{{parefeu.modele}}</a>
{% endif %}</td>
</tr>
{% if parefeu.parefeu_set.count %}
<tr>
......@@ -29,8 +32,11 @@
</tr>
{% endif %}
<tr><td colspan="2">
<a href="{% url 'parefeu-update' parefeu.id %}" class="btn btn-primary" role="button">Modifier</a>
<a href="{% url 'parefeu-delete' parefeu.id %}" class="btn btn-danger" role="button">Supprimer</a>
</td></tr></tbody></table>
<a href="{% url 'parefeu-update' parefeu.id %}"
class="btn btn-primary" role="button">Modifier</a>
<a href="{% url 'parefeu-delete' parefeu.id %}"
class="btn btn-danger" role="button">Supprimer</a>
</td></tr>
</tbody>
</table>
{% endblock %}
......@@ -82,4 +82,4 @@ class ParefeuUpdate(ParefeuMenu, UpdateView):
class ParefeuDelete(ParefeuMenu, DeleteView):
model = Parefeu
success_url = reverse_lazy('parefeux')
\ No newline at end of file
success_url = reverse_lazy('parefeux')
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
werkzeug = "*"
[packages]
django = "*"
django-extensions = "*"
ipython = "*"
django-bootstrap-static = "*"
fontawesome = "*"
[requires]
python_version = "3.6"
{
"_meta": {
"hash": {
"sha256": "fd58c91dce041b4f99db1f6bd2a030b08dd754e75beca928640d5ef5c2ef705a"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.6"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"asgiref": {
"hashes": [
"sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0",
"sha256:ea448f92fc35a0ef4b1508f53a04c4670255a3f33d22a81c8fc9c872036adbe5"
],
"version": "==3.2.3"
},
"backcall": {
"hashes": [
"sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
"sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
],
"version": "==0.1.0"
},
"decorator": {
"hashes": [
"sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce",
"sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d"
],
"version": "==4.4.1"
},
"django": {
"hashes": [
"sha256:6f857bd4e574442ba35a7172f1397b303167dae964cf18e53db5e85fe248d000",
"sha256:d98c9b6e5eed147bc51f47c014ff6826bd1ab50b166956776ee13db5a58804ae"
],
"index": "pypi",
"version": "==3.0"
},
"django-bootstrap-static": {
"hashes": [
"sha256:d9f1f9f7561efb9a73cd80a380d26628047805540ddb12002a453cebb6a74fed",
"sha256:fb8516189604b783f24a76e582ccbd90254ba81499dcf01e04e9016666760e8c"
],
"index": "pypi",
"version": "==4.2.1"
},
"django-extensions": {
"hashes": [
"sha256:a9db7c56a556d244184f589f2437b4228de86ee45e5ebb837fb20c6d54e95ea5",
"sha256:b58320d3fe3d6ae7d1d8e38959713fa92272f4921e662d689058d942a5b444f7"
],
"index": "pypi",
"version": "==2.2.5"
},
"fontawesome": {
"hashes": [
"sha256:1b5e8f2ed12a74b88aef6f404ff770af425324041bc5620523a802d5406595b8",
"sha256:642d74da5f4726bb2c8f035a2f4a98fdc4294d213a291994ee0b3d845166f9e8"
],
"index": "pypi",
"version": "==5.10.1.post1"
},
"ipython": {
"hashes": [
"sha256:c66c7e27239855828a764b1e8fc72c24a6f4498a2637572094a78c5551fb9d51",
"sha256:f186b01b36609e0c5d0de27c7ef8e80c990c70478f8c880863004b3489a9030e"
],
"index": "pypi",
"version": "==7.10.1"
},
"ipython-genutils": {
"hashes": [
"sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
"sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
],
"version": "==0.2.0"
},
"jedi": {
"hashes": [
"sha256:786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27",
"sha256:ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e"
],
"version": "==0.15.1"
},
"parso": {
"hashes": [
"sha256:63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc",
"sha256:666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"
],
"version": "==0.5.1"
},
"pexpect": {
"hashes": [
"sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1",
"sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"
],
"markers": "sys_platform != 'win32'",