Convert event from integer field to foreign key

This commit also fix conference creation for initial Site
This commit is contained in:
Élie Bouttier 2016-08-22 17:23:01 +02:00
parent 4049841028
commit 22a0fe3ca9
11 changed files with 155 additions and 14 deletions

View File

@ -7,3 +7,4 @@ class AccountsConfig(AppConfig):
def ready(self): def ready(self):
import accounts.signals # noqa import accounts.signals # noqa
post_migrate.connect(accounts.signals.create_default_options, sender=self)

View File

@ -2,7 +2,7 @@ from django.contrib import messages
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.signals import user_logged_in, user_logged_out from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.contrib.sites.shortcuts import get_current_site from django.contrib.sites.shortcuts import get_current_site
from django.db.models.signals import post_migrate, post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext_noop from django.utils.translation import ugettext_noop
@ -10,7 +10,6 @@ from django.utils.translation import ugettext_noop
from .models import Connector, Participation, Profile, Transport from .models import Connector, Participation, Profile, Transport
@receiver(post_migrate)
def create_default_options(sender, **kwargs): def create_default_options(sender, **kwargs):
Transport.objects.get_or_create(name=ugettext_noop('Train')) Transport.objects.get_or_create(name=ugettext_noop('Train'))
Transport.objects.get_or_create(name=ugettext_noop('Plane')) Transport.objects.get_or_create(name=ugettext_noop('Plane'))

View File

@ -6,7 +6,7 @@ from django.core import mail
from django.conf import settings from django.conf import settings
from accounts.models import Participation from accounts.models import Participation
from proposals.models import Topic, Talk from proposals.models import Topic, Talk, Event
from .models import ConversationAboutTalk, ConversationWithParticipant, Message from .models import ConversationAboutTalk, ConversationWithParticipant, Message
@ -20,7 +20,8 @@ class ConversationTests(TestCase):
conversation, _ = ConversationWithParticipant.objects.get_or_create(participation=pa) conversation, _ = ConversationWithParticipant.objects.get_or_create(participation=pa)
Message.objects.create(content='allo', conversation=conversation, author=b) Message.objects.create(content='allo', conversation=conversation, author=b)
Message.objects.create(content='aluil', conversation=conversation, author=a) Message.objects.create(content='aluil', conversation=conversation, author=a)
Talk.objects.get_or_create(site=Site.objects.first(), proposer=a, title='a talk', description='yay') site = Site.objects.first()
Talk.objects.get_or_create(site=site, proposer=a, title='a talk', description='yay', event=Event.objects.get(site=site, name='other'))
def test_models(self): def test_models(self):
talk, participant, message = (model.objects.first() for model in talk, participant, message = (model.objects.first() for model in

View File

@ -33,6 +33,9 @@ ALLOWED_HOSTS = []
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
# the post_migrate creating the first site should be call at first
'django.contrib.sites',
# our apps # our apps
'accounts', 'accounts',
'ponyconf', 'ponyconf',
@ -53,7 +56,6 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.sites',
] ]
MIDDLEWARE_CLASSES = [ MIDDLEWARE_CLASSES = [

View File

@ -0,0 +1 @@
default_app_config = 'proposals.apps.ProposalsConfig'

View File

@ -1,7 +1,23 @@
from django.contrib import admin from django.contrib import admin
from proposals.models import Conference, Talk, Topic from proposals.models import Conference, Talk, Topic, Event
class TalkAdmin(admin.ModelAdmin):
# Disable add button in django admin has it is too dangerous
# (it is easy to obtain incoherent data due to site framework)
def has_add_permission(self, request):
return False
# Filter for 'on site' tocpis and event
def get_form(self, request, obj=None, **kwargs):
form = super(TalkAdmin, self).get_form(request, obj, **kwargs)
# in fact, obj should never be none as 'add' button is disabled
if obj:
form.base_fields['topics'].queryset = Topic.objects.filter(site=obj.site)
form.base_fields['event'].queryset = Event.objects.filter(site=obj.site)
return form
admin.site.register(Conference) admin.site.register(Conference)
admin.site.register(Topic) admin.site.register(Topic)
admin.site.register(Talk) admin.site.register(Talk, TalkAdmin)
admin.site.register(Event)

View File

@ -1,4 +1,5 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.db.models.signals import post_migrate
class ProposalsConfig(AppConfig): class ProposalsConfig(AppConfig):
@ -6,3 +7,4 @@ class ProposalsConfig(AppConfig):
def ready(self): def ready(self):
import proposals.signals # noqa import proposals.signals # noqa
post_migrate.connect(proposals.signals.call_first_site_post_save, sender=self)

View File

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-08-22 10:10
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
def migrate_event(apps, schema_editor):
db_alias = schema_editor.connection.alias
# Create default Event instance, as Site post_save is not triggered
Event = apps.get_model('proposals', 'Event')
Site = apps.get_model('sites', 'Site')
for site in Site.objects.using(db_alias).all():
Event.objects.using(db_alias).bulk_create([
Event(site=site, name='conference (short)'),
Event(site=site, name='conference (long)'),
Event(site=site, name='workshop'),
Event(site=site, name='stand'),
Event(site=site, name='other'),
])
# Migrate event_old field to event field
Talk = apps.get_model('proposals', 'Talk')
mapping = {
0: 'conference (short)',
1: 'conference (long)',
2: 'workshop',
3: 'stand',
4: 'other',
}
for talk in Talk.objects.using(db_alias).all():
talk.event = Event.objects.using(db_alias).get(site=talk.site, name=mapping[talk.event_old])
talk.save()
class Migration(migrations.Migration):
dependencies = [
('sites', '0002_alter_domain_unique'),
('proposals', '0009_auto_20160822_0921'),
]
operations = [
# Creation Event model
migrations.CreateModel(
name='Event',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
),
migrations.AlterUniqueTogether(
name='event',
unique_together=set([('site', 'name')]),
),
# Move field event to event_old
migrations.RenameField(
model_name='talk',
old_name='event',
new_name='event_old',
),
# Add field event as ForeignKey to Event model (with NULL allowed for now)
migrations.AddField(
model_name='talk',
name='event',
field=models.ForeignKey(to='proposals.Event', null=True),
),
# We slightly need to modify the ordering to work with the M2M
migrations.AlterModelOptions(
name='talk',
options={'ordering': ('event__id',)},
),
# Migrate the data from event_old field to event field
migrations.RunPython(migrate_event),
# Remove the event_old field as data have been migrated
migrations.RemoveField(
model_name='talk',
name='event_old',
),
# As data are now migrated, switch to null=False for event field
migrations.AlterField(
model_name='talk',
name='event',
field=models.ForeignKey(to='proposals.Event')
),
]

View File

@ -6,6 +6,7 @@ from django.core.urlresolvers import reverse
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from autoslug import AutoSlugField from autoslug import AutoSlugField
@ -44,9 +45,19 @@ class Topic(PonyConfModel):
return reverse('list-talks-by-topic', kwargs={'topic': self.slug}) return reverse('list-talks-by-topic', kwargs={'topic': self.slug})
class Talk(PonyConfModel): class Event(models.Model):
EVENTS = IntEnum('Event', 'conference_short conference_long workshop stand other') site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
class Meta:
unique_together = ('site', 'name')
def __str__(self):
return ugettext(self.name)
class Talk(PonyConfModel):
site = models.ForeignKey(Site, on_delete=models.CASCADE) site = models.ForeignKey(Site, on_delete=models.CASCADE)
@ -58,7 +69,7 @@ class Talk(PonyConfModel):
description = models.TextField(blank=True, verbose_name=_('Description')) description = models.TextField(blank=True, verbose_name=_('Description'))
topics = models.ManyToManyField(Topic, blank=True, verbose_name=_('Topics')) topics = models.ManyToManyField(Topic, blank=True, verbose_name=_('Topics'))
notes = models.TextField(blank=True, verbose_name=_('Notes')) notes = models.TextField(blank=True, verbose_name=_('Notes'))
event = models.IntegerField(choices=enum_to_choices(EVENTS), default=EVENTS.conference_short.value, verbose_name=_('Format')) event = models.ForeignKey(Event)
accepted = models.NullBooleanField(default=None) accepted = models.NullBooleanField(default=None)
def __str__(self): def __str__(self):
@ -83,7 +94,7 @@ class Talk(PonyConfModel):
return query_sum(self.vote_set, 'vote') return query_sum(self.vote_set, 'vote')
class Meta: class Meta:
ordering = ('event',) ordering = ('event__id',)
class Vote(PonyConfModel): class Vote(PonyConfModel):

View File

@ -1,10 +1,13 @@
from django.db.models.signals import m2m_changed, post_migrate, post_save from django.db.models.signals import m2m_changed, post_save
from django.dispatch import Signal, receiver from django.dispatch import Signal, receiver
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.utils.translation import ugettext_noop
from django.conf import settings
from accounts.models import Participation from accounts.models import Participation
from .models import Conference, Talk, Topic from .models import Conference, Talk, Topic, Event
talk_added = Signal(providing_args=["sender", "instance", "author"]) talk_added = Signal(providing_args=["sender", "instance", "author"])
talk_edited = Signal(providing_args=["sender", "instance", "author"]) talk_edited = Signal(providing_args=["sender", "instance", "author"])
@ -15,6 +18,24 @@ def create_conference(sender, instance, **kwargs):
Conference.objects.get_or_create(site=instance) Conference.objects.get_or_create(site=instance)
@receiver(post_save, sender=Site, dispatch_uid="Create default events type for Site")
def create_events(sender, instance, **kwargs):
if not Event.objects.filter(site=instance).exists():
Event.objects.bulk_create([
Event(site=instance, name=ugettext_noop('conference (short)')),
Event(site=instance, name=ugettext_noop('conference (long)')),
Event(site=instance, name=ugettext_noop('workshop')),
Event(site=instance, name=ugettext_noop('stand')),
Event(site=instance, name=ugettext_noop('other')),
])
def call_first_site_post_save(apps, **kwargs):
site = Site.objects.filter(id=getattr(settings, 'SITE_ID', 1))
if site.exists():
site.first().save()
@receiver(m2m_changed, sender=Talk.speakers.through, dispatch_uid="Create Participation for speakers") @receiver(m2m_changed, sender=Talk.speakers.through, dispatch_uid="Create Participation for speakers")
def create_participation_for_speakers(sender, instance, action, reverse, model, pk_set, using, **kwargs): def create_participation_for_speakers(sender, instance, action, reverse, model, pk_set, using, **kwargs):
if action != "pre_add": if action != "pre_add":

View File

@ -14,7 +14,7 @@
<h3>{% trans "Format:" %}</h3> <h3>{% trans "Format:" %}</h3>
<p>{{ talk.get_event_display }}</p> <p>{{ talk.event }}</p>
<h3>{% trans "Abstract:" %}</h3> <h3>{% trans "Abstract:" %}</h3>