diff --git a/AKModel/migrations/0028_unique_constraints.py b/AKModel/migrations/0028_unique_constraints.py new file mode 100644 index 0000000000000000000000000000000000000000..2cba695a4379d44d4b0393d1a32d2a12a512b32c --- /dev/null +++ b/AKModel/migrations/0028_unique_constraints.py @@ -0,0 +1,57 @@ +# Generated by Django 2.2.6 on 2020-05-10 10:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('AKModel', '0027_event_timezone'), + ] + + operations = [ + migrations.AlterField( + model_name='akcategory', + name='name', + field=models.CharField(help_text='Name of the AK Category', max_length=64, verbose_name='Name'), + ), + migrations.AlterField( + model_name='akowner', + name='slug', + field=models.SlugField(blank=True, help_text='Slug for URL generation', max_length=64, verbose_name='Slug'), + ), + migrations.AlterField( + model_name='akrequirement', + name='name', + field=models.CharField(help_text='Name of the Requirement', max_length=128, verbose_name='Name'), + ), + migrations.AlterField( + model_name='aktrack', + name='name', + field=models.CharField(help_text='Name of the AK Track', max_length=64, verbose_name='Name'), + ), + migrations.AlterUniqueTogether( + name='ak', + unique_together={('event', 'name'), ('event', 'short_name')}, + ), + migrations.AlterUniqueTogether( + name='akcategory', + unique_together={('event', 'name')}, + ), + migrations.AlterUniqueTogether( + name='akowner', + unique_together={('event', 'name', 'institution'), ('event', 'slug')}, + ), + migrations.AlterUniqueTogether( + name='akrequirement', + unique_together={('event', 'name')}, + ), + migrations.AlterUniqueTogether( + name='aktrack', + unique_together={('event', 'name')}, + ), + migrations.AlterUniqueTogether( + name='room', + unique_together={('event', 'name', 'building')}, + ), + ] diff --git a/AKModel/models.py b/AKModel/models.py index 24ceb4d7e7a1771a34b293b549f41f57dd7d2562..c9aab78c92aa4cc427e837f889b052e48ef85c19 100644 --- a/AKModel/models.py +++ b/AKModel/models.py @@ -50,8 +50,7 @@ class AKOwner(models.Model): """ An AKOwner describes the person organizing/holding an AK. """ name = models.CharField(max_length=64, verbose_name=_('Nickname'), help_text=_('Name to identify an AK owner by')) - slug = models.SlugField(max_length=64, blank=True, unique=True, verbose_name=_('Slug'), - help_text=_('Slug for URL generation')) + slug = models.SlugField(max_length=64, blank=True, verbose_name=_('Slug'), help_text=_('Slug for URL generation')) institution = models.CharField(max_length=128, blank=True, verbose_name=_('Institution'), help_text=_('Uni etc.')) link = models.URLField(blank=True, verbose_name=_('Web Link'), help_text=_('Link to Homepage')) @@ -62,7 +61,7 @@ class AKOwner(models.Model): verbose_name = _('AK Owner') verbose_name_plural = _('AK Owners') ordering = ['name'] - unique_together = [['name', 'institution']] + unique_together = [['event', 'name', 'institution'], ['event', 'slug']] def __str__(self): if self.institution: @@ -73,15 +72,15 @@ class AKOwner(models.Model): max_length = self._meta.get_field('slug').max_length slug_candidate = slugify(self.name)[:max_length] - if not AKOwner.objects.filter(slug=slug_candidate).exists(): + if not AKOwner.objects.filter(event=self.event, slug=slug_candidate).exists(): self.slug = slug_candidate return slug_candidate = slugify(slug_candidate + '_' + self.institution)[:max_length] - if not AKOwner.objects.filter(slug=slug_candidate).exists(): + if not AKOwner.objects.filter(event=self.event, slug=slug_candidate).exists(): self.slug = slug_candidate return for i in itertools.count(1): - if not AKOwner.objects.filter(slug=slug_candidate).exists(): + if not AKOwner.objects.filter(event=self.event, slug=slug_candidate).exists(): break digits = len(str(i)) slug_candidate = '{}-{}'.format(slug_candidate[:-(digits + 1)], i) @@ -95,14 +94,14 @@ class AKOwner(models.Model): super().save(*args, **kwargs) @staticmethod - def get_by_slug(slug): - return AKOwner.objects.get(slug=slug) + def get_by_slug(event, slug): + return AKOwner.objects.get(event=event, slug=slug) class AKCategory(models.Model): """ An AKCategory describes the characteristics of an AK, e.g. content vs. recreational. """ - name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'), help_text=_('Name of the AK Category')) + name = models.CharField(max_length=64, verbose_name=_('Name'), help_text=_('Name of the AK Category')) color = models.CharField(max_length=7, blank=True, verbose_name=_('Color'), help_text=_('Color for displaying')) description = models.TextField(blank=True, verbose_name=_("Description"), help_text=_("Short description of this AK Category")) @@ -114,6 +113,7 @@ class AKCategory(models.Model): verbose_name = _('AK Category') verbose_name_plural = _('AK Categories') ordering = ['name'] + unique_together = ['event', 'name'] def __str__(self): return self.name @@ -122,7 +122,7 @@ class AKCategory(models.Model): class AKTrack(models.Model): """ An AKTrack describes a set of semantically related AKs. """ - name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'), help_text=_('Name of the AK Track')) + name = models.CharField(max_length=64, verbose_name=_('Name'), help_text=_('Name of the AK Track')) color = models.CharField(max_length=7, blank=True, verbose_name=_('Color'), help_text=_('Color for displaying')) event = models.ForeignKey(to=Event, on_delete=models.CASCADE, verbose_name=_('Event'), @@ -132,6 +132,7 @@ class AKTrack(models.Model): verbose_name = _('AK Track') verbose_name_plural = _('AK Tracks') ordering = ['name'] + unique_together = ['event', 'name'] def __str__(self): return self.name @@ -154,7 +155,7 @@ class AKTag(models.Model): class AKRequirement(models.Model): """ An AKRequirement describes something needed to hold an AK, e.g. infrastructure. """ - name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'), help_text=_('Name of the Requirement')) + name = models.CharField(max_length=128, verbose_name=_('Name'), help_text=_('Name of the Requirement')) event = models.ForeignKey(to=Event, on_delete=models.CASCADE, verbose_name=_('Event'), help_text=_('Associated event')) @@ -163,6 +164,7 @@ class AKRequirement(models.Model): verbose_name = _('AK Requirement') verbose_name_plural = _('AK Requirements') ordering = ['name'] + unique_together = ['event', 'name'] def __str__(self): return self.name @@ -211,7 +213,7 @@ class AK(models.Model): class Meta: verbose_name = _('AK') verbose_name_plural = _('AKs') - unique_together = [('name', 'event'), ('short_name', 'event')] + unique_together = [['event', 'name'], ['event', 'short_name']] def __str__(self): if self.short_name: @@ -248,7 +250,7 @@ class Room(models.Model): verbose_name = _('Room') verbose_name_plural = _('Rooms') ordering = ['building', 'name'] - unique_together = [['name', 'building']] + unique_together = ['event', 'name', 'building'] @property def title(self): diff --git a/AKSubmission/forms.py b/AKSubmission/forms.py index dc8bd854d7834bdc41291355baff24b1d57be6bc..8b605f3c69bb7eb02b312b9ae0aa1bcbd413f749 100644 --- a/AKSubmission/forms.py +++ b/AKSubmission/forms.py @@ -134,7 +134,10 @@ class AKOwnerForm(forms.ModelForm): class Meta: model = AKOwner - fields = ['name', 'institution', 'link'] + fields = ['name', 'institution', 'link', 'event'] + widgets = { + 'event': forms.HiddenInput + } class AKDurationForm(forms.ModelForm): diff --git a/AKSubmission/views.py b/AKSubmission/views.py index 99968a106f04ab0982221ef8040165987718de4e..9cff56f48c5626ba460cf818021c4fd9fa5b823d 100644 --- a/AKSubmission/views.py +++ b/AKSubmission/views.py @@ -8,7 +8,6 @@ from django.views import View from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView from AKModel.models import AK, AKCategory, AKTag, AKOwner, AKSlot -from AKModel.models import Event from AKModel.views import EventSlugMixin from AKModel.views import FilterByEventSlugMixin from AKSubmission.forms import AKWishForm, AKOwnerForm, AKEditForm, AKSubmissionForm, AKDurationForm @@ -128,13 +127,13 @@ class AKAndAKWishSubmissionView(EventSlugMixin, CreateView): class AKSubmissionView(AKAndAKWishSubmissionView): def get_initial(self): initials = super(AKAndAKWishSubmissionView, self).get_initial() - initials['owners'] = [AKOwner.get_by_slug(self.kwargs['owner_slug'])] + initials['owners'] = [AKOwner.get_by_slug(self.event, self.kwargs['owner_slug'])] initials['event'] = self.event return initials def get_context_data(self, *, object_list=None, **kwargs): context = super().get_context_data(object_list=object_list, **kwargs) - context['owner'] = get_object_or_404(AKOwner, slug=self.kwargs['owner_slug']) + context['owner'] = get_object_or_404(AKOwner, event=self.event, slug=self.kwargs['owner_slug']) return context @@ -180,13 +179,10 @@ class AKOwnerCreateView(EventSlugMixin, CreateView): return reverse_lazy('submit:submit_ak', kwargs={'event_slug': self.kwargs['event_slug'], 'owner_slug': self.object.slug}) - def form_valid(self, form): - instance = form.save(commit=False) - - # Set event - instance.event = Event.get_by_slug(self.kwargs["event_slug"]) - - return super().form_valid(form) + def get_initial(self): + initials = super(AKOwnerCreateView, self).get_initial() + initials['event'] = self.event + return initials class AKOwnerSelectDispatchView(EventSlugMixin, View): @@ -206,7 +202,7 @@ class AKOwnerSelectDispatchView(EventSlugMixin, View): reverse_lazy('submit:submit_ak', kwargs={'event_slug': kwargs['event_slug'], 'owner_slug': owner.slug})) -class AKOwnerEditView(EventSlugMixin, UpdateView): +class AKOwnerEditView(FilterByEventSlugMixin, UpdateView): model = AKOwner template_name = "AKSubmission/akowner_create_update.html" form_class = AKOwnerForm @@ -252,7 +248,8 @@ class AKSlotAddView(EventSlugMixin, CreateView): def get_success_url(self): messages.add_message(self.request, messages.SUCCESS, _("AK Slot successfully added")) - return reverse_lazy('submit:ak_detail', kwargs={'event_slug': self.kwargs['event_slug'], 'pk': self.object.ak.pk}) + return reverse_lazy('submit:ak_detail', + kwargs={'event_slug': self.kwargs['event_slug'], 'pk': self.object.ak.pk}) class AKSlotEditView(EventSlugMixin, UpdateView): @@ -263,7 +260,8 @@ class AKSlotEditView(EventSlugMixin, UpdateView): def get(self, request, *args, **kwargs): akslot = get_object_or_404(AKSlot, pk=kwargs["pk"]) if akslot.start is not None: - messages.add_message(self.request, messages.WARNING, _("You cannot edit a slot that has already been scheduled")) + messages.add_message(self.request, messages.WARNING, + _("You cannot edit a slot that has already been scheduled")) return redirect('submit:ak_detail', event_slug=self.kwargs['event_slug'], pk=akslot.ak.pk) return super().get(request, *args, **kwargs) @@ -274,7 +272,8 @@ class AKSlotEditView(EventSlugMixin, UpdateView): def get_success_url(self): messages.add_message(self.request, messages.SUCCESS, _("AK Slot successfully updated")) - return reverse_lazy('submit:ak_detail', kwargs={'event_slug': self.kwargs['event_slug'], 'pk': self.object.ak.pk}) + return reverse_lazy('submit:ak_detail', + kwargs={'event_slug': self.kwargs['event_slug'], 'pk': self.object.ak.pk}) class AKSlotDeleteView(EventSlugMixin, DeleteView): @@ -284,7 +283,8 @@ class AKSlotDeleteView(EventSlugMixin, DeleteView): def get(self, request, *args, **kwargs): akslot = get_object_or_404(AKSlot, pk=kwargs["pk"]) if akslot.start is not None: - messages.add_message(self.request, messages.WARNING, _("You cannot delete a slot that has already been scheduled")) + messages.add_message(self.request, messages.WARNING, + _("You cannot delete a slot that has already been scheduled")) return redirect('submit:ak_detail', event_slug=self.kwargs['event_slug'], pk=akslot.ak.pk) return super().get(request, *args, **kwargs) @@ -295,4 +295,5 @@ class AKSlotDeleteView(EventSlugMixin, DeleteView): def get_success_url(self): messages.add_message(self.request, messages.SUCCESS, _("AK Slot successfully deleted")) - return reverse_lazy('submit:ak_detail', kwargs={'event_slug': self.kwargs['event_slug'], 'pk': self.object.ak.pk}) + return reverse_lazy('submit:ak_detail', + kwargs={'event_slug': self.kwargs['event_slug'], 'pk': self.object.ak.pk})