From e3bab2389cacf5fb4fb7fa5b3160d0ea328549ac Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 26 Oct 2021 11:37:32 +0200 Subject: [PATCH] Replace old models by new models to update DB structure --- media/admin.py | 24 ++- media/migrations/0042_auto_20211023_1929.py | 70 ++++++ media/migrations/0043_auto_20211023_2012.py | 166 ++++++++++++++ media/models.py | 226 +++++++++++++++++++- 4 files changed, 467 insertions(+), 19 deletions(-) create mode 100644 media/migrations/0042_auto_20211023_1929.py create mode 100644 media/migrations/0043_auto_20211023_2012.py diff --git a/media/admin.py b/media/admin.py index 4ee4522..9801eb0 100644 --- a/media/admin.py +++ b/media/admin.py @@ -5,12 +5,14 @@ from django.urls import reverse from django.utils.html import format_html from django.utils.translation import ugettext_lazy as _ +from polymorphic.admin import PolymorphicChildModelAdmin, \ + PolymorphicParentModelAdmin from med.admin import admin_site from reversion.admin import VersionAdmin from .forms import MediaAdminForm -from .models import Author, CD, Comic, Emprunt, FutureMedium, Game, Manga,\ - Novel, Review, Vinyl +from .models import Author, Borrowable, CD, Comic, Emprunt, FutureMedium, \ + Game, Manga, Novel, Review, Vinyl class AuthorAdmin(VersionAdmin): @@ -18,7 +20,12 @@ class AuthorAdmin(VersionAdmin): search_fields = ('name',) -class MediumAdmin(VersionAdmin): +class BorrowableAdmin(PolymorphicParentModelAdmin): + search_fields = ('title',) + child_models = (CD, Comic, Manga, Novel, Review, Vinyl,) + + +class MediumAdmin(VersionAdmin, PolymorphicChildModelAdmin): list_display = ('__str__', 'authors_list', 'side_identifier', 'isbn', 'external_link') search_fields = ('title', 'authors__name', 'side_identifier', 'subtitle', @@ -77,7 +84,7 @@ class FutureMediumAdmin(VersionAdmin): extra_context=extra_context) -class CDAdmin(VersionAdmin): +class CDAdmin(VersionAdmin, PolymorphicChildModelAdmin): list_display = ('title', 'authors_list', 'side_identifier',) search_fields = ('title', 'authors__name', 'side_identifier',) autocomplete_fields = ('authors',) @@ -88,7 +95,7 @@ class CDAdmin(VersionAdmin): authors_list.short_description = _('authors') -class VinylAdmin(VersionAdmin): +class VinylAdmin(VersionAdmin, PolymorphicChildModelAdmin): list_display = ('title', 'authors_list', 'side_identifier', 'rpm',) search_fields = ('title', 'authors__name', 'side_identifier', 'rpm',) autocomplete_fields = ('authors',) @@ -99,7 +106,7 @@ class VinylAdmin(VersionAdmin): authors_list.short_description = _('authors') -class ReviewAdmin(VersionAdmin): +class ReviewAdmin(VersionAdmin, PolymorphicChildModelAdmin): list_display = ('__str__', 'number', 'year', 'month', 'day', 'double',) search_fields = ('title', 'number', 'year',) @@ -140,14 +147,15 @@ class EmpruntAdmin(VersionAdmin): return super().add_view(request, form_url, extra_context) -class GameAdmin(VersionAdmin): - list_display = ('name', 'owner', 'duration', 'players_min', +class GameAdmin(VersionAdmin, PolymorphicChildModelAdmin): + list_display = ('title', 'owner', 'duration', 'players_min', 'players_max', 'comment') search_fields = ('name', 'owner__username', 'duration', 'comment') autocomplete_fields = ('owner',) admin_site.register(Author, AuthorAdmin) +admin_site.register(Borrowable, BorrowableAdmin) admin_site.register(Comic, MediumAdmin) admin_site.register(Manga, MediumAdmin) admin_site.register(Novel, MediumAdmin) diff --git a/media/migrations/0042_auto_20211023_1929.py b/media/migrations/0042_auto_20211023_1929.py new file mode 100644 index 0000000..7370f46 --- /dev/null +++ b/media/migrations/0042_auto_20211023_1929.py @@ -0,0 +1,70 @@ +# Generated by Django 2.2.17 on 2021-10-23 17:29 +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('media', '0041_auto_20211023_1838'), + ] + + operations = [ + migrations.RenameModel( + old_name='CD', + new_name='OldCD', + ), + migrations.RenameModel( + old_name='Manga', + new_name='OldComic', + ), + # Remove index before renaming the model + migrations.AlterField( + model_name='game', + name='owner', + field=models.ForeignKey(db_index=False, on_delete=models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='owner'), + ), + migrations.RenameModel( + old_name='Game', + new_name='OldGame', + ), + migrations.AlterField( + model_name='oldgame', + name='owner', + field=models.ForeignKey(db_index=True, on_delete=models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='owner'), + ), + migrations.RenameField( + model_name='oldgame', + old_name='name', + new_name='title', + ), + migrations.RenameModel( + old_name='Novel', + new_name='OldManga', + ), + migrations.RenameModel( + old_name='Comic', + new_name='OldNovel', + ), + migrations.RenameModel( + old_name='Review', + new_name='OldReview', + ), + migrations.RenameModel( + old_name='Vinyl', + new_name='OldVinyl', + ), + migrations.AlterModelOptions( + name='oldcomic', + options={'ordering': ['title', 'subtitle'], 'verbose_name': 'comic', 'verbose_name_plural': 'comics'}, + ), + migrations.AlterModelOptions( + name='oldmanga', + options={'ordering': ['title'], 'verbose_name': 'manga', 'verbose_name_plural': 'mangas'}, + ), + migrations.AlterModelOptions( + name='oldnovel', + options={'ordering': ['title', 'subtitle'], 'verbose_name': 'novel', 'verbose_name_plural': 'novels'}, + ), + ] diff --git a/media/migrations/0043_auto_20211023_2012.py b/media/migrations/0043_auto_20211023_2012.py new file mode 100644 index 0000000..a1bc2f1 --- /dev/null +++ b/media/migrations/0043_auto_20211023_2012.py @@ -0,0 +1,166 @@ +# Generated by Django 2.2.17 on 2021-10-23 18:12 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import media.fields +import media.validators + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('media', '0042_auto_20211023_1929'), + ] + + operations = [ + migrations.CreateModel( + name='Borrowable', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255, verbose_name='title')), + ('present', models.BooleanField(default=False, help_text='Tell that the medium is present in the Mediatek.', verbose_name='present')), + ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_media.borrowable_set+', to='contenttypes.ContentType')), + ], + options={ + 'verbose_name': 'borrowable', + 'verbose_name_plural': 'borrowables', + }, + ), + migrations.AlterModelOptions( + name='oldgame', + options={'ordering': ['title'], 'verbose_name': 'game', 'verbose_name_plural': 'games'}, + ), + migrations.AlterField( + model_name='emprunt', + name='media', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='media.Borrowable'), + ), + migrations.CreateModel( + name='Medium', + fields=[ + ('borrowable_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='media.Borrowable')), + ('external_url', models.URLField(blank=True, verbose_name='external URL')), + ('side_identifier', models.CharField(max_length=255, verbose_name='side identifier')), + ('authors', models.ManyToManyField(to='media.Author', verbose_name='authors')), + ], + options={ + 'verbose_name': 'medium', + 'verbose_name_plural': 'media', + }, + bases=('media.borrowable',), + ), + migrations.CreateModel( + name='Review', + fields=[ + ('borrowable_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='media.Borrowable')), + ('number', models.PositiveIntegerField(verbose_name='number')), + ('year', models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='year')), + ('month', models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='month')), + ('day', models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='day')), + ('double', models.BooleanField(default=False, verbose_name='double')), + ], + options={ + 'verbose_name': 'review', + 'verbose_name_plural': 'reviews', + 'ordering': ['title', 'number'], + }, + bases=('media.borrowable',), + ), + migrations.CreateModel( + name='Book', + fields=[ + ('medium_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='media.Medium')), + ('isbn', media.fields.ISBNField(blank=True, help_text='You may be able to scan it from a bar code.', max_length=28, null=True, unique=True, validators=[media.validators.isbn_validator], verbose_name='ISBN')), + ('subtitle', models.CharField(blank=True, max_length=255, verbose_name='subtitle')), + ('number_of_pages', models.PositiveIntegerField(blank=True, null=True, verbose_name='number of pages')), + ('publish_date', models.DateField(blank=True, null=True, verbose_name='publish date')), + ], + options={ + 'verbose_name': 'book', + 'verbose_name_plural': 'books', + }, + bases=('media.medium',), + ), + migrations.CreateModel( + name='CD', + fields=[ + ('medium_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='media.Medium')), + ], + options={ + 'verbose_name': 'CD', + 'verbose_name_plural': 'CDs', + 'ordering': ['title'], + }, + bases=('media.medium',), + ), + migrations.CreateModel( + name='Vinyl', + fields=[ + ('medium_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='media.Medium')), + ('rpm', models.PositiveIntegerField(choices=[(33, '33 RPM'), (45, '45 RPM')], verbose_name='rounds per minute')), + ], + options={ + 'verbose_name': 'vinyl', + 'verbose_name_plural': 'vinyls', + 'ordering': ['title'], + }, + bases=('media.medium',), + ), + migrations.CreateModel( + name='Game', + fields=[ + ('borrowable_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='media.Borrowable')), + ('duration', models.CharField(choices=[('-1h', '-1h'), ('1-2h', '1-2h'), ('2-3h', '2-3h'), ('3-4h', '3-4h'), ('4h+', '4h+')], max_length=255, verbose_name='duration')), + ('players_min', models.IntegerField(validators=[django.core.validators.MinValueValidator(1)], verbose_name='minimum number of players')), + ('players_max', models.IntegerField(validators=[django.core.validators.MinValueValidator(1)], verbose_name='maximum number of players')), + ('comment', models.CharField(blank=True, max_length=255, verbose_name='comment')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='owner')), + ], + options={ + 'verbose_name': 'game', + 'verbose_name_plural': 'games', + 'ordering': ['title'], + }, + bases=('media.borrowable',), + ), + migrations.CreateModel( + name='Comic', + fields=[ + ('book_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='media.Book')), + ], + options={ + 'verbose_name': 'comic', + 'verbose_name_plural': 'comics', + 'ordering': ['title', 'subtitle'], + }, + bases=('media.book',), + ), + migrations.CreateModel( + name='Manga', + fields=[ + ('book_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='media.Book')), + ], + options={ + 'verbose_name': 'manga', + 'verbose_name_plural': 'mangas', + 'ordering': ['title', 'subtitle'], + }, + bases=('media.book',), + ), + migrations.CreateModel( + name='Novel', + fields=[ + ('book_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='media.Book')), + ], + options={ + 'verbose_name': 'novel', + 'verbose_name_plural': 'novels', + 'ordering': ['title', 'subtitle'], + }, + bases=('media.book',), + ), + ] diff --git a/media/models.py b/media/models.py index a797cff..669ee7a 100644 --- a/media/models.py +++ b/media/models.py @@ -5,6 +5,7 @@ from django.core.validators import MinValueValidator from django.db import models from django.utils.translation import gettext_lazy as _ +from polymorphic.models import PolymorphicModel from .fields import ISBNField @@ -30,7 +31,86 @@ class Author(models.Model): ordering = ['name'] -class Comic(models.Model): +class Borrowable(PolymorphicModel): + title = models.CharField( + max_length=255, + verbose_name=_("title"), + ) + + present = models.BooleanField( + verbose_name=_("present"), + help_text=_("Tell that the medium is present in the Mediatek."), + default=False, + ) + + def __str__(self): + return self.title + + class Meta: + verbose_name = _('borrowable') + verbose_name_plural = _('borrowables') + + +class Medium(Borrowable): + external_url = models.URLField( + verbose_name=_('external URL'), + blank=True, + ) + + side_identifier = models.CharField( + verbose_name=_('side identifier'), + max_length=255, + ) + + authors = models.ManyToManyField( + 'Author', + verbose_name=_('authors'), + ) + + class Meta: + verbose_name = _("medium") + verbose_name_plural = _("media") + + +class Book(Medium): + isbn = ISBNField( + _('ISBN'), + help_text=_('You may be able to scan it from a bar code.'), + unique=True, + blank=True, + null=True, + ) + + subtitle = models.CharField( + verbose_name=_('subtitle'), + max_length=255, + blank=True, + ) + + number_of_pages = models.PositiveIntegerField( + verbose_name=_('number of pages'), + blank=True, + null=True, + ) + + publish_date = models.DateField( + verbose_name=_('publish date'), + blank=True, + null=True, + ) + + def __str__(self): + if self.subtitle: + return "{} : {}".format(self.title, self.subtitle) + else: + return self.title + + class Meta: + verbose_name = _("book") + verbose_name_plural = _("books") + + +class OldComic(models.Model): isbn = ISBNField( _('ISBN'), help_text=_('You may be able to scan it from a bar code.'), @@ -95,7 +175,14 @@ class Comic(models.Model): ordering = ['title', 'subtitle'] -class Manga(models.Model): +class Comic(Book): + class Meta: + verbose_name = _("comic") + verbose_name_plural = _("comics") + ordering = ['title', 'subtitle'] + + +class OldManga(models.Model): isbn = ISBNField( _('ISBN'), help_text=_('You may be able to scan it from a bar code.'), @@ -157,7 +244,14 @@ class Manga(models.Model): ordering = ['title'] -class Novel(models.Model): +class Manga(Book): + class Meta: + verbose_name = _("manga") + verbose_name_plural = _("mangas") + ordering = ['title', 'subtitle'] + + +class OldNovel(models.Model): isbn = ISBNField( _('ISBN'), help_text=_('You may be able to scan it from a bar code.'), @@ -219,7 +313,14 @@ class Novel(models.Model): ordering = ['title', 'subtitle'] -class Vinyl(models.Model): +class Novel(Book): + class Meta: + verbose_name = _("novel") + verbose_name_plural = _("novels") + ordering = ['title', 'subtitle'] + + +class OldVinyl(models.Model): title = models.CharField( verbose_name=_('title'), max_length=255, @@ -258,7 +359,22 @@ class Vinyl(models.Model): ordering = ['title'] -class CD(models.Model): +class Vinyl(Medium): + rpm = models.PositiveIntegerField( + verbose_name=_('rounds per minute'), + choices=[ + (33, _('33 RPM')), + (45, _('45 RPM')), + ], + ) + + class Meta: + verbose_name = _("vinyl") + verbose_name_plural = _("vinyls") + ordering = ['title'] + + +class OldCD(models.Model): title = models.CharField( verbose_name=_('title'), max_length=255, @@ -289,7 +405,14 @@ class CD(models.Model): ordering = ['title'] -class Review(models.Model): +class CD(Medium): + class Meta: + verbose_name = _("CD") + verbose_name_plural = _("CDs") + ordering = ['title'] + + +class OldReview(models.Model): title = models.CharField( verbose_name=_('title'), max_length=255, @@ -340,6 +463,46 @@ class Review(models.Model): ordering = ['title', 'number'] +class Review(Borrowable): + number = models.PositiveIntegerField( + verbose_name=_('number'), + ) + + year = models.PositiveIntegerField( + verbose_name=_('year'), + null=True, + blank=True, + default=None, + ) + + month = models.PositiveIntegerField( + verbose_name=_('month'), + null=True, + blank=True, + default=None, + ) + + day = models.PositiveIntegerField( + verbose_name=_('day'), + null=True, + blank=True, + default=None, + ) + + double = models.BooleanField( + verbose_name=_('double'), + default=False, + ) + + def __str__(self): + return self.title + " n°" + str(self.number) + + class Meta: + verbose_name = _("review") + verbose_name_plural = _("reviews") + ordering = ['title', 'number'] + + class FutureMedium(models.Model): isbn = ISBNField( _('ISBN'), @@ -375,7 +538,7 @@ class FutureMedium(models.Model): class Emprunt(models.Model): media = models.ForeignKey( - 'Comic', + 'media.Borrowable', on_delete=models.PROTECT, ) user = models.ForeignKey( @@ -417,7 +580,7 @@ class Emprunt(models.Model): ordering = ['-date_emprunt'] -class Game(models.Model): +class OldGame(models.Model): DURATIONS = ( ('-1h', '-1h'), ('1-2h', '1-2h'), @@ -426,7 +589,7 @@ class Game(models.Model): ('4h+', '4h+'), ) - name = models.CharField( + title = models.CharField( max_length=255, verbose_name=_("name"), ) @@ -455,9 +618,50 @@ class Game(models.Model): ) def __str__(self): - return str(self.name) + return str(self.title) class Meta: verbose_name = _("game") verbose_name_plural = _("games") - ordering = ['name'] + ordering = ['title'] + + +class Game(Borrowable): + DURATIONS = ( + ('-1h', '-1h'), + ('1-2h', '1-2h'), + ('2-3h', '2-3h'), + ('3-4h', '3-4h'), + ('4h+', '4h+'), + ) + owner = models.ForeignKey( + 'users.User', + on_delete=models.PROTECT, + verbose_name=_("owner"), + ) + duration = models.CharField( + choices=DURATIONS, + max_length=255, + verbose_name=_("duration"), + ) + players_min = models.IntegerField( + validators=[MinValueValidator(1)], + verbose_name=_("minimum number of players"), + ) + players_max = models.IntegerField( + validators=[MinValueValidator(1)], + verbose_name=_('maximum number of players'), + ) + comment = models.CharField( + max_length=255, + blank=True, + verbose_name=_('comment'), + ) + + def __str__(self): + return str(self.title) + + class Meta: + verbose_name = _("game") + verbose_name_plural = _("games") + ordering = ['title']