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/4] 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/4] 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/4] 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 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 4/4] 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"))