Commit a4d31005 authored by chirac's avatar chirac

Crée des statistiques générales de la bdd + model alias

parent 6af40d9e
...@@ -6,6 +6,8 @@ from dateutil.relativedelta import relativedelta ...@@ -6,6 +6,8 @@ from dateutil.relativedelta import relativedelta
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
class Facture(models.Model): class Facture(models.Model):
PRETTY_NAME = "Factures émises"
user = models.ForeignKey('users.User', on_delete=models.PROTECT) user = models.ForeignKey('users.User', on_delete=models.PROTECT)
paiement = models.ForeignKey('Paiement', on_delete=models.PROTECT) paiement = models.ForeignKey('Paiement', on_delete=models.PROTECT)
banque = models.ForeignKey('Banque', on_delete=models.PROTECT, blank=True, null=True) banque = models.ForeignKey('Banque', on_delete=models.PROTECT, blank=True, null=True)
...@@ -40,6 +42,8 @@ def facture_post_delete(sender, **kwargs): ...@@ -40,6 +42,8 @@ def facture_post_delete(sender, **kwargs):
#user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) #user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
class Vente(models.Model): class Vente(models.Model):
PRETTY_NAME = "Ventes effectuées"
facture = models.ForeignKey('Facture', on_delete=models.CASCADE) facture = models.ForeignKey('Facture', on_delete=models.CASCADE)
number = models.IntegerField(validators=[MinValueValidator(1)]) number = models.IntegerField(validators=[MinValueValidator(1)])
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
...@@ -74,6 +78,8 @@ def vente_post_delete(sender, **kwargs): ...@@ -74,6 +78,8 @@ def vente_post_delete(sender, **kwargs):
#user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) #user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
class Article(models.Model): class Article(models.Model):
PRETTY_NAME = "Articles en vente"
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
prix = models.DecimalField(max_digits=5, decimal_places=2) prix = models.DecimalField(max_digits=5, decimal_places=2)
iscotisation = models.BooleanField() iscotisation = models.BooleanField()
...@@ -83,18 +89,24 @@ class Article(models.Model): ...@@ -83,18 +89,24 @@ class Article(models.Model):
return self.name return self.name
class Banque(models.Model): class Banque(models.Model):
PRETTY_NAME = "Banques enregistrées"
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
def __str__(self): def __str__(self):
return self.name return self.name
class Paiement(models.Model): class Paiement(models.Model):
PRETTY_NAME = "Moyens de paiement"
moyen = models.CharField(max_length=255) moyen = models.CharField(max_length=255)
def __str__(self): def __str__(self):
return self.moyen return self.moyen
class Cotisation(models.Model): class Cotisation(models.Model):
PRETTY_NAME = "Cotisations"
vente = models.OneToOneField('Vente', on_delete=models.CASCADE, null=True) vente = models.OneToOneField('Vente', on_delete=models.CASCADE, null=True)
date_start = models.DateTimeField() date_start = models.DateTimeField()
date_end = models.DateTimeField() date_end = models.DateTimeField()
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
<th>Modification par</th> <th>Modification par</th>
<th>Date de modification</th> <th>Date de modification</th>
<th>Commentaire</th> <th>Commentaire</th>
<th></th>
</tr> </tr>
</thead> </thead>
{% for revision in revisions_list %} {% for revision in revisions_list %}
...@@ -21,6 +22,7 @@ ...@@ -21,6 +22,7 @@
<td>{{ revision.user }}</td> <td>{{ revision.user }}</td>
<td>{{ revision.date_created }}</td> <td>{{ revision.date_created }}</td>
<td>{{ revision.comment }}</td> <td>{{ revision.comment }}</td>
</tr> {% if is_bureau %}<td><a class="btn btn-info btn-sm" role="button" href="{% url 'logs:revert-action' revision.id %}"><i class="glyphicon glyphicon-repeat"></i>Annuler</a></p></td>{% endif %}
</tr>
{% endfor %} {% endfor %}
</table> </table>
{% for key, stats in stats_list.items %}
<table class="table table-striped">
<h4>Statistiques de l'ensemble {{ key }}</h4>
<thead>
<tr>
<th>Type d'objet</th>
<th>Nombre d'entrée stockées</th>
</tr>
</thead>
{% for key, stat in stats.items %}
<tr>
<td>{{ stat.0 }}</td>
<td>{{ stat.1 }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
{% extends "logs/sidebar.html" %}
{% load bootstrap3 %}
{% block title %}Supression d'action{% endblock %}
{% block content %}
<form class="form" method="post">
{% csrf_token %}
<h4>Attention, voulez-vous vraiment annuler cette action {{ objet_name }} ( {{ objet }} ) ?</h4>
{% bootstrap_button "Confirmer" button_type="submit" icon="trash" %}
</form>
<br />
<br />
<br />
{% endblock %}
...@@ -2,5 +2,6 @@ ...@@ -2,5 +2,6 @@
{% block sidebar %} {% block sidebar %}
{% if is_cableur %} {% if is_cableur %}
<p><a href="{% url "logs:stats-models" %}">Statistiques base de donnée</a></p>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% extends "logs/sidebar.html" %}
{% load bootstrap3 %}
{% block title %}Statistiques des objets base de données{% endblock %}
{% block content %}
<h2>Statistiques bdd</h2>
{% include "logs/aff_stats_models.html" with stats_list=stats_list %}
<br />
<br />
<br />
{% endblock %}
...@@ -4,4 +4,6 @@ from . import views ...@@ -4,4 +4,6 @@ from . import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.index, name='index'), url(r'^$', views.index, name='index'),
url(r'^revert_action/(?P<revision_id>[0-9]+)$', views.revert_action, name='revert-action'),
url(r'^stats_models/$', views.stats_models, name='stats-models'),
] ]
...@@ -16,8 +16,18 @@ from django.db import transaction ...@@ -16,8 +16,18 @@ from django.db import transaction
from reversion.models import Revision from reversion.models import Revision
from reversion.models import Version from reversion.models import Version
from users.models import User, ServiceUser, Right, School, ListRight, ListShell, Ban, Whitelist
from cotisations.models import Facture, Vente, Article, Banque, Paiement, Cotisation
from machines.models import Machine, MachineType, IpType, Extension, Interface, Alias, IpList
from topologie.models import Switch, Port, Room
from re2o.settings import PAGINATION_NUMBER, PAGINATION_LARGE_NUMBER from re2o.settings import PAGINATION_NUMBER, PAGINATION_LARGE_NUMBER
def form(ctx, template, request):
c = ctx
c.update(csrf(request))
return render_to_response(template, c, context_instance=RequestContext(request))
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index(request): def index(request):
...@@ -34,3 +44,55 @@ def index(request): ...@@ -34,3 +44,55 @@ def index(request):
revisions = paginator.page(paginator.num_pages) revisions = paginator.page(paginator.num_pages)
return render(request, 'logs/index.html', {'revisions_list': revisions}) return render(request, 'logs/index.html', {'revisions_list': revisions})
@login_required
@permission_required('bureau')
def revert_action(request, revision_id):
""" Annule l'action en question """
try:
revision = Revision.objects.get(id=revision_id)
except Revision.DoesNotExist:
messages.error(request, u"Revision inexistante" )
if request.method == "POST":
revision.revert()
messages.success(request, "L'action a été supprimée")
return redirect("/logs/")
return form({'objet': revision, 'objet_name': revision.__class__.__name__ }, 'logs/delete.html', request)
@login_required
@permission_required('cableur')
def stats_models(request):
stats = {
'Users' : {
'users' : [User.PRETTY_NAME, User.objects.count()],
'serviceuser' : [ServiceUser.PRETTY_NAME, ServiceUser.objects.count()],
'right' : [Right.PRETTY_NAME, Right.objects.count()],
'school' : [School.PRETTY_NAME, School.objects.count()],
'listright' : [ListRight.PRETTY_NAME, ListRight.objects.count()],
'listshell' : [ListShell.PRETTY_NAME, ListShell.objects.count()],
'ban' : [Ban.PRETTY_NAME, Ban.objects.count()],
'whitelist' : [Whitelist.PRETTY_NAME, Whitelist.objects.count()]
},
'Cotisations' : {
'factures' : [Facture.PRETTY_NAME, Facture.objects.count()],
'vente' : [Vente.PRETTY_NAME, Vente.objects.count()],
'cotisation' : [Cotisation.PRETTY_NAME, Cotisation.objects.count()],
'article' : [Article.PRETTY_NAME, Article.objects.count()],
'banque' : [Banque.PRETTY_NAME, Banque.objects.count()],
'cotisation' : [Cotisation.PRETTY_NAME, Cotisation.objects.count()],
},
'Machines' : {
'machine' : [Machine.PRETTY_NAME, Machine.objects.count()],
'typemachine' : [MachineType.PRETTY_NAME, MachineType.objects.count()],
'typeip' : [IpType.PRETTY_NAME, IpType.objects.count()],
'extension' : [Extension.PRETTY_NAME, Extension.objects.count()],
'interface' : [Interface.PRETTY_NAME, Interface.objects.count()],
'alias' : [Alias.PRETTY_NAME, Alias.objects.count()],
'iplist' : [IpList.PRETTY_NAME, IpList.objects.count()],
},
'Topologie' : {
'switch' : [Switch.PRETTY_NAME, Switch.objects.count()],
'port' : [Port.PRETTY_NAME, Port.objects.count()],
'chambre' : [Room.PRETTY_NAME, Room.objects.count()],
},
}
return render(request, 'logs/stats_models.html', {'stats_list': stats})
from django.contrib import admin from django.contrib import admin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from .models import IpType, Machine, MachineType, IpList, Interface, Extension from .models import IpType, Machine, MachineType, Alias, IpList, Interface, Extension
class MachineAdmin(VersionAdmin): class MachineAdmin(VersionAdmin):
list_display = ('user','name','active') list_display = ('user','name','active')
...@@ -22,9 +22,13 @@ class IpListAdmin(VersionAdmin): ...@@ -22,9 +22,13 @@ class IpListAdmin(VersionAdmin):
class InterfaceAdmin(VersionAdmin): class InterfaceAdmin(VersionAdmin):
list_display = ('machine','type','dns','mac_address','ipv4','details') list_display = ('machine','type','dns','mac_address','ipv4','details')
class AliasAdmin(VersionAdmin):
list_display = ('interface_parent', 'alias')
admin.site.register(Machine, MachineAdmin) admin.site.register(Machine, MachineAdmin)
admin.site.register(MachineType, MachineTypeAdmin) admin.site.register(MachineType, MachineTypeAdmin)
admin.site.register(IpType, IpTypeAdmin) admin.site.register(IpType, IpTypeAdmin)
admin.site.register(Extension, ExtensionAdmin) admin.site.register(Extension, ExtensionAdmin)
admin.site.register(IpList, IpListAdmin) admin.site.register(IpList, IpListAdmin)
admin.site.register(Interface, InterfaceAdmin) admin.site.register(Interface, InterfaceAdmin)
admin.site.register(Alias, AliasAdmin)
from django.forms import ModelForm, Form, ValidationError from django.forms import ModelForm, Form, ValidationError
from django import forms from django import forms
from .models import Machine, Interface, IpList, MachineType, Extension, IpType from .models import Alias, Machine, Interface, IpList, MachineType, Extension, IpType
class EditMachineForm(ModelForm): class EditMachineForm(ModelForm):
class Meta: class Meta:
...@@ -59,6 +59,15 @@ class BaseEditInterfaceForm(EditInterfaceForm): ...@@ -59,6 +59,15 @@ class BaseEditInterfaceForm(EditInterfaceForm):
self.fields['type'].queryset = MachineType.objects.filter(ip_type=IpType.objects.filter(need_infra=False)) self.fields['type'].queryset = MachineType.objects.filter(ip_type=IpType.objects.filter(need_infra=False))
self.fields['ipv4'].queryset = IpList.objects.filter(ip_type=IpType.objects.filter(need_infra=False)) self.fields['ipv4'].queryset = IpList.objects.filter(ip_type=IpType.objects.filter(need_infra=False))
class NewAliasForm(ModelForm):
class Meta:
model = Alias
fields = ['alias']
class EditAliasFullForm(NewAliasForm):
class Meta(NewAliasForm.Meta):
fields = '__all__'
class MachineTypeForm(ModelForm): class MachineTypeForm(ModelForm):
class Meta: class Meta:
model = MachineType model = MachineType
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('machines', '0026_auto_20161026_1348'),
]
operations = [
migrations.CreateModel(
name='Alias',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, auto_created=True, verbose_name='ID')),
('alias', models.CharField(max_length=255, help_text='Obligatoire et unique, ne doit pas comporter de points', unique=True)),
('interface_parent', models.ForeignKey(to='machines.Interface')),
],
),
]
...@@ -8,6 +8,8 @@ from re2o.settings import MAIN_EXTENSION ...@@ -8,6 +8,8 @@ from re2o.settings import MAIN_EXTENSION
class Machine(models.Model): class Machine(models.Model):
PRETTY_NAME = "Machine"
user = models.ForeignKey('users.User', on_delete=models.PROTECT) user = models.ForeignKey('users.User', on_delete=models.PROTECT)
name = models.CharField(max_length=255, help_text="Optionnel", blank=True, null=True) name = models.CharField(max_length=255, help_text="Optionnel", blank=True, null=True)
active = models.BooleanField(default=True) active = models.BooleanField(default=True)
...@@ -16,6 +18,8 @@ class Machine(models.Model): ...@@ -16,6 +18,8 @@ class Machine(models.Model):
return str(self.user) + ' - ' + str(self.id) + ' - ' + str(self.name) return str(self.user) + ' - ' + str(self.id) + ' - ' + str(self.name)
class MachineType(models.Model): class MachineType(models.Model):
PRETTY_NAME = "Type de machine"
type = models.CharField(max_length=255) type = models.CharField(max_length=255)
ip_type = models.ForeignKey('IpType', on_delete=models.PROTECT, blank=True, null=True) ip_type = models.ForeignKey('IpType', on_delete=models.PROTECT, blank=True, null=True)
...@@ -23,6 +27,8 @@ class MachineType(models.Model): ...@@ -23,6 +27,8 @@ class MachineType(models.Model):
return self.type return self.type
class IpType(models.Model): class IpType(models.Model):
PRETTY_NAME = "Type d'ip"
type = models.CharField(max_length=255) type = models.CharField(max_length=255)
extension = models.ForeignKey('Extension', on_delete=models.PROTECT) extension = models.ForeignKey('Extension', on_delete=models.PROTECT)
need_infra = models.BooleanField(default=False) need_infra = models.BooleanField(default=False)
...@@ -31,12 +37,16 @@ class IpType(models.Model): ...@@ -31,12 +37,16 @@ class IpType(models.Model):
return self.type return self.type
class Extension(models.Model): class Extension(models.Model):
PRETTY_NAME = "Extensions dns"
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
def __str__(self): def __str__(self):
return self.name return self.name
class Interface(models.Model): class Interface(models.Model):
PRETTY_NAME = "Interface"
ipv4 = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True) ipv4 = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True)
#ipv6 = models.GenericIPAddressField(protocol='IPv6', null=True) #ipv6 = models.GenericIPAddressField(protocol='IPv6', null=True)
mac_address = MACAddressField(integer=False, unique=True) mac_address = MACAddressField(integer=False, unique=True)
...@@ -54,7 +64,18 @@ class Interface(models.Model): ...@@ -54,7 +64,18 @@ class Interface(models.Model):
def __str__(self): def __str__(self):
return self.dns return self.dns
class Alias(models.Model):
PRETTY_NAME = "Alias dns"
interface_parent = models.ForeignKey('Interface', on_delete=models.CASCADE)
alias = models.CharField(help_text="Obligatoire et unique, ne doit pas comporter de points", max_length=255, unique=True)
def __str__(self):
return self.alias
class IpList(models.Model): class IpList(models.Model):
PRETTY_NAME = "Addresses ipv4"
ipv4 = models.GenericIPAddressField(protocol='IPv4', unique=True) ipv4 = models.GenericIPAddressField(protocol='IPv4', unique=True)
ip_type = models.ForeignKey('IpType', on_delete=models.PROTECT) ip_type = models.ForeignKey('IpType', on_delete=models.PROTECT)
......
...@@ -20,7 +20,7 @@ from reversion import revisions as reversion ...@@ -20,7 +20,7 @@ from reversion import revisions as reversion
import re import re
from .forms import NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm, MachineTypeForm, DelMachineTypeForm, ExtensionForm, DelExtensionForm, BaseEditInterfaceForm, BaseEditMachineForm from .forms import NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm, MachineTypeForm, DelMachineTypeForm, ExtensionForm, DelExtensionForm, BaseEditInterfaceForm, BaseEditMachineForm
from .forms import IpTypeForm, DelIpTypeForm from .forms import IpTypeForm, DelIpTypeForm, NewAliasForm, EditAliasFullForm
from .models import IpType, Machine, Interface, IpList, MachineType, Extension from .models import IpType, Machine, Interface, IpList, MachineType, Extension
from users.models import User from users.models import User
from re2o.settings import PAGINATION_NUMBER, PAGINATION_LARGE_NUMBER from re2o.settings import PAGINATION_NUMBER, PAGINATION_LARGE_NUMBER
......
...@@ -16,6 +16,8 @@ def clean_port_related(port): ...@@ -16,6 +16,8 @@ def clean_port_related(port):
related_port.save() related_port.save()
class Switch(models.Model): class Switch(models.Model):
PRETTY_NAME = "Switch / Commutateur"
switch_interface = models.OneToOneField('machines.Interface', on_delete=models.CASCADE) switch_interface = models.OneToOneField('machines.Interface', on_delete=models.CASCADE)
location = models.CharField(max_length=255) location = models.CharField(max_length=255)
number = models.IntegerField() number = models.IntegerField()
...@@ -25,6 +27,8 @@ class Switch(models.Model): ...@@ -25,6 +27,8 @@ class Switch(models.Model):
return str(self.location) return str(self.location)
class Port(models.Model): class Port(models.Model):
PRETTY_NAME = "Port de switch"
switch = models.ForeignKey('Switch', related_name="ports") switch = models.ForeignKey('Switch', related_name="ports")
port = models.IntegerField() port = models.IntegerField()
room = models.ForeignKey('Room', on_delete=models.PROTECT, blank=True, null=True) room = models.ForeignKey('Room', on_delete=models.PROTECT, blank=True, null=True)
...@@ -36,6 +40,8 @@ class Port(models.Model): ...@@ -36,6 +40,8 @@ class Port(models.Model):
unique_together = ('switch', 'port') unique_together = ('switch', 'port')
def clean(self): def clean(self):
if self.port > self.switch.number:
raise ValidationError("Ce port ne peut exister, numero trop élevé")
if self.room and self.machine_interface or self.room and self.related or self.machine_interface and self.related: if self.room and self.machine_interface or self.room and self.related or self.machine_interface and self.related:
raise ValidationError("Chambre, interface et related_port sont mutuellement exclusifs") raise ValidationError("Chambre, interface et related_port sont mutuellement exclusifs")
if self.related==self: if self.related==self:
...@@ -52,6 +58,8 @@ class Port(models.Model): ...@@ -52,6 +58,8 @@ class Port(models.Model):
return str(self.switch) + " - " + str(self.port) return str(self.switch) + " - " + str(self.port)
class Room(models.Model): class Room(models.Model):
PRETTY_NAME = "Chambre/ Prise murale"
name = models.CharField(max_length=255, unique=True) name = models.CharField(max_length=255, unique=True)
details = models.CharField(max_length=255, blank=True) details = models.CharField(max_length=255, blank=True)
......
...@@ -101,6 +101,7 @@ class UserManager(BaseUserManager): ...@@ -101,6 +101,7 @@ class UserManager(BaseUserManager):
class User(AbstractBaseUser): class User(AbstractBaseUser):
PRETTY_NAME = "Utilisateurs"
STATE_ACTIVE = 0 STATE_ACTIVE = 0
STATE_DISABLED = 1 STATE_DISABLED = 1
STATE_ARCHIVE = 2 STATE_ARCHIVE = 2
...@@ -287,6 +288,7 @@ def user_post_delete(sender, **kwargs): ...@@ -287,6 +288,7 @@ def user_post_delete(sender, **kwargs):
#user.ldap_del() #user.ldap_del()
class ServiceUser(AbstractBaseUser): class ServiceUser(AbstractBaseUser):
PRETTY_NAME = "Utilisateurs de service"
pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", validators=[linux_user_validator]) pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", validators=[linux_user_validator])
...@@ -323,6 +325,8 @@ def service_user_post_delete(sender, **kwargs): ...@@ -323,6 +325,8 @@ def service_user_post_delete(sender, **kwargs):
service_user.ldap_del() service_user.ldap_del()
class Right(models.Model): class Right(models.Model):
PRETTY_NAME = "Droits affectés à des users"
user = models.ForeignKey('User', on_delete=models.PROTECT) user = models.ForeignKey('User', on_delete=models.PROTECT)
right = models.ForeignKey('ListRight', on_delete=models.PROTECT) right = models.ForeignKey('ListRight', on_delete=models.PROTECT)
...@@ -343,6 +347,8 @@ def right_post_delete(sender, **kwargs): ...@@ -343,6 +347,8 @@ def right_post_delete(sender, **kwargs):
right.ldap_sync() right.ldap_sync()
class School(models.Model): class School(models.Model):
PRETTY_NAME = "Etablissements enregistrés"
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
def __str__(self): def __str__(self):
...@@ -350,6 +356,8 @@ class School(models.Model): ...@@ -350,6 +356,8 @@ class School(models.Model):
class ListRight(models.Model): class ListRight(models.Model):
PRETTY_NAME = "Liste des droits existants"
listright = models.CharField(max_length=255, unique=True) listright = models.CharField(max_length=255, unique=True)
gid = models.IntegerField(unique=True, null=True) gid = models.IntegerField(unique=True, null=True)
...@@ -383,12 +391,16 @@ def listright_post_delete(sender, **kwargs): ...@@ -383,12 +391,16 @@ def listright_post_delete(sender, **kwargs):
right.ldap_del() right.ldap_del()
class ListShell(models.Model): class ListShell(models.Model):
PRETTY_NAME = "Liste des shells disponibles"
shell = models.CharField(max_length=255, unique=True) shell = models.CharField(max_length=255, unique=True)
def __str__(self): def __str__(self):
return self.shell return self.shell
class Ban(models.Model): class Ban(models.Model):
PRETTY_NAME = "Liste des bannissements"
user = models.ForeignKey('User', on_delete=models.PROTECT) user = models.ForeignKey('User', on_delete=models.PROTECT)
raison = models.CharField(max_length=255) raison = models.CharField(max_length=255)
date_start = models.DateTimeField(auto_now_add=True) date_start = models.DateTimeField(auto_now_add=True)
...@@ -399,6 +411,8 @@ class Ban(models.Model): ...@@ -399,6 +411,8 @@ class Ban(models.Model):
class Whitelist(models.Model): class Whitelist(models.Model):
PRETTY_NAME = "Liste des accès gracieux"
user = models.ForeignKey('User', on_delete=models.PROTECT) user = models.ForeignKey('User', on_delete=models.PROTECT)
raison = models.CharField(max_length=255) raison = models.CharField(max_length=255)
date_start = models.DateTimeField(auto_now_add=True) date_start = models.DateTimeField(auto_now_add=True)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment