Gestion des contenus.
This commit is contained in:
parent
0d237b52a8
commit
95895d23f1
18 changed files with 175 additions and 79 deletions
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 2.0.1 on 2018-02-28 12:53
|
# Generated by Django 2.0.1 on 2018-02-28 18:43
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
@ -9,7 +9,6 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('auth', '0009_alter_user_last_name_max_length'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
@ -27,9 +26,8 @@ class Migration(migrations.Migration):
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('name', models.CharField(max_length=255, verbose_name='Nom du contenu')),
|
('name', models.CharField(max_length=255, verbose_name='Nom du contenu')),
|
||||||
('content_url', models.URLField(null=True, verbose_name='URL du contenu')),
|
('file', models.FileField(upload_to='', verbose_name='Fichier')),
|
||||||
('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='content.Category', verbose_name='Catégorie')),
|
('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='content.Category', verbose_name='Catégorie')),
|
||||||
('group_owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
22
content/migrations/0002_content_school_owner.py
Normal file
22
content/migrations/0002_content_school_owner.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 2.0.1 on 2018-02-28 18:43
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('content', '0001_initial'),
|
||||||
|
('users', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='content',
|
||||||
|
name='school_owner',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.SchoolProfile'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -3,6 +3,7 @@ from django.urls import reverse
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
from users.models import SchoolProfile
|
||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
"""Une catégorie de contenu."""
|
"""Une catégorie de contenu."""
|
||||||
|
@ -31,20 +32,19 @@ class Content(models.Model):
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name="Nom du contenu"
|
verbose_name="Nom du contenu"
|
||||||
)
|
)
|
||||||
group_owner = models.ForeignKey(
|
school_owner = models.ForeignKey(
|
||||||
Group,
|
SchoolProfile,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
)
|
)
|
||||||
content_url = models.URLField(
|
|
||||||
verbose_name='URL du contenu',
|
|
||||||
null=True,
|
|
||||||
)
|
|
||||||
category = models.ForeignKey(
|
category = models.ForeignKey(
|
||||||
Category,
|
Category,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
verbose_name="Catégorie",
|
verbose_name="Catégorie",
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
file = models.FileField(
|
||||||
|
verbose_name="Fichier"
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
18
content/templates/content/content.html
Normal file
18
content/templates/content/content.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<div class=" col-md-4 text-center overflow-hidden">
|
||||||
|
<div class="card mb-4 box-shadow">
|
||||||
|
<video controls class="card-img-top">
|
||||||
|
<source src="{{content.file.url}}" type="video/mp4">
|
||||||
|
</video>
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="display-5">{{content.name}}</h2>
|
||||||
|
<p class="lead">Contenu proposé par {{content.school_owner.group.name}}</p>
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div class="btn-group">
|
||||||
|
<a class="btn btn-sm btn-outline-primary"i href="{% url "content:content-edit" content.pk %}"><i class="fa fa-edit"></i> Éditer</a>
|
||||||
|
<a class="btn btn-sm btn-outline-danger" href="{% url "content:content-delete" content.pk %}" ><i class="fa fa-trash"></i> Supprimer</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -27,18 +27,11 @@ $('html, body').animate({scrollTop: $('#category-content').offset().top}, 800);
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<span id="category-content"></span>
|
<div class="row text-center" id="category-content">
|
||||||
{% for content in contents %}
|
{% for content in contents %}
|
||||||
<div class="bg-dark pt-3 px-3 pt-md-5 px-md-5 text-center text-white overflow-hidden">
|
{% include "content/content.html" %}
|
||||||
<div class="my-3 py-3">
|
|
||||||
<h2 class="display-5">{{content.name}}</h2>
|
|
||||||
<p class="lead">Contenu proposé par {{content.group_owner.name}}</p>
|
|
||||||
</div>
|
|
||||||
<video controls>
|
|
||||||
<source src="{{content.content_url}}" type="video/mp4">
|
|
||||||
</video>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,9 @@ from .views import (
|
||||||
CreateCategory,
|
CreateCategory,
|
||||||
DeleteCategory,
|
DeleteCategory,
|
||||||
EditCategory,
|
EditCategory,
|
||||||
|
CreateContent,
|
||||||
|
DeleteContent,
|
||||||
|
EditContent,
|
||||||
)
|
)
|
||||||
|
|
||||||
app_name = 'content'
|
app_name = 'content'
|
||||||
|
@ -28,5 +31,21 @@ urlpatterns = [
|
||||||
'category/edit/<int:pk>',
|
'category/edit/<int:pk>',
|
||||||
EditCategory.as_view(),
|
EditCategory.as_view(),
|
||||||
name='category-edit',
|
name='category-edit',
|
||||||
)
|
),
|
||||||
|
path(
|
||||||
|
'new',
|
||||||
|
CreateContent.as_view(),
|
||||||
|
name='content-new',
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
'<int:pk>/delete',
|
||||||
|
DeleteContent.as_view(),
|
||||||
|
name="content-delete",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
'<int:pk>/edit',
|
||||||
|
EditContent.as_view(),
|
||||||
|
name="content-edit",
|
||||||
|
),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -54,3 +54,53 @@ class EditCategory(generic.UpdateView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class CreateContent(generic.CreateView):
|
||||||
|
"""Création de contenu."""
|
||||||
|
model = Content
|
||||||
|
fields = [
|
||||||
|
'name',
|
||||||
|
'category',
|
||||||
|
'file'
|
||||||
|
]
|
||||||
|
template_name = "edit.html"
|
||||||
|
extra_context = {
|
||||||
|
'title' : 'Envoi de contenu',
|
||||||
|
'validate' : 'Envoyer'
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return self.object.school_owner.get_absolute_url()
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.instance.school_owner = self.request.user.userprofile.school
|
||||||
|
r = super().form_valid(form)
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteContent(generic.DeleteView):
|
||||||
|
"""Suppression de contenu"""
|
||||||
|
model = Content
|
||||||
|
template_name = "confirm_delete.html"
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return self.object.school_owner.get_absolute_url()
|
||||||
|
|
||||||
|
|
||||||
|
class EditContent(generic.UpdateView):
|
||||||
|
"""Édition d'un contenu"""
|
||||||
|
model = Content
|
||||||
|
template_name = "edit.html"
|
||||||
|
fields = [
|
||||||
|
'name',
|
||||||
|
'category',
|
||||||
|
'file'
|
||||||
|
]
|
||||||
|
template_name = "edit.html"
|
||||||
|
extra_context = {
|
||||||
|
'title' : 'Édition de contenu',
|
||||||
|
'validate' : 'Envoyer'
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return self.object.school_owner.get_absolute_url()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import SiteSettings, ContentSettings
|
from .models import SiteSettings
|
||||||
|
|
||||||
admin.site.register(SiteSettings)
|
admin.site.register(SiteSettings)
|
||||||
admin.site.register(ContentSettings)
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# Generated by Django 2.0.1 on 2018-02-28 12:53
|
# Generated by Django 2.0.1 on 2018-02-28 18:43
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import settings.aes_field
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
@ -12,15 +11,6 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
|
||||||
name='ContentSettings',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('ftp_url', models.URLField(default='', max_length=255, verbose_name='URL du FTP')),
|
|
||||||
('ftp_id', models.CharField(default='', max_length=255, verbose_name='Identifiant sur le FTP')),
|
|
||||||
('ftp_pass', settings.aes_field.AESEncryptedField(default='', max_length=255, verbose_name='Mot de passe')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='SiteSettings',
|
name='SiteSettings',
|
||||||
fields=[
|
fields=[
|
||||||
|
|
|
@ -3,25 +3,6 @@ from django.db import models
|
||||||
from .aes_field import AESEncryptedField
|
from .aes_field import AESEncryptedField
|
||||||
|
|
||||||
|
|
||||||
class ContentSettings(models.Model):
|
|
||||||
PRETTY_NAME = "Réglages des contenus"
|
|
||||||
ftp_url = models.URLField(
|
|
||||||
max_length=255,
|
|
||||||
verbose_name="URL du FTP",
|
|
||||||
default="",
|
|
||||||
)
|
|
||||||
ftp_id = models.CharField(
|
|
||||||
max_length=255,
|
|
||||||
verbose_name="Identifiant sur le FTP",
|
|
||||||
default=""
|
|
||||||
)
|
|
||||||
ftp_pass = AESEncryptedField(
|
|
||||||
max_length=255,
|
|
||||||
verbose_name="Mot de passe",
|
|
||||||
default=""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SiteSettings(models.Model):
|
class SiteSettings(models.Model):
|
||||||
PRETTY_NAME = "Réglages du site"
|
PRETTY_NAME = "Réglages du site"
|
||||||
allow_upload = models.BooleanField(
|
allow_upload = models.BooleanField(
|
||||||
|
|
|
@ -61,7 +61,6 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
<h2>Réglages</h2>
|
<h2>Réglages</h2>
|
||||||
<h3>Réglages du site</h3>
|
|
||||||
<a class="btn btn-primary btn-sm" href="{% url 'settings:site-settings' %}">
|
<a class="btn btn-primary btn-sm" href="{% url 'settings:site-settings' %}">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
Éditer
|
Éditer
|
||||||
|
@ -84,22 +83,4 @@
|
||||||
<td>{{site_settings.home_message}}</td>
|
<td>{{site_settings.home_message}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>Réglage du contenu</h3>
|
|
||||||
<a class="btn btn-primary btn-sm" href="">
|
|
||||||
<i class="fas fa-edit"></i>
|
|
||||||
Éditer
|
|
||||||
</a>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<table class="table table-striped">
|
|
||||||
<tr>
|
|
||||||
<th>URL du FTP</th>
|
|
||||||
<td>{{content_settings.ftp_url}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>Identifiant du FTP</th>
|
|
||||||
<td>{{content_settings.ftp_id}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django.views.generic import TemplateView, UpdateView
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from content.models import Category
|
from content.models import Category
|
||||||
from users.models import SchoolProfile
|
from users.models import SchoolProfile
|
||||||
from .models import ContentSettings, SiteSettings
|
from .models import SiteSettings
|
||||||
|
|
||||||
|
|
||||||
class SettingsView(TemplateView):
|
class SettingsView(TemplateView):
|
||||||
|
@ -12,7 +12,6 @@ class SettingsView(TemplateView):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['categories'] = Category.objects.all()
|
context['categories'] = Category.objects.all()
|
||||||
context['site_settings'], _ = SiteSettings.objects.get_or_create()
|
context['site_settings'], _ = SiteSettings.objects.get_or_create()
|
||||||
context['content_settings'], _ = ContentSettings.objects.get_or_create()
|
|
||||||
context['schools'] = SchoolProfile.objects.all()
|
context['schools'] = SchoolProfile.objects.all()
|
||||||
context['settings'] = True
|
context['settings'] = True
|
||||||
return context
|
return context
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<h1>Suppression</h1>
|
||||||
<form action="" method="post">{% csrf_token %}
|
<form action="" method="post">{% csrf_token %}
|
||||||
<p>Êtes-vous certain de vouloir supprimer "{{ object }}"?</p>
|
<p>Êtes-vous certain de vouloir supprimer "{{ object }}"?</p>
|
||||||
<input type="submit" value="Confirmer" />
|
<input type="submit" value="Confirmer" />
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 2.0.1 on 2018-02-28 12:53
|
# Generated by Django 2.0.1 on 2018-02-28 18:43
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
@ -10,8 +10,8 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
('auth', '0009_alter_user_last_name_max_length'),
|
('auth', '0009_alter_user_last_name_max_length'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
@ -19,7 +19,7 @@ class Migration(migrations.Migration):
|
||||||
name='SchoolProfile',
|
name='SchoolProfile',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
|
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='students', to='auth.Group')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import User, Group
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
|
from django.urls import reverse
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
|
||||||
class SchoolProfile(models.Model):
|
class SchoolProfile(models.Model):
|
||||||
"""Ajoute un champ pour distinguer les groupes écoles des autres."""
|
"""Ajoute un champ pour distinguer les groupes écoles des autres."""
|
||||||
group = models.OneToOneField(Group, on_delete=models.CASCADE)
|
group = models.OneToOneField(
|
||||||
|
Group,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="students"
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.group.name
|
return self.group.name
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse("users:school", kwargs={'pk':self.pk})
|
||||||
|
|
||||||
class UserProfile(models.Model):
|
class UserProfile(models.Model):
|
||||||
"""Profil d'un utilisateur"""
|
"""Profil d'un utilisateur"""
|
||||||
|
|
21
users/templates/users/school.html
Normal file
21
users/templates/users/school.html
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load bootstrap4 %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{object.group.name}}</h1>
|
||||||
|
<a class="btn btn-primary btn-sm" href="{% url 'users:edit-school' object.pk %}">
|
||||||
|
<i class="fa fa-edit"></i>
|
||||||
|
Éditer
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-success btn-sm" href="{% url 'content:content-new' %}">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Ajouter un contenu
|
||||||
|
</a>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<div class="row">
|
||||||
|
{% for content in contents %}
|
||||||
|
{% include "content/content.html" %}
|
||||||
|
{%endfor%}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -9,6 +9,7 @@ from .views import (
|
||||||
Logout,
|
Logout,
|
||||||
PasswordChange,
|
PasswordChange,
|
||||||
Profile,
|
Profile,
|
||||||
|
School,
|
||||||
)
|
)
|
||||||
|
|
||||||
app_name = 'users'
|
app_name = 'users'
|
||||||
|
@ -48,6 +49,11 @@ urlpatterns = [
|
||||||
CreateSchool.as_view(),
|
CreateSchool.as_view(),
|
||||||
name='new-school'
|
name='new-school'
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
'school/<int:pk>',
|
||||||
|
School.as_view(),
|
||||||
|
name='school'
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
'school/<int:pk>/edit',
|
'school/<int:pk>/edit',
|
||||||
EditSchool.as_view(),
|
EditSchool.as_view(),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import User, Group
|
||||||
from django.views.generic import CreateView, UpdateView, DeleteView
|
from django.views.generic import CreateView, UpdateView, DeleteView, DetailView
|
||||||
from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView
|
from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView
|
||||||
from django.contrib.auth.hashers import make_password
|
from django.contrib.auth.hashers import make_password
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
|
@ -7,6 +7,7 @@ from django.urls import reverse, reverse_lazy
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from .models import UserProfile, SchoolProfile
|
from .models import UserProfile, SchoolProfile
|
||||||
|
from content.models import Content
|
||||||
|
|
||||||
|
|
||||||
class CreateUser(CreateView):
|
class CreateUser(CreateView):
|
||||||
|
@ -116,6 +117,16 @@ class DeleteSchool(DeleteView):
|
||||||
model = Group
|
model = Group
|
||||||
|
|
||||||
|
|
||||||
|
class School(DetailView):
|
||||||
|
model = SchoolProfile
|
||||||
|
template_name = "users/school.html"
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data()
|
||||||
|
context['contents'] = Content.objects.filter(school_owner=self.object)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class Logout(SuccessMessageMixin, LogoutView):
|
class Logout(SuccessMessageMixin, LogoutView):
|
||||||
success_message = "Vous vous êtes bien déconnecté."
|
success_message = "Vous vous êtes bien déconnecté."
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue