From 31cf42a5e7b9a62aa2ce6f865c9ece99959c27af Mon Sep 17 00:00:00 2001 From: David Gardiner Date: Tue, 27 Mar 2018 13:13:08 -0500 Subject: [PATCH 1/5] Add a "Tag" model for event classification. In order to classify an event (for positioning on the home page or if users need to pay for it) a "Tag" model was created and linked to the "Event" model through a ManyToManyField. The tags are displayed as a CheckboxSelectMultiple widget to make it user-friendly. --- ACM_General/events/forms.py | 8 +++--- ACM_General/events/models.py | 47 ++++++++++++++++++++++++++++++++++++ ACM_General/events/views.py | 6 +++-- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/ACM_General/events/forms.py b/ACM_General/events/forms.py index 312928a..80b0cef 100755 --- a/ACM_General/events/forms.py +++ b/ACM_General/events/forms.py @@ -4,10 +4,10 @@ # Django from django.forms import ModelForm -from django.forms.widgets import DateTimeInput, Textarea, TextInput +from django.forms.widgets import DateTimeInput, Textarea, TextInput, CheckboxSelectMultiple # local Django -from .models import Event +from .models import Event, Tag class EventForm(ModelForm): @@ -15,6 +15,7 @@ class EventForm(ModelForm): This class is used for combining the Event Class model with a form (ModelForm). """ + class Meta: model = Event exclude = ('id', 'creator', 'date_created') @@ -23,5 +24,6 @@ class Meta: 'date_expire': DateTimeInput(attrs={'id': 'calendar'}), 'title': Textarea(attrs={'rows': 3}), 'description': Textarea(attrs={'rows': 3}), - 'link': TextInput(), + 'link': TextInput(), + 'tags': CheckboxSelectMultiple(), } diff --git a/ACM_General/events/models.py b/ACM_General/events/models.py index f99b1f7..b4de7a7 100644 --- a/ACM_General/events/models.py +++ b/ACM_General/events/models.py @@ -47,6 +47,46 @@ def get_path_for_flier(instance, filename): ) +class Tag(models.Model): + """ + Class used for adding classification tags on events. + """ + + #: The name of the tag that is displayed on the event creation form; represented + #: as a CharField. + display_name = models.CharField( + verbose_name=_('Event Tag'), + help_text=_('Different categories for the event.'), + max_length=256, + ) + + #: Unique id of the tag; represented as an IntegerField. + id = models.IntegerField( + verbose_name=_('ID'), + help_text=_('Unique ID of the tag.'), + primary_key=True, + ) + + #: If the tag should be diplayed on the event creation form; represented as a + #: BooleanField. + display = models.BooleanField( + verbose_name=_('Display'), + help_text=_('Whether or not the tag is to be displayed on the event creation form.'), + default = True, + ) + + + def __str__(self): + """ + Changes what is displayed for each tag on the event creation + form (without this it shows 'Tag object (id)'). + + :return: String containing the name of the tag. + :rtype: string. + """ + return self.display_name + + class Event(models.Model): """ Class used to define what is needed when creating new events. @@ -154,6 +194,13 @@ class Event(models.Model): blank=True, ) + #: Tags available to attach to events; represented as a ManyToManyField. + tags = models.ManyToManyField( + Tag, + verbose_name=_('Event Tags'), + help_text=_('Different categories for classifying the event.'), + ) + @property def is_active(self): """ diff --git a/ACM_General/events/views.py b/ACM_General/events/views.py index abf3767..1a7c1c0 100755 --- a/ACM_General/events/views.py +++ b/ACM_General/events/views.py @@ -48,8 +48,9 @@ def create_event(request): - If the user is submitting a GET request, it will send them to the blank create event page. - If the user is submitting a POST request: - - If the form is valid, it will save the event to the database and - redirect the user to the homepage. + - If the form is valid, it will save the event as well as the + ManyToMany relation to the database and redirect the user to the + homepage. - If the form is invalid, the user will be redirected back to the same create event page with same information that they filled out, but also with information that now explains the errors that @@ -74,6 +75,7 @@ def create_event(request): event = form.save(commit=False) event.creator = request.user event.save() + form.save_m2m() return HttpResponseRedirect("/") return render(request, 'events/create-event.html', {'form': form}) From 47be3386991bf3358df50331d83bb613842c1a23 Mon Sep 17 00:00:00 2001 From: David Gardiner Date: Fri, 30 Mar 2018 08:01:40 +0000 Subject: [PATCH 2/5] Remove bullets from M2M Field. --- ACM_General/events/static/events/css/create-event.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ACM_General/events/static/events/css/create-event.css b/ACM_General/events/static/events/css/create-event.css index fedd40d..d5513cc 100644 --- a/ACM_General/events/static/events/css/create-event.css +++ b/ACM_General/events/static/events/css/create-event.css @@ -17,6 +17,12 @@ a { color: white; } +ul { + list-style-type: none; + margin: 0; + padding: 0; +} + input, textarea, option { font-size: 15px; } From 48f09111d21a7974b9427e41e7fc84bc51e1c7aa Mon Sep 17 00:00:00 2001 From: David Gardiner Date: Thu, 5 Apr 2018 11:39:12 -0500 Subject: [PATCH 3/5] Add functionality to disabled a tag on event form. --- ACM_General/events/forms.py | 5 ++++- ACM_General/events/models.py | 34 +++++++++++++++------------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/ACM_General/events/forms.py b/ACM_General/events/forms.py index 80b0cef..103b349 100755 --- a/ACM_General/events/forms.py +++ b/ACM_General/events/forms.py @@ -24,6 +24,9 @@ class Meta: 'date_expire': DateTimeInput(attrs={'id': 'calendar'}), 'title': Textarea(attrs={'rows': 3}), 'description': Textarea(attrs={'rows': 3}), - 'link': TextInput(), + 'link': TextInput(), 'tags': CheckboxSelectMultiple(), } + def __init__(self, **kwargs): + super(EventForm, self).__init__(**kwargs) + self.fields['tags'].queryset = Tag.objects.filter(is_disabled=False) diff --git a/ACM_General/events/models.py b/ACM_General/events/models.py index b4de7a7..83c9bea 100644 --- a/ACM_General/events/models.py +++ b/ACM_General/events/models.py @@ -49,40 +49,36 @@ def get_path_for_flier(instance, filename): class Tag(models.Model): """ - Class used for adding classification tags on events. + Model used for adding tags on events. """ - #: The name of the tag that is displayed on the event creation form; represented - #: as a CharField. + #: The name of the tag that is displayed; represented as a CharField. display_name = models.CharField( verbose_name=_('Event Tag'), - help_text=_('Different categories for the event.'), + help_text=_('The name of the tag that will be displayed to users.'), + unique=True, max_length=256, ) - #: Unique id of the tag; represented as an IntegerField. - id = models.IntegerField( + #: Unique id of the tag; represented as an CharField. + id = models.CharField( verbose_name=_('ID'), help_text=_('Unique ID of the tag.'), primary_key=True, + max_length=256, ) - #: If the tag should be diplayed on the event creation form; represented as a - #: BooleanField. - display = models.BooleanField( + #: If the tag should be diplayed; represented as a BooleanField. + is_disabled = models.BooleanField( verbose_name=_('Display'), - help_text=_('Whether or not the tag is to be displayed on the event creation form.'), + help_text=_('Whether or not the tag is to be displayed.'), default = True, ) - def __str__(self): """ - Changes what is displayed for each tag on the event creation - form (without this it shows 'Tag object (id)'). - - :return: String containing the name of the tag. - :rtype: string. + :returns: Display the name of the tag. + :rtype: str """ return self.display_name @@ -198,7 +194,7 @@ class Event(models.Model): tags = models.ManyToManyField( Tag, verbose_name=_('Event Tags'), - help_text=_('Different categories for classifying the event.'), + help_text=_('The name of the Tag that will be displayed to users'), ) @property @@ -207,7 +203,7 @@ def is_active(self): Checking whether or not an event has already expired (gone past the current date). - :return: Bool value representing whether or not the event is + :returns: Bool value representing whether or not the event is considered 'active'. :rtype: bool """ @@ -218,7 +214,7 @@ def clean(self): The clean function is used for making checks on the data posted to the form. - :return: None. + :returns: None. :rtype: None :raises ValidationError: if date_expire or date_hosted are invalid. From a1f3ec4d090bfceb3821e5239d751d59e86ff07c Mon Sep 17 00:00:00 2001 From: David Gardiner Date: Thu, 5 Apr 2018 11:53:23 -0500 Subject: [PATCH 4/5] Fix test failures and add migration. --- .../migrations/0002_auto_20180330_0722.py | 26 +++++++++++++++++++ ACM_General/events/views.py | 4 +-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 ACM_General/events/migrations/0002_auto_20180330_0722.py diff --git a/ACM_General/events/migrations/0002_auto_20180330_0722.py b/ACM_General/events/migrations/0002_auto_20180330_0722.py new file mode 100644 index 0000000..dcbf1a7 --- /dev/null +++ b/ACM_General/events/migrations/0002_auto_20180330_0722.py @@ -0,0 +1,26 @@ +# Generated by Django 2.0.3 on 2018-03-30 07:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Tag', + fields=[ + ('display_name', models.CharField(help_text='The name of the tag that will be displayed to users.', max_length=256, unique=True, verbose_name='Event Tag')), + ('id', models.CharField(help_text='Unique ID of the tag.', max_length=256, primary_key=True, serialize=False, verbose_name='ID')), + ('is_disabled', models.BooleanField(default=True, help_text='Whether or not the tag is to be displayed.', verbose_name='Display')), + ], + ), + migrations.AddField( + model_name='event', + name='tags', + field=models.ManyToManyField(help_text='The name of the Tag that will be displayed to users', to='events.Tag', verbose_name='Event Tags'), + ), + ] diff --git a/ACM_General/events/views.py b/ACM_General/events/views.py index 1a7c1c0..093da07 100755 --- a/ACM_General/events/views.py +++ b/ACM_General/events/views.py @@ -48,8 +48,8 @@ def create_event(request): - If the user is submitting a GET request, it will send them to the blank create event page. - If the user is submitting a POST request: - - If the form is valid, it will save the event as well as the - ManyToMany relation to the database and redirect the user to the + - If the form is valid, it will save the event as well as the + ManyToMany relation to the database and redirect the user to the homepage. - If the form is invalid, the user will be redirected back to the same create event page with same information that they filled From cc64cfa13c84b052cc4b6ec204c623a59436a4ba Mon Sep 17 00:00:00 2001 From: David Gardiner Date: Sat, 7 Apr 2018 23:14:32 -0500 Subject: [PATCH 5/5] Fix issues with test cases for the events app. --- ACM_General/events/forms.py | 4 ++-- .../migrations/0003_auto_20180408_0410.py | 18 ++++++++++++++++++ ACM_General/events/models.py | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 ACM_General/events/migrations/0003_auto_20180408_0410.py diff --git a/ACM_General/events/forms.py b/ACM_General/events/forms.py index 103b349..6220e6e 100755 --- a/ACM_General/events/forms.py +++ b/ACM_General/events/forms.py @@ -27,6 +27,6 @@ class Meta: 'link': TextInput(), 'tags': CheckboxSelectMultiple(), } - def __init__(self, **kwargs): - super(EventForm, self).__init__(**kwargs) + def __init__(self, *args, **kwargs): + super(EventForm, self).__init__(*args, **kwargs) self.fields['tags'].queryset = Tag.objects.filter(is_disabled=False) diff --git a/ACM_General/events/migrations/0003_auto_20180408_0410.py b/ACM_General/events/migrations/0003_auto_20180408_0410.py new file mode 100644 index 0000000..39cdc67 --- /dev/null +++ b/ACM_General/events/migrations/0003_auto_20180408_0410.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.3 on 2018-04-08 04:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0002_auto_20180330_0722'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='tags', + field=models.ManyToManyField(blank=True, help_text='The name of the Tag that will be displayed to users', to='events.Tag', verbose_name='Event Tags'), + ), + ] diff --git a/ACM_General/events/models.py b/ACM_General/events/models.py index 83c9bea..c0d6f06 100644 --- a/ACM_General/events/models.py +++ b/ACM_General/events/models.py @@ -195,6 +195,7 @@ class Event(models.Model): Tag, verbose_name=_('Event Tags'), help_text=_('The name of the Tag that will be displayed to users'), + blank=True, ) @property