Dans ce tutoriel, nous allons voir pas à pas comment créer un blog django complet permettant de gérer les utilisateurs (users & authors) et qui contient deux catégories des utilisateurs:
- les utilisateurs enregistés qui aurons la possibilité de lire et commenter les articles
- les auteurs (authors) qui aurons la possibilité de lire, écrire et commenter les articles
shema relationnel
1. Création du projet principal
Avant de créer le blog django, nous devons au préalable:
- créer un projet nommons le mysite à titre d'exemple
- créer une application account pour gérer les utilisateurs
- créer et installer un template bootstrap
Pour la création du template, pous pouvez créer votre propre template Bootstrap si vous maîtriser le langage HTML, CSS,... Si non veuillez en télécharger un, vous pouvez aussi utiliser notre propre template Bootstrap via le lien suivant: https://drive.google.com/file/d/14owvtBXNOOgnAgJtiA5fM_KNCIfwtaTO/view?usp=drive_link
• Copier ensuite le fichier base.html à la racine du dossier template et le dossier static à la racine de votre projet
• Créer ensuite un fichier template/index.html qui sera utilisé comme home page et y ajouter le code d'héritage du template de base:
1 2 3 4 5 6 7 8 9 10 11 |
<!-- template/index.html --> {% extends 'base.html' %} {% block login %} <!-- Login block here ! --> {% endblock %} {% block content %} <!-- Content here ! --> {% endblock %} |
1.2 Gestion des fichiers statiques & médias
Au niveau du fichier mysite/settings.py ajouter le code suivant:
1 2 3 4 5 6 7 8 9 |
# mysite/settings.py STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join (BASE_DIR, 'static/'), ] MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/' |
1.3 Configuration de la page d'accueil avec la classe TemplateView
Comme son nom l'indique, la classe TemplateView permet de créer et configurer une vue directement sur un template via la syntaxe:
1 |
path('url_path' , TemplateView.as_view(template_name="template_name")), |
Nous pouvons donc l'utiliser pour configurer notre page d'accueil au niveau du fichier urls.py du projet:
1 2 3 4 5 6 7 |
from django.contrib import admin from django.urls import path , include from django.views.generic import TemplateView urlpatterns = [ path('admin/', admin.site.urls), path('' , TemplateView.as_view(template_name="index.html")), ] |
2 Gestion des utilisateurs
Pour la géstion des utilisateurs veuillez voir le tutoriel gestion des utilisateurs django: https://www.tresfacile.net/gestion-des-utilisateurs-django/
3. Création de l'application blog
De la même manière comme fait pour l'application accounts, créons maintenant une application nommée blog, destinées à la publication des articles (posts) et commentaires. Chaque article sera caractérisé par un identifiant id comme clé primaire(primary key) et une clé étrangère ( foreignkey) id_users qui fait référence à l'identifiant de l'utilisateur (author) qui l'a publié, a cet effet exécutons la commande suivante qui créera notre application blog:
1 |
python manage.py startapp blog |
Enregistrons ensuite l'application blog au niveau du fichier settings.py:
1 2 3 4 5 |
#settigns.py INSTALLED_APPS = [ 'blog.apps.BlogConfig', ##############, ] |
4. Création du modèle du blog
Créez ensuite le modèle du blog qui va contenir les champs: title, slug, author, content...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# mysite/blog/models.py from django.db import models from django.contrib.auth.models import User from django.contrib import admin from django.urls import reverse STATUS = ( (0,"Draft"),(1,"Publish")) class Post(models.Model): title = models.CharField(max_length=250 , unique=True) slug = models.SlugField(max_length=250 , unique=True) author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts') updated_date = models.DateTimeField(auto_now=True) content = models.TextField() created_date = models.DateTimeField(auto_now_add=True) status = models.IntegerField(choices=STATUS, default=0) # we use the save method to save slug fields automatically def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.title) super(Post, self).save(*args, **kwargs) class Meta: ordering = ['-created_date'] def __str__(self): return self.title # affichage au niveau de la zone admin class PostAdmin(admin.ModelAdmin): list_display = ('title', 'slug', 'author', 'content') list_filter = ('title',) prepopulated_fields = {'slug': ('title',)} |
Il nécessaire maintenant d'effectuer les commandes de migrations afin de créer automatiquement la table sql posts au niveau du modèle de la base de donnée: à cet effet, exécutez successivement les deux commandes:
1 |
python manage.py makemigrations blog |
et ensuite:
1 |
python manage.py migrate |
5. Affichage du blog au niveau de la zone admin
Et afin que le modèle Post soit disponible et administrable au niveau de la zone admin, on doit le déclarer au niveau du fichier mysite/blog/admin.py:
1 2 3 4 |
# mysite/blog/admin.py from django.contrib import admin from .models import Post , PostAdmin admin.site.register(Post , PostAdmin) |
Maintenant en se connectant à votre zone admin via le lien: http://127.0.0.1:8000/admin/ vous pouvez voir votre blog, profitez donc pour y ajouter quelques posts afin que le blog ne soit pas vide:
6. Création du view du blog
Pour créer un view performant d'une façon assez simple, nous allons utiliser les vues génériques basées sur les classes: ListView, DetailView, UpdateView, CreateView, DeleteView:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
from django.views.generic import ListView, DetailView,UpdateView, CreateView,DeleteView from django.urls import reverse_lazy from .models import Post class PostList(ListView): model = Post paginate_by = 3 class PostDetail(DetailView): model = Post #template_name = 'blog/post_detail.html' class PostEdit(UpdateView): model = Post fields = ['title', 'slug' ,'content'] success_url = reverse_lazy('post_list') class PostNew(CreateView): model = Post fields = ['title', 'slug' , 'content'] def form_valid(self, form): form.instance.author = self.request.user return super(PostNew, self).form_valid(form) success_url = reverse_lazy('post_list') class PostDelete(DeleteView): model = Post success_url = reverse_lazy('post_list') |
7. Mise à jours du système des urls
Nous devons maintenant mettre à jour le système des urls en créant un fichier mysite/blog/urls et y ajouter le code suivant:
1 2 3 4 5 6 7 8 9 10 11 |
# mysite/blog/urls.py from django.urls import path from .import views urlpatterns = [ path('' , views.PostList.as_view() , name='post_list'), path('view/', views.PostDetail.as_view(), name='post_detail'), path('edit/' , views.PostEdit.as_view() , name='post_edit'), path('new',views.PostNew.as_view() , name='post_new'), path('delete/' , views.PostDelete.as_view(), name='post_delete'), ] |
On doit aussi mettre à jour le système des urls du projet principal en ajoutant le chemin:
1 |
path('blog/', include('blog.urls')), |
Voici le code final du fichier mysite/urls:
1 2 3 4 5 6 7 8 9 10 11 12 |
# mysite/urls from django.contrib import admin from django.urls import path,include from django.views.generic import TemplateView urlpatterns = [ path('admin/', admin.site.urls), path('accounts/', include('accounts.urls')), path('blog/', include('blog.urls')), path('',TemplateView.as_view(template_name='index.html')) ] |
8. Configuration au niveau du template
Nous utilisons ici les vues génériques qui exigent certaines règles à respecter, les fichiers du template doivent être organisés de la façon suivante: au sein du répertoire template on doit créer un sous répertoire qui porte le même nom que l'application django et au sein de ce dernier les noms des fichiers doivent être de la forme : [model_name]_...Comme le montre le schéma suivant:
Ainsi dans notre cas on a:
- app_name = blog
- model_name = post
Et par suite nous aurons la structure suivante:
- post_confirm_delete.html : s'occupera de la suppression d'un article avec un message de confirmation
- post_detail.html : s'occupera de l'affichage d'un article en détail
- post_form.html : s'occupera du formulaire à double fonctionnalité : l'édition et l'ajout d'un nouveau article.
- post_list.html : s'occupera de l'affichage de la liste des articles
8.1 Affichage de la liste des articles
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<!-- template/blog/post_list.html --> {% extends 'base.html' %} {% block content %} <h2>Articles List</h2> <div class="container"> <div class="row"> <!-- Button To Add a New Post --> <a href = "/blog/new" class="btn btn-success" style="width:300px;">Write Post</a> <!-- Display The List Of Posts --> {% for post in post_list %} <div class="card"> <div class="card-header"> <h2 class="card-title">{{ post.title }}</h2> </div> <div class="card-body"> <p class="card-text text-muted h6">{{ post.author }} | {{ post.created_date}} </p> <p class="card-text">{{post.content|slice:":200" }}</p> <p style="float: right;"> <!-- CRUD BUTTON --> <a href="{% url 'post_detail' post.slug %}" class="btn btn-primary" >Read More -> </a> <a href="{% url 'post_edit' post.pk %}" class="btn btn-success">Edit</a> <a href="{% url 'post_delete' post.pk %}" class="btn btn-danger ">Delete</a> </p> </div> </div> <br /> {% endfor %} <!-- Load pagination system --> <div style="font-size:20px;"> {% if page_obj.has_previous %} <a href="?page=1"> << first</a> <a href="?page={{ page_obj.previous_page_number }}">previous</a> {% endif %} Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}. {% if page_obj.has_next %} <a href="?page={{ page_obj.next_page_number }}">Next</a> <a href="?page={{ page_obj.paginator.num_pages }}">last >></a> {% endif %} </div> </div></div> {% endblock %} |
8.2 Affichage des articles en détails
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!-- template/blog/post_detail.html --> {% extends 'base.html' %} {% block content %} <div class="card-body"> <h1 style="color: #0975C3;"> {{ object.title }} </h1> <p class=" text-muted">{{ post.author }} | {{ post.created_date }}</p> <p class="card-text ">{{ object.content | safe }}</p> </div> <div style="text-align: center;"> <a href="{% url 'post_list' %}" class="btn btn-secondary">Go back to the post list</a> </div> {% endblock content %} |
8.3 Formulaire d'ajout d'un article
Remarque. pour l'ajout, l'édition ou la suppression d'un article, l'utilisateur qui réalise l'action doit posséder les droits nécessaires: administrator author... Mais ici pour simplifier la tâche, nous allons le traiter spontanément et laisser le truc des droits d'utilisateurs au second paragraphe!
1 2 3 4 5 6 7 8 9 10 11 |
<!-- template/blog/post_form.html --> {% extends 'base.html' %} {% load crispy_forms_tags %} {% block content %} <h2>Add / Edit Post</h2> <div style="width:700px;"> <form method="post"> {% csrf_token %} {{ form | crispy }} <button type="submit" class="save btn btn-default">Save</button> {% endblock %} |
8.4 Page de suppression d'un article
Nous avons déjà parlé des vues génériques et indiqué que la page de suppression des articles (posts) sera nommée: template/blog/post_confirm_delete.html
1 2 3 4 5 6 7 8 9 10 11 12 |
<!-- template/blog/post_confirm_delete.html --> {% extends 'base.html' %} {% block content %} <h1>Delete Post</h1> <form method="post"> {% csrf_token %} <h3>Are you sure you want to delete {{ object }} !!? </h3> <input type="submit" value=" Yes " class="btn btn-danger"/> </form> {% endblock content %} |
9 Gérer les permissions
9.1 Gestion des droits d'utilisateurs
Jusque ici on pas encore touché à la gestion des droits d'utilisateurs: n'importe quel utilisateur qu'il soit connecté ou non, il peut ajouter, éditer ou supprimer des article, ce qui n'est pas logique. A cet effet nous devons gérer et attribuer des droits aux utilisateurs au sein de la zone principale d'administration. Nous allons à titre d'exemple créer via la zone admin un groupe d'auteurs et d'ajouter quelque utilisateurs à ce groupe. Ainsi seulement les utilisateurs ajoutés à ce groupe aurons la possibilité d'ajouter ou modifier des articles.
Au sein de la zone admin dans la rubrique Groups, cliquez sur Add:
Ensuite dans la zone name choisissez un nom pour votre groupe à titre d'exemple : author et dans la zone Chosen permission sélectionnez les droits pour le modèle post: can add post, can change post...
Nous allons ensuite attribuer le rôle d'auteur à quelque utilisateurs afin de leurs donner le droit d'ajout, d'édition ou suppression d'articles. A cet effet, au niveau de la zone admin, cliquez sur l'utilisateur auquel vous souhaitez attribuer ce droit et puis dans la zone Chosen groups ajouter le groupe author:
Dans ce cas l'utilisateur dont le nom d'utilisateur robert possède les droits d'ajout et de suppression d'articles.
10 Gérer les permissions au niveau du template
10.1 Ajout d'articles au niveau du template
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!-- template/blog/post_form.html --> {% extends 'base.html' %} {% load crispy_forms_tags %} {% block content %} <h2>Add / Edit Post</h2> {% if perms.blog.add_post %} <div style="width:700px;"> <form method="post"> {% csrf_token %} {{ form | crispy }} <button type="submit" class="btn btn-default"> Save </button> </form> {% else %} <h3 style="color:red;">You don't have permission to add post !</h3> {% endif %} |
A ce niveau là si l'utilisateur est connecté et possède le droit d'ajout darticles, le système lui affiche le formulaire d'ajout sans problème ! Et s'il est connecté sous un autre nom d'utilisateur qui n'appartient pas au groupe author, le système lui affiche le message suivant:
You don't have permission to add post !
Pour les autres rubriques : edit ou delete post, la même procédure sera appliquée:
1 |
{% if perms.blog.add_post %} |
Younes Derfoufi
CRMEF OUJDA