From 669129e30d5c5ae1824fe66461222d350f50a152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Mon, 20 Feb 2017 01:12:06 +0100 Subject: [PATCH 1/7] Move the club model to the gestion app - Move the model - Add some BDS-related fields - Add an `associations` fields to be able to separate the clubs between the different associations using groups --- cof/migrations/0012_remove_club.py | 23 ++++++ cof/models.py | 12 ---- gestion/migrations/0002_club_support.py | 95 +++++++++++++++++++++++++ gestion/models.py | 54 +++++++++++++- 4 files changed, 171 insertions(+), 13 deletions(-) create mode 100644 cof/migrations/0012_remove_club.py create mode 100644 gestion/migrations/0002_club_support.py diff --git a/cof/migrations/0012_remove_club.py b/cof/migrations/0012_remove_club.py new file mode 100644 index 00000000..ebc48d37 --- /dev/null +++ b/cof/migrations/0012_remove_club.py @@ -0,0 +1,23 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('cof', '0011_delete_clipper_and_custommail'), + ('gestion', '0002_club_support') + ] + + operations = [ + migrations.RemoveField( + model_name='club', + name='membres', + ), + migrations.RemoveField( + model_name='club', + name='respos', + ), + migrations.DeleteModel( + name='Club', + ), + ] diff --git a/cof/models.py b/cof/models.py index dafe9996..1d1f5b07 100644 --- a/cof/models.py +++ b/cof/models.py @@ -79,18 +79,6 @@ class CofProfile(models.Model): return self.profile.user.username -@python_2_unicode_compatible -class Club(models.Model): - name = models.CharField("Nom", max_length=200, unique=True) - description = models.TextField("Description", blank=True) - respos = models.ManyToManyField(User, related_name="clubs_geres", - blank=True) - membres = models.ManyToManyField(User, related_name="clubs", blank=True) - - def __str__(self): - return self.name - - @python_2_unicode_compatible class Event(models.Model): title = models.CharField("Titre", max_length=200) diff --git a/gestion/migrations/0002_club_support.py b/gestion/migrations/0002_club_support.py new file mode 100644 index 00000000..6b23882b --- /dev/null +++ b/gestion/migrations/0002_club_support.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings + + +def keep_cof_clubs(apps, schema_editor): + Group = apps.get_model("auth", "Group") + CofClub = apps.get_model("cof", "Club") + NewClub = apps.get_model("gestion", "Club") + Registration = apps.get_model("gestion", "ClubUser") + + cof_group = Group.objects.get(name="cof_members") + + for oldclub in CofClub.objects.all(): + newclub = NewClub.objects.create( + name=oldclub.name, + description=oldclub.description, + ) + for user in oldclub.membres.all(): + Registration.objects.create( + club=newclub, + user=user, + has_paid=True + ) + for user in oldclub.respos.all(): + reg = Registration.objects.get_or_create( + user=user, + club=newclub + ) + reg.is_respo = True + reg.save() + newclub.associations.add(cof_group) + newclub.save() + + +def restore_cof_clubs(apps, schema_editor): + Group = apps.get_model("auth", "Group") + Club = apps.get_model("cof", "Club") + Registration = apps.get_model("gestion", "ClubUser") + + cof_group = Group.objects.get(name="cof_members") + + for newclub in cof_group.clubs.all(): + club = Club.objects.create( + name=newclub.name, + description=newclub.description + ) + for reg in Registration.objects.filter(club=newclub): + if reg.is_respo: + club.respos.add(reg.user) + else: + club.membres.add(reg.user) + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0006_require_contenttypes_0002'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('cof', '0011_delete_clipper_and_custommail'), + ('gestion', '0001_initial'), + ] + + operations = [ + migrations.RunPython(lambda: 1/0), + migrations.CreateModel( + name='Club', + fields=[ + ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), + ('name', models.CharField(unique=True, max_length=200, verbose_name='Nom')), + ('description', models.TextField(verbose_name='Description', blank=True)), + ('price', models.DecimalField(verbose_name='Cotisation (€)', decimal_places=2, default=0, blank=True, max_digits=5)), + ('cotisation_frequency', models.CharField(choices=[('ANN', 'Annuel'), ('SEM', 'Semestriel'), ('COU', 'Au cours')], max_length=3, verbose_name='Fréquence de la cotisation', default='ANN', blank=True)), + ('associations', models.ManyToManyField(to='auth.Group', related_name='clubs')), + ], + ), + migrations.CreateModel( + name='ClubUser', + fields=[ + ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), + ('is_respo', models.BooleanField(verbose_name='Est responsable du club')), + ('has_paid', models.BooleanField(verbose_name='A payé sa cotisation')), + ('club', models.ForeignKey(to='gestion.Club')), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddField( + model_name='club', + name='members', + field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, related_name='in_clubs', through='gestion.ClubUser', blank=True), + ), + migrations.RunPython(keep_cof_clubs, restore_cof_clubs), + ] diff --git a/gestion/models.py b/gestion/models.py index 81335ddc..98c06d0e 100644 --- a/gestion/models.py +++ b/gestion/models.py @@ -1,4 +1,4 @@ -from django.contrib.auth.models import User +from django.contrib.auth.models import User, Group from django.db import models from django.dispatch import receiver from django.db.models.signals import post_save, post_delete @@ -17,6 +17,7 @@ OCCUPATION_CHOICES = ( ('CST', _("CST")), ) + class Profile(models.Model): user = models.OneToOneField(User, related_name="profile") @@ -39,6 +40,7 @@ class Profile(models.Model): def __str__(self): return self.user.username + @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: @@ -48,3 +50,53 @@ def create_user_profile(sender, instance, created, **kwargs): @receiver(post_delete, sender=Profile) def post_delete_user(sender, instance, *args, **kwargs): instance.user.delete() + + +class Club(models.Model): + ANNUAL = "ANN" + SEMESTER = "SEM" + COURSE = "COU" + + COTISATION_FREQUENCY_CHOICES = [ + (ANNUAL, _("Annuel")), + (SEMESTER, _("Semestriel")), + (COURSE, _("Au cours")) + ] + + associations = models.ManyToManyField(Group, related_name="clubs") + name = models.CharField(_("Nom"), max_length=200, unique=True) + description = models.TextField("Description", blank=True) + members = models.ManyToManyField( + User, + through="ClubUser", + related_name="in_clubs", + blank=True + ) + price = models.DecimalField( + _("Cotisation (€)"), + decimal_places=2, + max_digits=5, + blank=True, + default=0 + ) + cotisation_frequency = models.CharField( + _("Fréquence de la cotisation"), + default=ANNUAL, + choices=COTISATION_FREQUENCY_CHOICES, + max_length=3, + blank=True + ) + + def __str__(self): + template = ( + self.price and "{name} ({price}€ / {cotisation_frequency})" + or "{name}" + ) + return template.format(**vars(self)) + + +class ClubUser(models.Model): + user = models.ForeignKey(User) + club = models.ForeignKey(Club) + is_respo = models.BooleanField(_("Est responsable du club")) + has_paid = models.BooleanField(_("A payé sa cotisation")) From 1f85f7589663a650e1337d803086a053b9919d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Mon, 20 Feb 2017 01:16:50 +0100 Subject: [PATCH 2/7] Include the Clubs into the admin site --- cof/admin.py | 22 +--------------------- gestion/admin.py | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/cof/admin.py b/cof/admin.py index 177a5adf..227cfb55 100644 --- a/cof/admin.py +++ b/cof/admin.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from django import forms from django.contrib import admin from django.core.urlresolvers import reverse from django.utils.safestring import mark_safe @@ -13,7 +12,7 @@ from .petits_cours_models import PetitCoursDemande, \ PetitCoursAttributionCounter from .models import ( SurveyQuestionAnswer, SurveyQuestion, CofProfile, EventOption, - EventOptionChoice, Event, Club, EventCommentField, EventRegistration, + EventOptionChoice, Event, EventCommentField, EventRegistration, Survey ) @@ -135,30 +134,11 @@ class PetitCoursDemandeAdmin(admin.ModelAdmin): search_fields = ('name', 'email', 'phone', 'lieu', 'remarques') -class ClubAdminForm(forms.ModelForm): - def clean(self): - cleaned_data = super(ClubAdminForm, self).clean() - respos = cleaned_data.get('respos') - members = cleaned_data.get('membres') - for respo in respos.all(): - if respo not in members: - raise forms.ValidationError( - "Erreur : le respo %s n'est pas membre du club." - % respo.get_full_name()) - return cleaned_data - - -class ClubAdmin(admin.ModelAdmin): - list_display = ['name'] - form = ClubAdminForm - - admin.site.register(Survey, SurveyAdmin) admin.site.register(SurveyQuestion, SurveyQuestionAdmin) admin.site.register(Event, EventAdmin) admin.site.register(EventOption, EventOptionAdmin) admin.site.register(CofProfile) -admin.site.register(Club, ClubAdmin) admin.site.register(PetitCoursSubject) admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin) admin.site.register(PetitCoursAttribution, PetitCoursAttributionAdmin) diff --git a/gestion/admin.py b/gestion/admin.py index 52e3cb55..a25c618d 100644 --- a/gestion/admin.py +++ b/gestion/admin.py @@ -2,9 +2,13 @@ from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User -from .models import Profile +from .models import Profile, Club +# --- +# The user related stuff +# --- + class ProfileInline(admin.StackedInline): model = Profile inline_classes = ["collapse open"] @@ -16,5 +20,14 @@ class UserProfileAdmin(UserAdmin): ] +# --- +# Clubs +# --- + +@admin.register(Club) +class ClubAdmin(admin.ModelAdmin): + pass + + admin.site.unregister(User) admin.site.register(User, UserProfileAdmin) From f8a8465630fadacabae69bffd409a267293a5205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Wed, 22 Feb 2017 15:23:37 +0100 Subject: [PATCH 3/7] Remove useless imports --- gestion/urls.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gestion/urls.py b/gestion/urls.py index 55e5658a..0e7d63ac 100644 --- a/gestion/urls.py +++ b/gestion/urls.py @@ -1,7 +1,6 @@ -from django.conf.urls import url, include +from django.conf.urls import url from django.views.generic.base import TemplateView from django.contrib.auth import views as django_views -from django.contrib import admin from django_cas_ng import views as django_cas_views from . import views From 9f401b66e93a937c0986152af027e6f43b29906d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Wed, 22 Feb 2017 19:28:34 +0100 Subject: [PATCH 4/7] Specify the on_delete attribute everywhere - Models - Migrations --- bda/migrations/0001_initial.py | 26 +++++-- bda/migrations/0002_add_tirage.py | 14 +++- bda/migrations/0007_extends_spectacle.py | 11 ++- bda/migrations/0009_revente.py | 23 ++++-- bda/models.py | 65 +++++++++++----- bds/migrations/0001_initial.py | 5 +- cof/migrations/0001_initial.py | 94 +++++++++++++++++++----- cof/migrations/0006_add_calendar.py | 4 +- cof/migrations/0009_generic_profiles.py | 18 +++-- cof/models.py | 68 +++++++++++++---- cof/petits_cours_models.py | 45 ++++++++++-- gestion/migrations/0001_initial.py | 5 +- gestion/models.py | 9 ++- kfet/migrations/0048_generic_profiles.py | 5 +- 14 files changed, 299 insertions(+), 93 deletions(-) diff --git a/bda/migrations/0001_initial.py b/bda/migrations/0001_initial.py index aa2cb252..da447b9d 100644 --- a/bda/migrations/0001_initial.py +++ b/bda/migrations/0001_initial.py @@ -59,7 +59,9 @@ class Migration(migrations.Migration): ('price', models.FloatField(verbose_name=b"Prix d'une place", blank=True)), ('slots', models.IntegerField(verbose_name=b'Places')), ('priority', models.IntegerField(default=1000, verbose_name=b'Priorit\xc3\xa9')), - ('location', models.ForeignKey(to='bda.Salle')), + ('location', models.ForeignKey( + on_delete=models.CASCADE, + to='bda.Salle')), ], options={ 'ordering': ('priority', 'date', 'title'), @@ -79,27 +81,39 @@ class Migration(migrations.Migration): migrations.AddField( model_name='participant', name='user', - field=models.OneToOneField(to=settings.AUTH_USER_MODEL), + field=models.OneToOneField( + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL), ), migrations.AddField( model_name='choixspectacle', name='participant', - field=models.ForeignKey(to='bda.Participant'), + field=models.ForeignKey( + on_delete=models.CASCADE, + to='bda.Participant'), ), migrations.AddField( model_name='choixspectacle', name='spectacle', - field=models.ForeignKey(related_name='participants', to='bda.Spectacle'), + field=models.ForeignKey( + on_delete=models.CASCADE, + related_name='participants', + to='bda.Spectacle'), ), migrations.AddField( model_name='attribution', name='participant', - field=models.ForeignKey(to='bda.Participant'), + field=models.ForeignKey( + on_delete=models.CASCADE, + to='bda.Participant'), ), migrations.AddField( model_name='attribution', name='spectacle', - field=models.ForeignKey(related_name='attribues', to='bda.Spectacle'), + field=models.ForeignKey( + related_name='attribues', + on_delete=models.CASCADE, + to='bda.Spectacle'), ), migrations.AlterUniqueTogether( name='choixspectacle', diff --git a/bda/migrations/0002_add_tirage.py b/bda/migrations/0002_add_tirage.py index 1956a4a4..3b5c5a0b 100644 --- a/bda/migrations/0002_add_tirage.py +++ b/bda/migrations/0002_add_tirage.py @@ -39,18 +39,26 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='participant', name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL), + field=models.ForeignKey( + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL), ), migrations.AddField( model_name='participant', name='tirage', - field=models.ForeignKey(default=1, to='bda.Tirage'), + field=models.ForeignKey( + on_delete=models.CASCADE, + default=1, + to='bda.Tirage'), preserve_default=False, ), migrations.AddField( model_name='spectacle', name='tirage', - field=models.ForeignKey(default=1, to='bda.Tirage'), + field=models.ForeignKey( + on_delete=models.CASCADE, + default=1, + to='bda.Tirage'), preserve_default=False, ), ] diff --git a/bda/migrations/0007_extends_spectacle.py b/bda/migrations/0007_extends_spectacle.py index b95c18de..f6e1834f 100644 --- a/bda/migrations/0007_extends_spectacle.py +++ b/bda/migrations/0007_extends_spectacle.py @@ -72,8 +72,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='spectacle', name='category', - field=models.ForeignKey(blank=True, to='bda.CategorieSpectacle', - null=True), + field=models.ForeignKey( + on_delete=models.CASCADE, + blank=True, + to='bda.CategorieSpectacle', + null=True), ), migrations.AddField( model_name='spectacle', @@ -84,6 +87,8 @@ class Migration(migrations.Migration): migrations.AddField( model_name='quote', name='spectacle', - field=models.ForeignKey(to='bda.Spectacle'), + field=models.ForeignKey( + on_delete=models.CASCADE, + to='bda.Spectacle'), ), ] diff --git a/bda/migrations/0009_revente.py b/bda/migrations/0009_revente.py index 1cca4e86..223a2ed7 100644 --- a/bda/migrations/0009_revente.py +++ b/bda/migrations/0009_revente.py @@ -46,21 +46,28 @@ class Migration(migrations.Migration): migrations.AddField( model_name='spectaclerevente', name='attribution', - field=models.OneToOneField(to='bda.Attribution', - related_name='revente'), + field=models.OneToOneField( + to='bda.Attribution', + on_delete=models.CASCADE, + related_name='revente'), ), migrations.AddField( model_name='spectaclerevente', name='seller', - field=models.ForeignKey(to='bda.Participant', - verbose_name='Vendeur', - related_name='original_shows'), + field=models.ForeignKey( + on_delete=models.CASCADE, + to='bda.Participant', + verbose_name='Vendeur', + related_name='original_shows'), ), migrations.AddField( model_name='spectaclerevente', name='soldTo', - field=models.ForeignKey(to='bda.Participant', - verbose_name='Vendue à', null=True, - blank=True), + field=models.ForeignKey( + on_delete=models.CASCADE, + to='bda.Participant', + verbose_name='Vendue à', + null=True, + blank=True), ), ] diff --git a/bda/models.py b/bda/models.py index a405a665..008f55f5 100644 --- a/bda/models.py +++ b/bda/models.py @@ -46,9 +46,14 @@ class CategorieSpectacle(models.Model): class Spectacle(models.Model): title = models.CharField("Titre", max_length=300) - category = models.ForeignKey(CategorieSpectacle, blank=True, null=True) + category = models.ForeignKey( + CategorieSpectacle, + on_delete=models.CASCADE, + blank=True, + null=True + ) date = models.DateTimeField("Date & heure") - location = models.ForeignKey(Salle) + location = models.ForeignKey(Salle, on_delete=models.CASCADE) vips = models.TextField('Personnalités', blank=True) description = models.TextField("Description", blank=True) slots_description = models.TextField("Description des places", blank=True) @@ -58,7 +63,7 @@ class Spectacle(models.Model): max_length=500) price = models.FloatField("Prix d'une place") slots = models.IntegerField("Places") - tirage = models.ForeignKey(Tirage) + tirage = models.ForeignKey(Tirage, on_delete=models.CASCADE) listing = models.BooleanField("Les places sont sur listing") rappel_sent = models.DateTimeField("Mail de rappel envoyé", blank=True, null=True) @@ -116,7 +121,7 @@ class Spectacle(models.Model): class Quote(models.Model): - spectacle = models.ForeignKey(Spectacle) + spectacle = models.ForeignKey(Spectacle, on_delete=models.CASCADE) text = models.TextField('Citation') author = models.CharField('Auteur', max_length=200) @@ -130,7 +135,7 @@ PAYMENT_TYPES = ( class Participant(models.Model): - user = models.ForeignKey(User) + user = models.ForeignKey(User, on_delete=models.CASCADE) choices = models.ManyToManyField(Spectacle, through="ChoixSpectacle", related_name="chosen_by") @@ -141,7 +146,7 @@ class Participant(models.Model): paymenttype = models.CharField("Moyen de paiement", max_length=6, choices=PAYMENT_TYPES, blank=True) - tirage = models.ForeignKey(Tirage) + tirage = models.ForeignKey(Tirage, on_delete=models.CASCADE) choicesrevente = models.ManyToManyField(Spectacle, related_name="subscribed", blank=True) @@ -157,8 +162,15 @@ DOUBLE_CHOICES = ( class ChoixSpectacle(models.Model): - participant = models.ForeignKey(Participant) - spectacle = models.ForeignKey(Spectacle, related_name="participants") + participant = models.ForeignKey( + Participant, + on_delete=models.CASCADE + ) + spectacle = models.ForeignKey( + Spectacle, + on_delete=models.CASCADE, + related_name="participants" + ) priority = models.PositiveIntegerField("Priorité") double_choice = models.CharField("Nombre de places", default="1", choices=DOUBLE_CHOICES, @@ -185,8 +197,15 @@ class ChoixSpectacle(models.Model): class Attribution(models.Model): - participant = models.ForeignKey(Participant) - spectacle = models.ForeignKey(Spectacle, related_name="attribues") + participant = models.ForeignKey( + Participant, + on_delete=models.CASCADE + ) + spectacle = models.ForeignKey( + Spectacle, + on_delete=models.CASCADE, + related_name="attribues" + ) given = models.BooleanField("Donnée", default=False) def __str__(self): @@ -195,19 +214,29 @@ class Attribution(models.Model): class SpectacleRevente(models.Model): - attribution = models.OneToOneField(Attribution, - related_name="revente") + attribution = models.OneToOneField( + Attribution, + on_delete=models.CASCADE, + related_name="revente" + ) date = models.DateTimeField("Date de mise en vente", default=timezone.now) answered_mail = models.ManyToManyField(Participant, related_name="wanted", blank=True) - seller = models.ForeignKey(Participant, - related_name="original_shows", - verbose_name="Vendeur") - soldTo = models.ForeignKey(Participant, blank=True, null=True, - verbose_name="Vendue à") - + seller = models.ForeignKey( + Participant, + on_delete=models.CASCADE, + related_name="original_shows", + verbose_name="Vendeur" + ) + soldTo = models.ForeignKey( + Participant, + on_delete=models.CASCADE, + blank=True, + null=True, + verbose_name="Vendue à" + ) notif_sent = models.BooleanField("Notification envoyée", default=False) tirage_done = models.BooleanField("Tirage effectué", diff --git a/bds/migrations/0001_initial.py b/bds/migrations/0001_initial.py index e0d3ddd9..3d6f5218 100644 --- a/bds/migrations/0001_initial.py +++ b/bds/migrations/0001_initial.py @@ -23,7 +23,10 @@ class Migration(migrations.Migration): ('cotisation_period', models.CharField(choices=[('ANN', 'Année'), ('SE1', 'Premier semestre'), ('SE2', 'Deuxième semestre')], verbose_name='Inscription', max_length=3, default='ANN')), ('registration_date', models.DateField(verbose_name="Date d'inscription", auto_now_add=True)), ('payment_method', models.CharField(choices=[('CASH', 'Liquide'), ('BANK', 'Transfer bancaire'), ('CHEQUE', 'Cheque'), ('OTHER', 'Autre')], verbose_name='Methode de paiement', max_length=6, default='CASH')), - ('profile', models.OneToOneField(related_name='bds', to='gestion.Profile')), + ('profile', models.OneToOneField( + related_name='bds', + on_delete=models.CASCADE, + to='gestion.Profile')), ], ), ] diff --git a/cof/migrations/0001_initial.py b/cof/migrations/0001_initial.py index 0540e689..fdfeaed4 100644 --- a/cof/migrations/0001_initial.py +++ b/cof/migrations/0001_initial.py @@ -48,7 +48,10 @@ class Migration(migrations.Migration): ('is_buro', models.BooleanField(default=False, verbose_name=b'Membre du Bur\xc3\xb4')), ('petits_cours_accept', models.BooleanField(default=False, verbose_name=b'Recevoir des petits cours')), ('petits_cours_remarques', models.TextField(default=b'', verbose_name='Remarques et pr\xe9cisions pour les petits cours', blank=True)), - ('user', models.OneToOneField(related_name='profile', to=settings.AUTH_USER_MODEL)), + ('user', models.OneToOneField( + related_name='profile', + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL)), ], options={ 'verbose_name': 'Profil COF', @@ -91,7 +94,10 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=200, verbose_name=b'Champ')), ('fieldtype', models.CharField(default=b'text', max_length=10, verbose_name=b'Type', choices=[(b'text', 'Texte long'), (b'char', 'Texte court')])), ('default', models.TextField(verbose_name=b'Valeur par d\xc3\xa9faut', blank=True)), - ('event', models.ForeignKey(related_name='commentfields', to='cof.Event')), + ('event', models.ForeignKey( + related_name='commentfields', + on_delete=models.CASCADE, + to='cof.Event')), ], options={ 'verbose_name': 'Champ', @@ -102,7 +108,10 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('content', models.TextField(null=True, verbose_name=b'Contenu', blank=True)), - ('commentfield', models.ForeignKey(related_name='values', to='cof.EventCommentField')), + ('commentfield', models.ForeignKey( + related_name='values', + on_delete=models.CASCADE, + to='cof.EventCommentField')), ], ), migrations.CreateModel( @@ -111,7 +120,10 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=200, verbose_name=b'Option')), ('multi_choices', models.BooleanField(default=False, verbose_name=b'Choix multiples')), - ('event', models.ForeignKey(related_name='options', to='cof.Event')), + ('event', models.ForeignKey( + related_name='options', + on_delete=models.CASCADE, + to='cof.Event')), ], options={ 'verbose_name': 'Option', @@ -122,7 +134,10 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('value', models.CharField(max_length=200, verbose_name=b'Valeur')), - ('event_option', models.ForeignKey(related_name='choices', to='cof.EventOption')), + ('event_option', models.ForeignKey( + related_name='choices', + on_delete=models.CASCADE, + to='cof.EventOption')), ], options={ 'verbose_name': 'Choix', @@ -133,10 +148,14 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('paid', models.BooleanField(default=False, verbose_name=b'A pay\xc3\xa9')), - ('event', models.ForeignKey(to='cof.Event')), + ('event', models.ForeignKey( + on_delete=models.CASCADE, + to='cof.Event')), ('filledcomments', models.ManyToManyField(to='cof.EventCommentField', through='cof.EventCommentValue')), ('options', models.ManyToManyField(to='cof.EventOptionChoice')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey( + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL)), ], options={ 'verbose_name': 'Inscription', @@ -240,7 +259,10 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('question', models.CharField(max_length=200, verbose_name=b'Question')), ('multi_answers', models.BooleanField(default=False, verbose_name=b'Choix multiples')), - ('survey', models.ForeignKey(related_name='questions', to='cof.Survey')), + ('survey', models.ForeignKey( + on_delete=models.CASCADE, + related_name='questions', + to='cof.Survey')), ], options={ 'verbose_name': 'Question', @@ -251,7 +273,10 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('answer', models.CharField(max_length=200, verbose_name=b'R\xc3\xa9ponse')), - ('survey_question', models.ForeignKey(related_name='answers', to='cof.SurveyQuestion')), + ('survey_question', models.ForeignKey( + related_name='answers', + on_delete=models.CASCADE, + to='cof.SurveyQuestion')), ], options={ 'verbose_name': 'R\xe9ponse', @@ -265,12 +290,16 @@ class Migration(migrations.Migration): migrations.AddField( model_name='surveyanswer', name='survey', - field=models.ForeignKey(to='cof.Survey'), + field=models.ForeignKey( + on_delete=models.CASCADE, + to='cof.Survey'), ), migrations.AddField( model_name='surveyanswer', name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL), + field=models.ForeignKey( + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL), ), migrations.AddField( model_name='petitcoursdemande', @@ -280,47 +309,72 @@ class Migration(migrations.Migration): migrations.AddField( model_name='petitcoursdemande', name='traitee_par', - field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True), + field=models.ForeignKey( + on_delete=models.CASCADE, + blank=True, + to=settings.AUTH_USER_MODEL, + null=True), ), migrations.AddField( model_name='petitcoursattributioncounter', name='matiere', - field=models.ForeignKey(verbose_name='Matiere', to='cof.PetitCoursSubject'), + field=models.ForeignKey( + on_delete=models.CASCADE, + verbose_name='Matiere', + to='cof.PetitCoursSubject'), ), migrations.AddField( model_name='petitcoursattributioncounter', name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL), + field=models.ForeignKey( + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL), ), migrations.AddField( model_name='petitcoursattribution', name='demande', - field=models.ForeignKey(verbose_name='Demande', to='cof.PetitCoursDemande'), + field=models.ForeignKey( + on_delete=models.CASCADE, + verbose_name='Demande', + to='cof.PetitCoursDemande'), ), migrations.AddField( model_name='petitcoursattribution', name='matiere', - field=models.ForeignKey(verbose_name='Mati\xe8re', to='cof.PetitCoursSubject'), + field=models.ForeignKey( + on_delete=models.CASCADE, + verbose_name='Mati\xe8re', + to='cof.PetitCoursSubject'), ), migrations.AddField( model_name='petitcoursattribution', name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL), + field=models.ForeignKey( + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL), ), migrations.AddField( model_name='petitcoursability', name='matiere', - field=models.ForeignKey(verbose_name='Mati\xe8re', to='cof.PetitCoursSubject'), + field=models.ForeignKey( + on_delete=models.CASCADE, + verbose_name='Mati\xe8re', + to='cof.PetitCoursSubject'), ), migrations.AddField( model_name='petitcoursability', name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL), + field=models.ForeignKey( + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL), ), migrations.AddField( model_name='eventcommentvalue', name='registration', - field=models.ForeignKey(related_name='comments', to='cof.EventRegistration'), + field=models.ForeignKey( + on_delete=models.CASCADE, + related_name='comments', + to='cof.EventRegistration'), ), migrations.AlterUniqueTogether( name='surveyanswer', diff --git a/cof/migrations/0006_add_calendar.py b/cof/migrations/0006_add_calendar.py index 3009ac4d..f40ad39f 100644 --- a/cof/migrations/0006_add_calendar.py +++ b/cof/migrations/0006_add_calendar.py @@ -23,7 +23,9 @@ class Migration(migrations.Migration): ('subscribe_to_events', models.BooleanField(default=True)), ('subscribe_to_my_shows', models.BooleanField(default=True)), ('other_shows', models.ManyToManyField(to='bda.Spectacle')), - ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)), + ('user', models.OneToOneField( + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL)), ], ), migrations.AlterModelOptions( diff --git a/cof/migrations/0009_generic_profiles.py b/cof/migrations/0009_generic_profiles.py index 673202b8..ebbba4ae 100644 --- a/cof/migrations/0009_generic_profiles.py +++ b/cof/migrations/0009_generic_profiles.py @@ -23,23 +23,23 @@ def create_profile(apps, schema_editor): def preserve_perms(apps, schema_editor): - from django.contrib.auth.management import create_permissions + # from django.contrib.auth.management import create_permissions - apps.models_module = True - create_permissions(apps, verbosity=0) - apps.models_module = None + # apps.models_module = True + # create_permissions(apps, verbosity=0) + # apps.models_module = None CofProfile = apps.get_model("cof", "CofProfile") - memberp = Permission.objects.get(codename='member') - burop = Permission.objects.get(codename='buro') + # memberp = Permission.objects.get(codename='member') + # burop = Permission.objects.get(codename='buro') # creates the groups for COF members and member = Group.objects.create(name='cof_members') buro = Group.objects.create(name='cof_buro') # associate permissions to the respective groups. - buro.permissions = [burop, memberp] - member.permissions = [memberp] + # buro.permissions = [burop, memberp] + # member.permissions = [memberp] for cofp in CofProfile.objects.filter(is_cof=True): cofp.profile.user.groups.add(member) @@ -70,6 +70,7 @@ class Migration(migrations.Migration): model_name='cofprofile', name='profile', field=models.OneToOneField( + on_delete=models.CASCADE, to='gestion.Profile', null=True, related_name='cof' @@ -89,6 +90,7 @@ class Migration(migrations.Migration): model_name='cofprofile', name='profile', field=models.OneToOneField( + on_delete=models.CASCADE, to='gestion.Profile', related_name='cof' ), diff --git a/cof/models.py b/cof/models.py index dafe9996..bfbaaf5f 100644 --- a/cof/models.py +++ b/cof/models.py @@ -113,7 +113,11 @@ class Event(models.Model): @python_2_unicode_compatible class EventCommentField(models.Model): - event = models.ForeignKey(Event, related_name="commentfields") + event = models.ForeignKey( + Event, + on_delete=models.CASCADE, + related_name="commentfields" + ) name = models.CharField("Champ", max_length=200) fieldtype = models.CharField("Type", max_length=10, choices=TYPE_COMMENT_FIELD, default="text") @@ -128,9 +132,16 @@ class EventCommentField(models.Model): @python_2_unicode_compatible class EventCommentValue(models.Model): - commentfield = models.ForeignKey(EventCommentField, related_name="values") - registration = models.ForeignKey("EventRegistration", - related_name="comments") + commentfield = models.ForeignKey( + EventCommentField, + on_delete=models.CASCADE, + related_name="values" + ) + registration = models.ForeignKey( + "EventRegistration", + on_delete=models.CASCADE, + related_name="comments" + ) content = models.TextField("Contenu", blank=True, null=True) def __str__(self): @@ -139,7 +150,11 @@ class EventCommentValue(models.Model): @python_2_unicode_compatible class EventOption(models.Model): - event = models.ForeignKey(Event, related_name="options") + event = models.ForeignKey( + Event, + on_delete=models.CASCADE, + related_name="options" + ) name = models.CharField("Option", max_length=200) multi_choices = models.BooleanField("Choix multiples", default=False) @@ -152,7 +167,11 @@ class EventOption(models.Model): @python_2_unicode_compatible class EventOptionChoice(models.Model): - event_option = models.ForeignKey(EventOption, related_name="choices") + event_option = models.ForeignKey( + EventOption, + on_delete=models.CASCADE, + related_name="choices" + ) value = models.CharField("Valeur", max_length=200) class Meta: @@ -165,8 +184,14 @@ class EventOptionChoice(models.Model): @python_2_unicode_compatible class EventRegistration(models.Model): - user = models.ForeignKey(User) - event = models.ForeignKey(Event) + user = models.ForeignKey( + User, + on_delete=models.CASCADE + ) + event = models.ForeignKey( + Event, + on_delete=models.CASCADE + ) options = models.ManyToManyField(EventOptionChoice) filledcomments = models.ManyToManyField(EventCommentField, through=EventCommentValue) @@ -197,7 +222,11 @@ class Survey(models.Model): @python_2_unicode_compatible class SurveyQuestion(models.Model): - survey = models.ForeignKey(Survey, related_name="questions") + survey = models.ForeignKey( + Survey, + on_delete=models.CASCADE, + related_name="questions" + ) question = models.CharField("Question", max_length=200) multi_answers = models.BooleanField("Choix multiples", default=False) @@ -210,7 +239,11 @@ class SurveyQuestion(models.Model): @python_2_unicode_compatible class SurveyQuestionAnswer(models.Model): - survey_question = models.ForeignKey(SurveyQuestion, related_name="answers") + survey_question = models.ForeignKey( + SurveyQuestion, + on_delete=models.CASCADE, + related_name="answers" + ) answer = models.CharField("Réponse", max_length=200) class Meta: @@ -222,8 +255,14 @@ class SurveyQuestionAnswer(models.Model): @python_2_unicode_compatible class SurveyAnswer(models.Model): - user = models.ForeignKey(User) - survey = models.ForeignKey(Survey) + user = models.ForeignKey( + User, + on_delete=models.CASCADE + ) + survey = models.ForeignKey( + Survey, + on_delete=models.CASCADE + ) answers = models.ManyToManyField(SurveyQuestionAnswer, related_name="selected_by") @@ -240,7 +279,10 @@ class SurveyAnswer(models.Model): @python_2_unicode_compatible class CalendarSubscription(models.Model): token = models.UUIDField() - user = models.OneToOneField(User) + user = models.OneToOneField( + User, + on_delete=models.CASCADE + ) other_shows = models.ManyToManyField(Spectacle) subscribe_to_events = models.BooleanField(default=True) subscribe_to_my_shows = models.BooleanField(default=True) diff --git a/cof/petits_cours_models.py b/cof/petits_cours_models.py index 753e8674..e67a20bf 100644 --- a/cof/petits_cours_models.py +++ b/cof/petits_cours_models.py @@ -35,8 +35,15 @@ class PetitCoursSubject(models.Model): class PetitCoursAbility(models.Model): - user = models.ForeignKey(User) - matiere = models.ForeignKey(PetitCoursSubject, verbose_name=_("Matière")) + user = models.ForeignKey( + User, + on_delete=models.CASCADE + ) + matiere = models.ForeignKey( + PetitCoursSubject, + on_delete=models.CASCADE, + verbose_name=_("Matière") + ) niveau = models.CharField(_("Niveau"), choices=LEVELS_CHOICES, max_length=choices_length(LEVELS_CHOICES)) @@ -84,7 +91,11 @@ class PetitCoursDemande(models.Model): remarques = models.TextField(_("Remarques et précisions"), blank=True) traitee = models.BooleanField(_("Traitée"), default=False) - traitee_par = models.ForeignKey(User, blank=True, null=True) + traitee_par = models.ForeignKey( + User, + on_delete=models.CASCADE, + blank=True, null=True + ) processed = models.DateTimeField(_("Date de traitement"), blank=True, null=True) created = models.DateTimeField(_("Date de création"), auto_now_add=True) @@ -126,9 +137,20 @@ class PetitCoursDemande(models.Model): class PetitCoursAttribution(models.Model): - user = models.ForeignKey(User) - demande = models.ForeignKey(PetitCoursDemande, verbose_name=_("Demande")) - matiere = models.ForeignKey(PetitCoursSubject, verbose_name=_("Matière")) + user = models.ForeignKey( + User, + on_delete=models.CASCADE + ) + demande = models.ForeignKey( + PetitCoursDemande, + on_delete=models.CASCADE, + verbose_name=_("Demande") + ) + matiere = models.ForeignKey( + PetitCoursSubject, + on_delete=models.CASCADE, + verbose_name=_("Matière") + ) date = models.DateTimeField(_("Date d'attribution"), auto_now_add=True) rank = models.IntegerField("Rang dans l'email") selected = models.BooleanField(_("Sélectionné par le demandeur"), @@ -145,8 +167,15 @@ class PetitCoursAttribution(models.Model): class PetitCoursAttributionCounter(models.Model): - user = models.ForeignKey(User) - matiere = models.ForeignKey(PetitCoursSubject, verbose_name=_("Matiere")) + user = models.ForeignKey( + User, + on_delete=models.CASCADE + ) + matiere = models.ForeignKey( + PetitCoursSubject, + on_delete=models.CASCADE, + verbose_name=_("Matiere") + ) count = models.IntegerField("Nombre d'envois", default=0) @classmethod diff --git a/gestion/migrations/0001_initial.py b/gestion/migrations/0001_initial.py index daeaa39b..a301bb9a 100644 --- a/gestion/migrations/0001_initial.py +++ b/gestion/migrations/0001_initial.py @@ -21,7 +21,10 @@ class Migration(migrations.Migration): ('occupation', models.CharField(choices=[('exterieur', 'Extérieur'), ('1A', '1A'), ('2A', '2A'), ('3A', '3A'), ('4A', '4A'), ('archicube', 'Archicube'), ('doctorant', 'Doctorant'), ('CST', 'CST')], verbose_name='Occupation', max_length=9, default='1A')), ('departement', models.CharField(verbose_name='Département', max_length=50, blank=True)), ('comments', models.TextField(verbose_name="Commentaires visibles par l'utilisateur", blank=True)), - ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, related_name='profile')), + ('user', models.OneToOneField( + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL, + related_name='profile')), ], options={ 'verbose_name': 'Profil', diff --git a/gestion/models.py b/gestion/models.py index 81335ddc..78d8aaab 100644 --- a/gestion/models.py +++ b/gestion/models.py @@ -17,9 +17,13 @@ OCCUPATION_CHOICES = ( ('CST', _("CST")), ) -class Profile(models.Model): - user = models.OneToOneField(User, related_name="profile") +class Profile(models.Model): + user = models.OneToOneField( + User, + on_delete=models.CASCADE, + related_name="profile" + ) login_clipper = models.CharField("Login clipper", max_length=8, blank=True) phone = models.CharField("Téléphone", max_length=20, blank=True) occupation = models.CharField(_("Occupation"), @@ -39,6 +43,7 @@ class Profile(models.Model): def __str__(self): return self.user.username + @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: diff --git a/kfet/migrations/0048_generic_profiles.py b/kfet/migrations/0048_generic_profiles.py index e3fda98b..ef5eae4e 100644 --- a/kfet/migrations/0048_generic_profiles.py +++ b/kfet/migrations/0048_generic_profiles.py @@ -15,6 +15,9 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='account', name='cofprofile', - field=models.OneToOneField(to='gestion.Profile', related_name='account_kfet'), + field=models.OneToOneField( + to='gestion.Profile', + on_delete=models.CASCADE, + related_name='account_kfet'), ), ] From 7d1c1fc868ac5ffdb283023b73b4b81ca32d0e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Thu, 23 Feb 2017 10:47:48 +0100 Subject: [PATCH 5/7] Specify the on_delete strategy - Remove an absurd debug line in the migration - Specify the on_delete strategy for the club-related models --- gestion/migrations/0002_club_support.py | 9 ++++++--- gestion/models.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/gestion/migrations/0002_club_support.py b/gestion/migrations/0002_club_support.py index 6b23882b..dc16f400 100644 --- a/gestion/migrations/0002_club_support.py +++ b/gestion/migrations/0002_club_support.py @@ -64,7 +64,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(lambda: 1/0), migrations.CreateModel( name='Club', fields=[ @@ -82,8 +81,12 @@ class Migration(migrations.Migration): ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), ('is_respo', models.BooleanField(verbose_name='Est responsable du club')), ('has_paid', models.BooleanField(verbose_name='A payé sa cotisation')), - ('club', models.ForeignKey(to='gestion.Club')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('club', models.ForeignKey( + to='gestion.Club', + on_delete=models.CASCADE)), + ('user', models.ForeignKey( + on_delete=models.CASCADE, + to=settings.AUTH_USER_MODEL)), ], ), migrations.AddField( diff --git a/gestion/models.py b/gestion/models.py index 98c06d0e..5716cb1b 100644 --- a/gestion/models.py +++ b/gestion/models.py @@ -96,7 +96,7 @@ class Club(models.Model): class ClubUser(models.Model): - user = models.ForeignKey(User) - club = models.ForeignKey(Club) + user = models.ForeignKey(User, on_delete=models.CASCADE) + club = models.ForeignKey(Club, on_delete=models.CASCADE) is_respo = models.BooleanField(_("Est responsable du club")) has_paid = models.BooleanField(_("A payé sa cotisation")) From 213c11721e4ded716b01560c693e3849ebde4999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Thu, 23 Feb 2017 11:03:28 +0100 Subject: [PATCH 6/7] Prevent petits cours demandes deletion --- cof/migrations/0001_initial.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cof/migrations/0001_initial.py b/cof/migrations/0001_initial.py index fdfeaed4..1ba71322 100644 --- a/cof/migrations/0001_initial.py +++ b/cof/migrations/0001_initial.py @@ -310,7 +310,7 @@ class Migration(migrations.Migration): model_name='petitcoursdemande', name='traitee_par', field=models.ForeignKey( - on_delete=models.CASCADE, + on_delete=models.PROTECT, blank=True, to=settings.AUTH_USER_MODEL, null=True), @@ -319,7 +319,7 @@ class Migration(migrations.Migration): model_name='petitcoursattributioncounter', name='matiere', field=models.ForeignKey( - on_delete=models.CASCADE, + on_delete=models.PROTECT, verbose_name='Matiere', to='cof.PetitCoursSubject'), ), @@ -327,7 +327,7 @@ class Migration(migrations.Migration): model_name='petitcoursattributioncounter', name='user', field=models.ForeignKey( - on_delete=models.CASCADE, + on_delete=models.PROTECT, to=settings.AUTH_USER_MODEL), ), migrations.AddField( @@ -342,7 +342,7 @@ class Migration(migrations.Migration): model_name='petitcoursattribution', name='matiere', field=models.ForeignKey( - on_delete=models.CASCADE, + on_delete=models.PROTECT, verbose_name='Mati\xe8re', to='cof.PetitCoursSubject'), ), From 83e73376ad141fa9ded7ef163b38ce790b1b3a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Thu, 23 Feb 2017 12:04:33 +0100 Subject: [PATCH 7/7] Change CofProfile to Profile in kfet/backends.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - K-Fêt accounts are now linked to profiles - There is no need to perform the `get_or_create` as long as the profile creation has been automated. - This file is now PEP8 compliant --- kfet/backends.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/kfet/backends.py b/kfet/backends.py index d0dcc5f6..56d85b81 100644 --- a/kfet/backends.py +++ b/kfet/backends.py @@ -1,15 +1,11 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from builtins import * - import hashlib from django.contrib.auth.models import User, Permission -from cof.models import CofProfile from kfet.models import Account, GenericTeamToken + class KFetBackend(object): def authenticate(self, request): password = request.POST.get('KFETPASSWORD', '') @@ -17,8 +13,8 @@ class KFetBackend(object): if not password: return None + password_sha256 = hashlib.sha256(password.encode('utf-8')).hexdigest() try: - password_sha256 = hashlib.sha256(password.encode('utf-8')).hexdigest() account = Account.objects.get(password=password_sha256) user = account.profile.user except Account.DoesNotExist: @@ -26,16 +22,20 @@ class KFetBackend(object): return user + class GenericTeamBackend(object): + """ + Authenticate using the generic_team user. + """ def authenticate(self, username=None, token=None): valid_token = GenericTeamToken.objects.get(token=token) if username == 'kfet_genericteam' and valid_token: # Création du user s'il n'existe pas déjà user, _ = User.objects.get_or_create(username='kfet_genericteam') - profile, _ = CofProfile.objects.get_or_create(user=user) account, _ = Account.objects.get_or_create( - profile=profile, - trigramme='GNR') + profile=user.profile, + trigramme='GNR' + ) # Ajoute la permission kfet.is_team à ce user perm_is_team = Permission.objects.get(codename='is_team')