diff --git a/program/migrations/0001_initial.py b/program/migrations/0001_initial.py index bb417c913596886863f7217907637a4e9ac8b66b..c0677f087c45b6775455b79a9c30501c439ed31d 100644 --- a/program/migrations/0001_initial.py +++ b/program/migrations/0001_initial.py @@ -130,11 +130,11 @@ class Migration(migrations.Migration): ('automation_id', models.IntegerField(blank=True, null=True, verbose_name='Automation ID', choices=[])), ('created', models.DateTimeField(auto_now_add=True)), ('last_updated', models.DateTimeField(auto_now=True)), - ('broadcastformat', models.ForeignKey(related_name='shows', verbose_name='Broadcast format', to='program.BroadcastFormat')), + ('broadcastformat', models.ForeignKey(related_name='shows', on_delete=models.CASCADE, verbose_name='Broadcast format', to='program.BroadcastFormat')), ('hosts', models.ManyToManyField(related_name='shows', verbose_name='Hosts', to='program.Host', blank=True)), ('musicfocus', models.ManyToManyField(related_name='shows', verbose_name='Music focus', to='program.MusicFocus', blank=True)), ('owners', models.ManyToManyField(related_name='shows', verbose_name='Owners', to=settings.AUTH_USER_MODEL, blank=True)), - ('predecessor', models.ForeignKey(related_name='successors', verbose_name='Predecessor', blank=True, to='program.Show', null=True)), + ('predecessor', models.ForeignKey(related_name='successors', on_delete=models.CASCADE, verbose_name='Predecessor', blank=True, to='program.Show', null=True)), ], options={ 'ordering': ('slug',), @@ -182,8 +182,8 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('start', models.DateTimeField(unique=True, verbose_name='Start time')), ('end', models.DateTimeField(verbose_name='End time')), - ('programslot', models.ForeignKey(related_name='timeslots', verbose_name='Program slot', to='program.ProgramSlot')), - ('show', models.ForeignKey(related_name='timeslots', editable=False, to='program.Show')), + ('programslot', models.ForeignKey(related_name='timeslots', on_delete=models.CASCADE, verbose_name='Program slot', to='program.ProgramSlot')), + ('show', models.ForeignKey(related_name='timeslots', on_delete=models.CASCADE, editable=False, to='program.Show')), ], options={ 'ordering': ('start', 'end'), @@ -204,22 +204,22 @@ class Migration(migrations.Migration): migrations.AddField( model_name='programslot', name='rrule', - field=models.ForeignKey(related_name='programslots', verbose_name='Recurrence rule', to='program.RRule'), + field=models.ForeignKey(related_name='programslots', on_delete=models.CASCADE, verbose_name='Recurrence rule', to='program.RRule'), ), migrations.AddField( model_name='programslot', name='show', - field=models.ForeignKey(related_name='programslots', verbose_name='Show', to='program.Show'), + field=models.ForeignKey(related_name='programslots', on_delete=models.CASCADE, verbose_name='Show', to='program.Show'), ), migrations.AddField( model_name='note', name='show', - field=models.ForeignKey(related_name='notes', editable=False, to='program.Show'), + field=models.ForeignKey(related_name='notes', on_delete=models.CASCADE, editable=False, to='program.Show'), ), migrations.AddField( model_name='note', name='timeslot', - field=models.OneToOneField(verbose_name='Time slot', to='program.TimeSlot'), + field=models.OneToOneField(verbose_name='Time slot', on_delete=models.CASCADE, to='program.TimeSlot'), ), migrations.AlterUniqueTogether( name='programslot', diff --git a/program/models.py b/program/models.py index def5d0b694e3a89eb74fed90f4c5a0f6c5383706..c56e6708361ce74f0c4565fa3bb2aa5b45645c90 100644 --- a/program/models.py +++ b/program/models.py @@ -8,7 +8,6 @@ from django.db.models import Q from django.forms.models import model_to_dict from django.utils.translation import ugettext_lazy as _ from versatileimagefield.fields import VersatileImageField, PPOIField -from django.conf import settings from tinymce import models as tinymce_models @@ -263,7 +262,7 @@ class Host(models.Model): cba_url = models.URLField(_("CBA URL"), blank=True, help_text=_("URL to your CBA profile.")) ppoi = PPOIField('Image PPOI') height = models.PositiveIntegerField('Image Height', blank=True, null=True, editable=False) - width = models.PositiveIntegerField('Image Width', blank=True, null=True,editable=False) + width = models.PositiveIntegerField('Image Width', blank=True, null=True, editable=False) image = VersatileImageField(_("Profile picture"), blank=True, null=True, upload_to='host_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload a picture of yourself. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save.")) class Meta: @@ -301,20 +300,20 @@ class Host(models.Model): class Show(models.Model): - predecessor = models.ForeignKey('self', blank=True, null=True, related_name='successors', verbose_name=_("Predecessor")) + predecessor = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE, related_name='successors', verbose_name=_("Predecessor")) hosts = models.ManyToManyField(Host, blank=True, related_name='shows', verbose_name=_("Hosts")) owners = models.ManyToManyField(User, blank=True, related_name='shows', verbose_name=_("Owners")) language = models.ManyToManyField(Language, blank=True, related_name='language', verbose_name=_("Language")) - type = models.ForeignKey(Type, related_name='shows', verbose_name=_("Type")) + type = models.ForeignKey(Type, on_delete=models.CASCADE, related_name='shows', verbose_name=_("Type")) category = models.ManyToManyField(Category, blank=True, related_name='shows', verbose_name=_("Category")) - fundingcategory = models.ForeignKey(FundingCategory, null=True, blank=True, related_name='shows', verbose_name=_("Funding Category")) + fundingcategory = models.ForeignKey(FundingCategory, null=True, on_delete=models.CASCADE, blank=True, related_name='shows', verbose_name=_("Funding Category")) topic = models.ManyToManyField(Topic, blank=True, related_name='shows', verbose_name=_("Topic")) musicfocus = models.ManyToManyField(MusicFocus, blank=True, related_name='shows', verbose_name=_("Music focus")) name = models.CharField(_("Name"), max_length=255, help_text=_("The show's name. Avoid a subtitle.")) slug = models.CharField(_("Slug"), max_length=255, unique=True, help_text=_("A simple to read URL for your show")) ppoi = PPOIField('Image PPOI') height = models.PositiveIntegerField('Image Height', blank=True, null=True, editable=False) - width = models.PositiveIntegerField('Image Width', blank=True, null=True,editable=False) + width = models.PositiveIntegerField('Image Width', blank=True, null=True, editable=False) image = VersatileImageField(_("Image"), blank=True, null=True, upload_to='show_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload an image to your show. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save.")) logo = models.ImageField(_("Logo"), blank=True, null=True, upload_to='show_images', help_text=_("Upload a logo of your show.")) short_description = models.TextField(_("Short description"), help_text=_("Describe your show for your listeners in some sentences. Avoid technical data like airing times and contact information. They will be added automatically.")) @@ -333,7 +332,7 @@ class Show(models.Model): verbose_name_plural = _("Shows") def __str__(self): - if self.id == None: + if self.id is None: return '%s' % (self.name) return '%04d | %s' % (self.id, self.name) @@ -400,9 +399,9 @@ class Schedule(models.Model): (6, _("Sunday")), ) - rrule = models.ForeignKey(RRule, related_name='schedules', verbose_name=_("Recurrence rule")) + rrule = models.ForeignKey(RRule, on_delete=models.CASCADE, related_name='schedules', verbose_name=_("Recurrence rule")) byweekday = models.IntegerField(_("Weekday"), choices=BYWEEKDAY_CHOICES) - show = models.ForeignKey(Show, related_name='schedules', verbose_name=_("Show")) + show = models.ForeignKey(Show, on_delete=models.CASCADE, related_name='schedules', verbose_name=_("Show")) dstart = models.DateField(_("First date")) tstart = models.TimeField(_("Start time")) tend = models.TimeField(_("End time")) @@ -411,8 +410,8 @@ class Schedule(models.Model): add_days_no = models.IntegerField(_("Add days"), blank=True, null=True) add_business_days_only = models.BooleanField(_("Only add business days?"), default=False) fallback_id = models.IntegerField(_("Fallback ID"), blank=True, null=True) - automation_id = models.IntegerField(_("Automation ID"), blank=True, null=True, choices=get_automation_id_choices()) # Deprecated - created = models.DateTimeField(auto_now_add=True, editable=False, null=True) #-> both see https://stackoverflow.com/questions/1737017/django-auto-now-and-auto-now-add + automation_id = models.IntegerField(_("Automation ID"), blank=True, null=True, choices=get_automation_id_choices()) # Deprecated + created = models.DateTimeField(auto_now_add=True, editable=False, null=True) # -> both see https://stackoverflow.com/questions/1737017/django-auto-now-and-auto-now-add last_updated = models.DateTimeField(auto_now=True, editable=False, null=True) class Meta: @@ -436,7 +435,7 @@ class Schedule(models.Model): def instantiate_upcoming(sdl, show_pk, pk=None): """Returns an upcoming schedule instance for conflict resolution""" - pk = int(pk) if pk != None else None + pk = int(pk) if pk is not None else None rrule = RRule.objects.get(pk=int(sdl['rrule'])) show = Show.objects.get(pk=int(show_pk)) @@ -449,7 +448,7 @@ class Schedule(models.Model): dstart = datetime.strptime(str(sdl['dstart']), '%Y-%m-%d').date() # Schedule mustn't start in the past when adding - if pk == None and dstart < datetime.today().date(): + if pk is None and dstart < datetime.today().date(): dstart = datetime.today().date() tstart = sdl['tstart'] + ':00' if len(str(sdl['tstart'])) == 5 else sdl['tstart'] @@ -478,7 +477,6 @@ class Schedule(models.Model): return schedule - def generate_timeslots(schedule): """ Returns a list of timeslot objects based on a schedule and its rrule @@ -505,20 +503,20 @@ class Schedule(models.Model): else: dend = schedule.dstart - if schedule.rrule.freq == 0: # Ignore weekdays for one-time timeslots + if schedule.rrule.freq == 0: # Ignore weekdays for one-time timeslots byweekday_start = None byweekday_end = None - elif schedule.rrule.freq == 3 and schedule.rrule.pk == 2: # Daily timeslots + elif schedule.rrule.freq == 3 and schedule.rrule.pk == 2: # Daily timeslots byweekday_start = (0, 1, 2, 3, 4, 5, 6) byweekday_end = (0, 1, 2, 3, 4, 5, 6) - elif schedule.rrule.freq == 3 and schedule.rrule.pk == 3: # Business days MO - FR/SA + elif schedule.rrule.freq == 3 and schedule.rrule.pk == 3: # Business days MO - FR/SA byweekday_start = (0, 1, 2, 3, 4) if schedule.tend < schedule.tstart: # End days for over midnight byweekday_end = (1, 2, 3, 4, 5) else: byweekday_end = (0, 1, 2, 3, 4) - elif schedule.rrule.freq == 2 and schedule.rrule.pk == 7: # Even calendar weeks + elif schedule.rrule.freq == 2 and schedule.rrule.pk == 7: # Even calendar weeks byweekday_start = int(schedule.byweekday) byweekno = list(range(2, 54, 2)) # Reverse ending weeks if from Sun - Mon @@ -526,7 +524,7 @@ class Schedule(models.Model): byweekno_end = list(range(1, 54, 2)) else: byweekno_end = byweekno - elif schedule.rrule.freq == 2 and schedule.rrule.pk == 8: # Odd calendar weeks + elif schedule.rrule.freq == 2 and schedule.rrule.pk == 8: # Odd calendar weeks byweekday_start = int(schedule.byweekday) byweekno = list(range(1, 54, 2)) # Reverse ending weeks if from Sun - Mon @@ -543,20 +541,20 @@ class Schedule(models.Model): else: starts = list(rrule(freq=schedule.rrule.freq, - dtstart=datetime.combine(schedule.dstart, schedule.tstart), - interval=schedule.rrule.interval, - until=schedule.until + relativedelta(days=+1), - bysetpos=schedule.rrule.bysetpos, - byweekday=byweekday_start, - byweekno=byweekno)) + dtstart=datetime.combine(schedule.dstart, schedule.tstart), + interval=schedule.rrule.interval, + until=schedule.until + relativedelta(days=+1), + bysetpos=schedule.rrule.bysetpos, + byweekday=byweekday_start, + byweekno=byweekno)) ends = list(rrule(freq=schedule.rrule.freq, - dtstart=datetime.combine(dend, schedule.tend), - interval=schedule.rrule.interval, - until=schedule.until + relativedelta(days=+1), - bysetpos=schedule.rrule.bysetpos, - byweekday=byweekday_end, - byweekno=byweekno_end)) + dtstart=datetime.combine(dend, schedule.tend), + interval=schedule.rrule.interval, + until=schedule.until + relativedelta(days=+1), + bysetpos=schedule.rrule.bysetpos, + byweekday=byweekday_end, + byweekno=byweekno_end)) for k in range(min(len(starts), len(ends))): @@ -567,7 +565,6 @@ class Schedule(models.Model): if starts[k] > ends[k]: ends[k] = datetime.combine(starts[k] + relativedelta(days=+1), schedule.tend) - ''' Add a number of days to the generated dates? @@ -591,7 +588,7 @@ class Schedule(models.Model): "On the following business day" (add_days_no=1,add_business_days_only=True) ''' - if schedule.add_days_no != None and schedule.add_days_no > 0: + if schedule.add_days_no is not None and schedule.add_days_no > 0: # If only business days and weekday is Fri, Sat or Sun: add add_days_no beginning from Sunday weekday = datetime.date(starts[k]).weekday() if schedule.add_business_days_only and weekday > 3: @@ -609,7 +606,6 @@ class Schedule(models.Model): return timeslots - def get_collisions(timeslots): """ Tests a list of timeslot objects for colliding timeslots in the database @@ -622,20 +618,19 @@ class Schedule(models.Model): for ts in timeslots: collision = TimeSlot.objects.filter( - ( Q(start__lt=ts.end) & Q(end__gte=ts.end) ) | - ( Q(end__gt=ts.start) & Q(end__lte=ts.end) ) | - ( Q(start__gte=ts.start) & Q(end__lte=ts.end) ) | - ( Q(start__lte=ts.start) & Q(end__gte=ts.end) ) + (Q(start__lt=ts.end) & Q(end__gte=ts.end)) | + (Q(end__gt=ts.start) & Q(end__lte=ts.end)) | + (Q(start__gte=ts.start) & Q(end__lte=ts.end)) | + (Q(start__lte=ts.start) & Q(end__gte=ts.end)) ) if collision: - collisions.append(collision[0]) # TODO: Do we really always retrieve one? + collisions.append(collision[0]) # TODO: Do we really always retrieve one? else: collisions.append(None) return collisions - def generate_conflicts(timeslots): """ Tests a list of timeslot objects for colliding timeslots in the database @@ -660,10 +655,10 @@ class Schedule(models.Model): # Get collisions for each timeslot collision_list = list(TimeSlot.objects.filter( - ( Q(start__lt=ts.end) & Q(end__gte=ts.end) ) | - ( Q(end__gt=ts.start) & Q(end__lte=ts.end) ) | - ( Q(start__gte=ts.start) & Q(end__lte=ts.end) ) | - ( Q(start__lte=ts.start) & Q(end__gte=ts.end) ) + (Q(start__lt=ts.end) & Q(end__gte=ts.end)) | + (Q(end__gt=ts.start) & Q(end__lte=ts.end)) | + (Q(start__gte=ts.start) & Q(end__lte=ts.end)) | + (Q(start__lte=ts.start) & Q(end__gte=ts.end)) ).order_by('start')) # Add the projected timeslot @@ -695,7 +690,6 @@ class Schedule(models.Model): collisions.append(collision) - '''Determine acceptable solutions''' if len(collision_list) > 1: @@ -777,7 +771,6 @@ class Schedule(models.Model): return conflicts - def make_conflicts(sdl, schedule_pk, show_pk): """ Retrieves POST vars @@ -795,7 +788,7 @@ class Schedule(models.Model): # Generate timeslots # If extending: Get last timeslot and start generating from that date on - if schedule_pk != None: + if schedule_pk is not None: existing_schedule = Schedule.objects.get(pk=int(schedule_pk)) if schedule.until > existing_schedule.until: @@ -810,7 +803,6 @@ class Schedule(models.Model): return conflicts - def resolve_conflicts(data, schedule_pk, show_pk): """ Resolves conflicts @@ -861,7 +853,7 @@ class Schedule(models.Model): # # - Create the projected timeslot and skip # - if not 'solution_choices' in ts or len(ts['collisions']) < 1: + if 'solution_choices' not in ts or len(ts['collisions']) < 1: projected_ts = TimeSlot.objects.instantiate(ts['start'], ts['end'], schedule, show) create.append(projected_ts) continue @@ -887,7 +879,6 @@ class Schedule(models.Model): errors[ts['hash']] = _("Given solution is not accepted for this conflict.") continue - '''Conflict resolution''' existing = ts['collisions'][0] @@ -901,7 +892,6 @@ class Schedule(models.Model): if solution == 'theirs': continue - # ours # # - Create the projected timeslot @@ -919,7 +909,6 @@ class Schedule(models.Model): except ObjectDoesNotExist: pass - # theirs-end # # - Keep the existing timeslot @@ -929,7 +918,6 @@ class Schedule(models.Model): projected_ts = TimeSlot.objects.instantiate(ts['start'], existing['start'], schedule, show) create.append(projected_ts) - # ours-end # # - Create the projected timeslot @@ -943,7 +931,6 @@ class Schedule(models.Model): existing_ts.start = datetime.strptime(ts['end'], '%Y-%m-%d %H:%M:%S') update.append(existing_ts) - # theirs-start # # - Keep existing @@ -953,7 +940,6 @@ class Schedule(models.Model): projected_ts = TimeSlot.objects.instantiate(existing['end'], ts['end'], schedule, show) create.append(projected_ts) - # ours-start # # - Create the projected timeslot @@ -967,7 +953,6 @@ class Schedule(models.Model): existing_ts.end = datetime.strptime(ts['start'], '%Y-%m-%d %H:%M:%S') update.append(existing_ts) - # theirs-both # # - Keep existing @@ -980,7 +965,6 @@ class Schedule(models.Model): projected_ts = TimeSlot.objects.instantiate(existing['end'], ts['end'], schedule, show) create.append(projected_ts) - # ours-both # # - Create projected @@ -999,7 +983,6 @@ class Schedule(models.Model): projected_ts = TimeSlot.objects.instantiate(ts['end'], existing['end'], schedule, show) create.append(projected_ts) - # If there were any errors, don't make any db changes yet # but add error messages and return already chosen solutions if len(errors) > 0: @@ -1029,13 +1012,11 @@ class Schedule(models.Model): return conflicts - # Collect upcoming timeslots to delete which might still remain del_timeslots = TimeSlot.objects.filter(schedule=schedule, start__gt=schedule.until) for del_ts in del_timeslots: delete.append(del_ts) - # If 'dryrun' is true, just return the projected changes instead of executing them if 'dryrun' in sdl and sdl['dryrun']: output = {} @@ -1044,7 +1025,6 @@ class Schedule(models.Model): output['delete'] = [model_to_dict(ts) for ts in delete] return output - '''Database changes if no errors found''' # Only save schedule if timeslots were created @@ -1085,12 +1065,10 @@ class Schedule(models.Model): return model_to_dict(schedule) - def save(self, *args, **kwargs): super(Schedule, self).save(*args, **kwargs) - class TimeSlotManager(models.Manager): @staticmethod def instantiate(start, end, schedule, show): @@ -1116,12 +1094,12 @@ class TimeSlotManager(models.Manager): until, tend = next_timeslot.start.date(), next_timeslot.start.time() new_schedule = Schedule(rrule=once, - byweekday=today, - show=default, - dstart=dstart, - tstart=tstart, - tend=tend, - until=until) + byweekday=today, + show=default, + dstart=dstart, + tstart=tstart, + tend=tend, + until=until) try: new_schedule.validate_unique() @@ -1146,7 +1124,6 @@ class TimeSlotManager(models.Manager): return TimeSlot.objects.filter(Q(start__lte=start, end__gte=start) | Q(start__gt=start, start__lt=end)).exclude(end=start) - @staticmethod def get_7d_timeslots(start): start = datetime.combine(start, time(0, 0)) @@ -1155,7 +1132,6 @@ class TimeSlotManager(models.Manager): return TimeSlot.objects.filter(Q(start__lte=start, end__gte=start) | Q(start__gt=start, start__lt=end)).exclude(end=start) - @staticmethod def get_timerange_timeslots(start, end): return TimeSlot.objects.filter(Q(start__lte=start, end__gte=start) | @@ -1163,10 +1139,10 @@ class TimeSlotManager(models.Manager): class TimeSlot(models.Model): - schedule = models.ForeignKey(Schedule, related_name='timeslots', verbose_name=_("Schedule")) - start = models.DateTimeField(_("Start time")) # Removed 'unique=True' because new Timeslots need to be created before deleting the old ones (otherwise linked notes get deleted first) + schedule = models.ForeignKey(Schedule, on_delete=models.CASCADE, related_name='timeslots', verbose_name=_("Schedule")) + start = models.DateTimeField(_("Start time")) # Removed 'unique=True' because new Timeslots need to be created before deleting the old ones (otherwise linked notes get deleted first) end = models.DateTimeField(_("End time")) - show = models.ForeignKey(Show, editable=False, related_name='timeslots') + show = models.ForeignKey(Show, editable=False, on_delete=models.CASCADE, related_name='timeslots') memo = models.TextField(_("Memo"), blank=True) is_repetition = models.BooleanField(_("Is repetition?"), default=False) playlist_id = models.IntegerField(_("Playlist ID"), null=True) @@ -1182,7 +1158,7 @@ class TimeSlot(models.Model): def __str__(self): start = self.start.strftime('%a, %d.%m.%Y %H:%M') end = self.end.strftime('%H:%M') - is_repetition = ' ' + _('(REP)') if self.schedule.is_repetition is 1 else '' + is_repetition = ' ' + _('(REP)') if self.schedule.is_repetition == 1 else '' return '%s - %s %s (%s)' % (start, end, is_repetition, self.show.name) @@ -1212,24 +1188,24 @@ class Note(models.Model): (1, _("Recommendation")), (2, _("Repetition")), ) - timeslot = models.OneToOneField(TimeSlot, verbose_name=_("Time slot"), unique=True) + timeslot = models.OneToOneField(TimeSlot, on_delete=models.CASCADE, verbose_name=_("Time slot"), unique=True) title = models.CharField(_("Title"), max_length=128, help_text=_("Give your note a good headline. What will your upcoming show be about? Try to arouse interest to listen to it!<br>Avoid technical data like the show's name, its airing times or its episode number. These data are added automatically.")) slug = models.SlugField(_("Slug"), max_length=32, unique=True, help_text=_("A simple to read URL for your show.")) summary = models.TextField(_("Summary"), blank=True, help_text=_("Describe your upcoming show in some sentences. Avoid technical data like airing times and contact information. They will be added automatically.")) content = tinymce_models.HTMLField(_("Content"), help_text=_("Describe your upcoming show in detail.")) ppoi = PPOIField('Image PPOI') height = models.PositiveIntegerField('Image Height', blank=True, null=True, editable=False) - width = models.PositiveIntegerField('Image Width', blank=True, null=True,editable=False) + width = models.PositiveIntegerField('Image Width', blank=True, null=True, editable=False) image = VersatileImageField(_("Featured image"), blank=True, null=True, upload_to='note_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload an image to your note. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save.")) status = models.IntegerField(_("Status"), choices=STATUS_CHOICES, default=1) start = models.DateTimeField(editable=False) - show = models.ForeignKey(Show, related_name='notes', editable=True) + show = models.ForeignKey(Show, on_delete=models.CASCADE, related_name='notes', editable=True) cba_id = models.IntegerField(_("CBA ID"), blank=True, null=True, help_text=_("Link the note to a certain CBA post by giving its ID. (E.g. if your post's CBA URL is https://cba.fro.at/1234, then your CBA ID is 1234)")) audio_url = models.TextField(_("Direct URL to a linked audio file"), blank=True, editable=False) created = models.DateTimeField(auto_now_add=True, editable=False) last_updated = models.DateTimeField(auto_now=True, editable=False) - user = models.ForeignKey(User, editable=False, related_name='users', default=1) - host = models.ForeignKey(Host, related_name='hosts', null=True) + user = models.ForeignKey(User, editable=False, on_delete=models.CASCADE, related_name='users', default=1) + host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='hosts', null=True) class Meta: ordering = ('timeslot',) @@ -1266,7 +1242,7 @@ class Note(models.Model): audio_url = '' - if cba_id != None and cba_id != '' and CBA_API_KEY != '': + if cba_id is not None and cba_id != '' and CBA_API_KEY != '': from urllib.request import urlopen import json @@ -1281,7 +1257,6 @@ class Note(models.Model): return audio_url - def save(self, *args, **kwargs): self.start = self.timeslot.start self.show = self.timeslot.schedule.show @@ -1295,4 +1270,4 @@ class Note(models.Model): # Generate thumbnails if self.image.name and THUMBNAIL_SIZES: for size in THUMBNAIL_SIZES: - thumbnail = self.image.crop[size].name \ No newline at end of file + thumbnail = self.image.crop[size].name