More data in catalogue
authorRadek Czajka <rczajka@rczajka.pl>
Thu, 22 Sep 2022 14:03:22 +0000 (16:03 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Thu, 22 Sep 2022 14:10:09 +0000 (16:10 +0200)
src/catalogue/admin.py
src/catalogue/constants.py
src/catalogue/locale/pl/LC_MESSAGES/django.mo
src/catalogue/locale/pl/LC_MESSAGES/django.po
src/catalogue/migrations/0035_place_remove_author_place_of_birth_and_more.py [new file with mode: 0644]
src/catalogue/migrations/0036_author_place_of_birth_author_place_of_death.py [new file with mode: 0644]
src/catalogue/migrations/0037_alter_place_options_author_year_of_birth_inexact_and_more.py [new file with mode: 0644]
src/catalogue/models.py
src/catalogue/translation.py
src/catalogue/wikidata.py

index 47152eb..213e9ca 100644 (file)
@@ -16,7 +16,7 @@ from .wikidata import WikidataAdminMixin
 
 class NotableBookInline(OrderableAdmin, admin.TabularInline):
     model = models.NotableBook
-    raw_id_fields = ['book']
+    autocomplete_fields = ['book']
     ordering_field_hide_input = True
 
 
@@ -45,8 +45,35 @@ class AuthorAdmin(WikidataAdminMixin, TabbedTranslationAdmin):
     ]
     list_per_page = 10000000
     search_fields = ["first_name", "last_name", "wikidata"]
+    readonly_fields = ["wikidata_link"]
+
+    fieldsets = [
+        (None, {"fields": [("wikidata", "wikidata_link")]}),
+        (
+            _("Identification"),
+            {
+                "fields": [
+                    ("first_name", "last_name"),
+                    "slug",
+                    "gender",
+                    "nationality",
+                    ("date_of_birth", "year_of_birth", "year_of_birth_inexact", "year_of_birth_range", "place_of_birth"),
+                    ("date_of_death", "year_of_death", "year_of_death_inexact", "year_of_death_range", "place_of_death"),
+                    "description",
+                    "status",
+                    "collections",
+                    "priority",
+                    
+                    "notes",
+                    "gazeta_link",
+                    "culturepl_link",
+                ]
+            },
+        ),
+    ]
+    
     prepopulated_fields = {"slug": ("first_name", "last_name")}
-    autocomplete_fields = ["collections"]
+    autocomplete_fields = ["collections", "place_of_birth", "place_of_death"]
     inlines = [
         NotableBookInline,
     ]
@@ -316,3 +343,8 @@ class WorkTypeAdmin(admin.ModelAdmin):
 
 admin.site.register(models.WorkType, WorkTypeAdmin)
 
+
+
+@admin.register(models.Place)
+class PlaceAdmin(WikidataAdminMixin, TabbedTranslationAdmin):
+    search_fields = ['name']
index 9134e2d..0f961b9 100644 (file)
@@ -2,10 +2,13 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 class WIKIDATA:
+    PLACE_OF_BIRTH = 'P19'
+    PLACE_OF_DEATH = 'P20'
     GENDER = "P21"
     AUTHOR = "P50"
     CREATOR = "P170"
     LANGUAGE = "P407"
+    DATE_OF_BIRTH = "P569"
     DATE_OF_DEATH = "P570"
     LAST_NAME = "P734"
     GIVEN_NAME = "P735"
index 3dbf04b..39239f8 100644 (file)
Binary files a/src/catalogue/locale/pl/LC_MESSAGES/django.mo and b/src/catalogue/locale/pl/LC_MESSAGES/django.mo differ
index ad827e5..044f960 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2022-09-19 15:45+0200\n"
+"PO-Revision-Date: 2022-09-22 15:57+0200\n"
 "Last-Translator: \n"
 "Language-Team: \n"
 "Language: pl\n"
@@ -19,29 +19,27 @@ msgstr ""
 "n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
 "X-Generator: Poedit 3.0.1\n"
 
-#: catalogue/admin.py:176
+#: catalogue/admin.py:53 catalogue/admin.py:203
 msgid "Identification"
 msgstr "Identyfikacja"
 
-#: catalogue/admin.py:190
+#: catalogue/admin.py:217
 msgid "Features"
 msgstr "Cechy"
 
-#: catalogue/admin.py:200
+#: catalogue/admin.py:227
 msgid "Plan"
 msgstr "Plan"
 
-#: catalogue/admin.py:242
-#, fuzzy
-#| msgid "title"
+#: catalogue/admin.py:269
 msgid "Title"
 msgstr "tytuł"
 
-#: catalogue/admin.py:248
+#: catalogue/admin.py:275
 msgid "Book"
 msgstr "książka"
 
-#: catalogue/admin.py:258 catalogue/models.py:139
+#: catalogue/admin.py:285 catalogue/models.py:160
 msgid "scans source"
 msgstr "źródło skanów"
 
@@ -73,217 +71,253 @@ msgstr "narodowość"
 msgid "year of birth"
 msgstr "rok urodzenia"
 
-#: catalogue/models.py:24
+#: catalogue/models.py:24 catalogue/models.py:33
+msgid "inexact"
+msgstr "niedokładny"
+
+#: catalogue/models.py:25
+msgid "year of birth, range end"
+msgstr "rok urodzenia, koniec zakresu"
+
+#: catalogue/models.py:26
+msgid "date_of_birth"
+msgstr "data urodzenia"
+
+#: catalogue/models.py:29
 msgid "place of birth"
 msgstr "miejsce urodzenia"
 
-#: catalogue/models.py:25
+#: catalogue/models.py:32
 msgid "year of death"
 msgstr "rok śmierci"
 
-#: catalogue/models.py:26
+#: catalogue/models.py:34
+msgid "year of death, range end"
+msgstr "rok śmierci, koniec zakresu"
+
+#: catalogue/models.py:35
+msgid "date_of_death"
+msgstr "data śmierci"
+
+#: catalogue/models.py:38
 msgid "place of death"
 msgstr "miejsce śmierci"
 
-#: catalogue/models.py:28
+#: catalogue/models.py:42
 msgid "status"
 msgstr "status"
 
-#: catalogue/models.py:32
+#: catalogue/models.py:46
 msgid "Alive"
 msgstr "Żyje"
 
-#: catalogue/models.py:33
+#: catalogue/models.py:47
 msgid "Dead"
 msgstr "Zmarły"
 
-#: catalogue/models.py:34
+#: catalogue/models.py:48
 msgid "Long dead"
 msgstr "Dawno zmarły"
 
-#: catalogue/models.py:35
+#: catalogue/models.py:49
 msgid "Unknown"
 msgstr "Nieznany"
 
-#: catalogue/models.py:38 catalogue/models.py:141 catalogue/models.py:207
-#: catalogue/models.py:225
+#: catalogue/models.py:52 catalogue/models.py:162 catalogue/models.py:228
+#: catalogue/models.py:246
 msgid "notes"
 msgstr "notatki"
 
-#: catalogue/models.py:39 catalogue/models.py:147
+#: catalogue/models.py:53 catalogue/models.py:168
 msgid "gazeta link"
 msgstr "link do bazy gazety"
 
-#: catalogue/models.py:40
+#: catalogue/models.py:54
 msgid "culture.pl link"
 msgstr "link do bazy culture.pl"
 
-#: catalogue/models.py:42
+#: catalogue/models.py:56
 msgid "description"
 msgstr "opis"
 
-#: catalogue/models.py:47 catalogue/models.py:143 catalogue/models.py:275
+#: catalogue/models.py:59 catalogue/models.py:164 catalogue/models.py:296
 msgid "priority"
 msgstr "priorytet"
 
-#: catalogue/models.py:48 catalogue/models.py:144
+#: catalogue/models.py:60 catalogue/models.py:165
 msgid "Low"
 msgstr "Niski"
 
-#: catalogue/models.py:48 catalogue/models.py:144
+#: catalogue/models.py:60 catalogue/models.py:165
 msgid "Medium"
 msgstr "Średni"
 
-#: catalogue/models.py:48 catalogue/models.py:144
+#: catalogue/models.py:60 catalogue/models.py:165
 msgid "High"
 msgstr "Wysoki"
 
-#: catalogue/models.py:50 catalogue/models.py:148 catalogue/models.py:230
-#: catalogue/models.py:282
+#: catalogue/models.py:62 catalogue/models.py:169 catalogue/models.py:251
+#: catalogue/models.py:303
 msgid "collections"
 msgstr "kolekcje"
 
-#: catalogue/models.py:53
+#: catalogue/models.py:65
 msgid "author"
 msgstr "autor"
 
-#: catalogue/models.py:54 catalogue/models.py:122
+#: catalogue/models.py:66 catalogue/models.py:143
 msgid "authors"
 msgstr "autorzy"
 
-#: catalogue/models.py:93 catalogue/models.py:205 catalogue/models.py:222
-#: catalogue/models.py:255
+#: catalogue/models.py:114 catalogue/models.py:226 catalogue/models.py:243
+#: catalogue/models.py:276 catalogue/models.py:329
 msgid "name"
 msgstr "nazwa"
 
-#: catalogue/models.py:104
+#: catalogue/models.py:125
 msgid "epoch"
 msgstr "epoka"
 
-#: catalogue/models.py:105 catalogue/models.py:130 catalogue/models.py:279
+#: catalogue/models.py:126 catalogue/models.py:151 catalogue/models.py:300
 msgid "epochs"
 msgstr "epoki"
 
-#: catalogue/models.py:110
+#: catalogue/models.py:131
 msgid "genre"
 msgstr "gatunek"
 
-#: catalogue/models.py:111 catalogue/models.py:132 catalogue/models.py:281
+#: catalogue/models.py:132 catalogue/models.py:153 catalogue/models.py:302
 msgid "genres"
 msgstr "gatunki"
 
-#: catalogue/models.py:116
+#: catalogue/models.py:137
 msgid "kind"
 msgstr "rodzaj"
 
-#: catalogue/models.py:117 catalogue/models.py:131 catalogue/models.py:280
+#: catalogue/models.py:138 catalogue/models.py:152 catalogue/models.py:301
 msgid "kinds"
 msgstr "rodzaje"
 
-#: catalogue/models.py:128
+#: catalogue/models.py:149
 msgid "translators"
 msgstr "tłumacze"
 
-#: catalogue/models.py:133
+#: catalogue/models.py:154
 msgid "title"
 msgstr "tytuł"
 
-#: catalogue/models.py:134
+#: catalogue/models.py:155
 msgid "language"
 msgstr "język"
 
-#: catalogue/models.py:137
+#: catalogue/models.py:158
 msgid "based on"
 msgstr "oparte na"
 
-#: catalogue/models.py:140
+#: catalogue/models.py:161
 msgid "text source"
 msgstr "źródło tekstu"
 
-#: catalogue/models.py:146
+#: catalogue/models.py:167
 msgid "year of entry into PD"
 msgstr "rok wstąpienia do DP"
 
-#: catalogue/models.py:150
+#: catalogue/models.py:171
 msgid "estimated number of characters"
 msgstr "szacowana liczba znaków"
 
-#: catalogue/models.py:151
+#: catalogue/models.py:172
 msgid "estimated number of verses"
 msgstr "szacowana liczba wersów"
 
-#: catalogue/models.py:152
+#: catalogue/models.py:173
 msgid "source of estimates"
 msgstr "źródło szacunków"
 
-#: catalogue/models.py:154
+#: catalogue/models.py:175
 msgid "free license"
 msgstr "wolna licencja"
 
-#: catalogue/models.py:155
+#: catalogue/models.py:176
 msgid "missing on Polona"
 msgstr "brak na Polonie"
 
-#: catalogue/models.py:159
+#: catalogue/models.py:180
 msgid "book"
 msgstr "książka"
 
-#: catalogue/models.py:160
+#: catalogue/models.py:181
 msgid "books"
 msgstr "książki"
 
-#: catalogue/models.py:190
+#: catalogue/models.py:211
 msgid "Author"
 msgstr "autor"
 
-#: catalogue/models.py:195
+#: catalogue/models.py:216
 msgid "Translator"
 msgstr "tłumacze"
 
-#: catalogue/models.py:206
+#: catalogue/models.py:227
 msgid "parent"
 msgstr "rodzic"
 
-#: catalogue/models.py:211
+#: catalogue/models.py:232
 msgid "collection category"
 msgstr "kategoria kolekcji"
 
-#: catalogue/models.py:212
+#: catalogue/models.py:233
 msgid "collection categories"
 msgstr "kategorie kolekcji"
 
-#: catalogue/models.py:224
+#: catalogue/models.py:245
 msgid "category"
 msgstr "kategoria"
 
-#: catalogue/models.py:229
+#: catalogue/models.py:250
 msgid "collection"
 msgstr "kolekcja"
 
-#: catalogue/models.py:259 catalogue/models.py:278
+#: catalogue/models.py:280 catalogue/models.py:299
 msgid "work type"
 msgstr "rodzaj pracy"
 
-#: catalogue/models.py:260
+#: catalogue/models.py:281
 msgid "work types"
 msgstr "rodzaje prac"
 
-#: catalogue/models.py:276
+#: catalogue/models.py:297
 msgid "per normalized page"
 msgstr "za stronę maszynopisu"
 
-#: catalogue/models.py:277
+#: catalogue/models.py:298
 msgid "per verse"
 msgstr "za wers"
 
-#: catalogue/models.py:286
+#: catalogue/models.py:307
 msgid "work rate"
 msgstr "stawka"
 
-#: catalogue/models.py:287
+#: catalogue/models.py:308
 msgid "work rates"
 msgstr "stawki"
 
+#: catalogue/models.py:330
+msgid "locative"
+msgstr "miejscownik"
+
+#: catalogue/models.py:330
+msgid "in…"
+msgstr "w…"
+
+#: catalogue/models.py:333
+msgid "place"
+msgstr "miejsce"
+
+#: catalogue/models.py:334
+msgid "places"
+msgstr "miejsca"
+
 #: catalogue/templates/catalogue/author_detail.html:7
 #: catalogue/templates/catalogue/author_detail.html:13
 #: catalogue/templates/catalogue/book_detail.html:7
diff --git a/src/catalogue/migrations/0035_place_remove_author_place_of_birth_and_more.py b/src/catalogue/migrations/0035_place_remove_author_place_of_birth_and_more.py
new file mode 100644 (file)
index 0000000..339af8f
--- /dev/null
@@ -0,0 +1,83 @@
+# Generated by Django 4.0.6 on 2022-09-22 14:57
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('catalogue', '0034_notablebook'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Place',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('wikidata', models.CharField(blank=True, help_text='If you have a Wikidata ID, like "Q1337", enter it and save.', max_length=255)),
+                ('name', models.CharField(blank=True, max_length=255, verbose_name='name')),
+                ('name_pl', models.CharField(blank=True, max_length=255, null=True, verbose_name='name')),
+                ('name_de', models.CharField(blank=True, max_length=255, null=True, verbose_name='name')),
+                ('name_lt', models.CharField(blank=True, max_length=255, null=True, verbose_name='name')),
+                ('locative', models.CharField(blank=True, help_text='in…', max_length=255, verbose_name='locative')),
+                ('locative_pl', models.CharField(blank=True, help_text='in…', max_length=255, null=True, verbose_name='locative')),
+                ('locative_de', models.CharField(blank=True, help_text='in…', max_length=255, null=True, verbose_name='locative')),
+                ('locative_lt', models.CharField(blank=True, help_text='in…', max_length=255, null=True, verbose_name='locative')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.RemoveField(
+            model_name='author',
+            name='place_of_birth',
+        ),
+        migrations.RemoveField(
+            model_name='author',
+            name='place_of_birth_de',
+        ),
+        migrations.RemoveField(
+            model_name='author',
+            name='place_of_birth_lt',
+        ),
+        migrations.RemoveField(
+            model_name='author',
+            name='place_of_birth_pl',
+        ),
+        migrations.RemoveField(
+            model_name='author',
+            name='place_of_death',
+        ),
+        migrations.RemoveField(
+            model_name='author',
+            name='place_of_death_de',
+        ),
+        migrations.RemoveField(
+            model_name='author',
+            name='place_of_death_lt',
+        ),
+        migrations.RemoveField(
+            model_name='author',
+            name='place_of_death_pl',
+        ),
+        migrations.AddField(
+            model_name='author',
+            name='date_of_birth',
+            field=models.DateField(blank=True, null=True, verbose_name='date_of_birth'),
+        ),
+        migrations.AddField(
+            model_name='author',
+            name='date_of_death',
+            field=models.DateField(blank=True, null=True, verbose_name='date_of_death'),
+        ),
+        migrations.AddField(
+            model_name='author',
+            name='year_of_birth_range',
+            field=models.SmallIntegerField(blank=True, null=True, verbose_name='year of birth, range end'),
+        ),
+        migrations.AddField(
+            model_name='author',
+            name='year_of_death_range',
+            field=models.SmallIntegerField(blank=True, null=True, verbose_name='year of death, range end'),
+        ),
+    ]
diff --git a/src/catalogue/migrations/0036_author_place_of_birth_author_place_of_death.py b/src/catalogue/migrations/0036_author_place_of_birth_author_place_of_death.py
new file mode 100644 (file)
index 0000000..b72e4ba
--- /dev/null
@@ -0,0 +1,24 @@
+# Generated by Django 4.0.6 on 2022-09-22 14:57
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('catalogue', '0035_place_remove_author_place_of_birth_and_more'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='author',
+            name='place_of_birth',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='authors_born', to='catalogue.place', verbose_name='place of birth'),
+        ),
+        migrations.AddField(
+            model_name='author',
+            name='place_of_death',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='authors_died', to='catalogue.place', verbose_name='place of death'),
+        ),
+    ]
diff --git a/src/catalogue/migrations/0037_alter_place_options_author_year_of_birth_inexact_and_more.py b/src/catalogue/migrations/0037_alter_place_options_author_year_of_birth_inexact_and_more.py
new file mode 100644 (file)
index 0000000..e5c5629
--- /dev/null
@@ -0,0 +1,27 @@
+# Generated by Django 4.0.6 on 2022-09-22 15:47
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('catalogue', '0036_author_place_of_birth_author_place_of_death'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='place',
+            options={'verbose_name': 'place', 'verbose_name_plural': 'places'},
+        ),
+        migrations.AddField(
+            model_name='author',
+            name='year_of_birth_inexact',
+            field=models.BooleanField(default=False, verbose_name='inexact'),
+        ),
+        migrations.AddField(
+            model_name='author',
+            name='year_of_death_inexact',
+            field=models.BooleanField(default=False, verbose_name='inexact'),
+        ),
+    ]
index d069880..d671425 100644 (file)
@@ -21,9 +21,23 @@ class Author(WikidataMixin, models.Model):
     gender = models.CharField(_("gender"), max_length=255, blank=True)
     nationality = models.CharField(_("nationality"), max_length=255, blank=True)
     year_of_birth = models.SmallIntegerField(_("year of birth"), null=True, blank=True)
-    place_of_birth = models.CharField(_('place of birth'), max_length=255, blank=True)
+    year_of_birth_inexact = models.BooleanField(_("inexact"), default=False)
+    year_of_birth_range = models.SmallIntegerField(_("year of birth, range end"), null=True, blank=True)
+    date_of_birth = models.DateField(_("date_of_birth"), null=True, blank=True)
+    place_of_birth = models.ForeignKey(
+        'Place', models.PROTECT, null=True, blank=True,
+        verbose_name=_('place of birth'),
+        related_name='authors_born'
+    )
     year_of_death = models.SmallIntegerField(_("year of death"), null=True, blank=True)
-    place_of_death = models.CharField(_('place of death'), max_length=255, blank=True)
+    year_of_death_inexact = models.BooleanField(_("inexact"), default=False)
+    year_of_death_range = models.SmallIntegerField(_("year of death, range end"), null=True, blank=True)
+    date_of_death = models.DateField(_("date_of_death"), null=True, blank=True)
+    place_of_death = models.ForeignKey(
+        'Place', models.PROTECT, null=True, blank=True,
+        verbose_name=_('place of death'),
+        related_name='authors_died'
+    )
     status = models.PositiveSmallIntegerField(
         _("status"), 
         null=True,
@@ -55,10 +69,19 @@ class Author(WikidataMixin, models.Model):
     class Wikidata:
         first_name = WIKIDATA.GIVEN_NAME
         last_name = WIKIDATA.LAST_NAME
+        date_of_birth = WIKIDATA.DATE_OF_BIRTH
+        year_of_birth = WIKIDATA.DATE_OF_BIRTH
+        place_of_birth = WIKIDATA.PLACE_OF_BIRTH
+        date_of_death = WIKIDATA.DATE_OF_DEATH
         year_of_death = WIKIDATA.DATE_OF_DEATH
+        place_of_death = WIKIDATA.PLACE_OF_DEATH
         gender = WIKIDATA.GENDER
         notes = "description"
 
+        def _supplement(obj):
+            if not obj.first_name and not obj.last_name:
+                yield 'first_name', 'label'
+
     def __str__(self):
         name = f"{self.first_name} {self.last_name}"
         if self.year_of_death is not None:
@@ -169,10 +192,10 @@ class Book(WikidataMixin, models.Model):
         txt = self.title
         astr = self.authors_str()
         if astr:
-            txt = f"{astr} – {txt}"
+            txt = f"{txt}, {astr}"
         tstr = self.translators_str()
         if tstr:
-            txt = f"{txt} (tłum. {tstr})"
+            txt = f"{txt}, tłum. {tstr}"
         return txt
 
     def get_absolute_url(self):
@@ -301,3 +324,17 @@ class WorkRate(models.Model):
             if book.estimated_chars:
                 return (decimal.Decimal(book.estimated_chars) / 1800 * self.per_normpage).quantize(decimal.Decimal('1.00'), rounding=decimal.ROUND_HALF_UP)
 
+
+class Place(WikidataMixin, models.Model):
+    name = models.CharField(_('name'), max_length=255, blank=True)
+    locative = models.CharField(_('locative'), max_length=255, blank=True, help_text=_('in…'))
+
+    class Meta:
+        verbose_name = _('place')
+        verbose_name_plural = _('places')
+    
+    class Wikidata:
+        name = 'label'
+
+    def __str__(self):
+        return self.name
index 0ee8071..9637e55 100644 (file)
@@ -7,7 +7,13 @@ class AuthorTranslationOptions(TranslationOptions):
     fields = (
         'first_name',
         'last_name',
-        'place_of_birth',
-        'place_of_death',
         'description',
     )
+
+
+@register(models.Place)
+class PlaceTranslationOptions(TranslationOptions):
+    fields = (
+        'name',
+        'locative',
+    )
index 4fc8bbd..610245d 100644 (file)
@@ -21,6 +21,28 @@ class WikidataMixin(models.Model):
     class Meta:
         abstract = True
 
+    def wikidata_populate(self, client, entity, attname, wd):
+        model_field = self._meta.get_field(attname)
+        if isinstance(model_field, models.ManyToManyField):
+            if getattr(self, attname).all().exists():
+                return
+        else:
+            if getattr(self, attname):
+                return
+
+        wdvalue = None
+        if wd == "description":
+            wdvalue = entity.description.get("pl", str(entity.description))
+        elif wd == "label":
+            wdvalue = entity.label.get("pl", str(entity.label))
+        else:
+            try:
+                wdvalue = entity.get(client.get(wd))
+            except DatavalueError:
+                pass
+
+        self.set_field_from_wikidata(attname, wdvalue)
+        
     def save(self, **kwargs):
         super().save()
         if self.wikidata and hasattr(self, "Wikidata"):
@@ -33,26 +55,11 @@ class WikidataMixin(models.Model):
                     continue
                 wd = getattr(Wikidata, attname)
 
-                model_field = self._meta.get_field(attname)
-                if isinstance(model_field, models.ManyToManyField):
-                    if getattr(self, attname).all().exists():
-                        continue
-                else:
-                    if getattr(self, attname):
-                        continue
-
-                wdvalue = None
-                if wd == "description":
-                    wdvalue = entity.description.get("pl", str(entity.description))
-                elif wd == "label":
-                    wdvalue = entity.label.get("pl", str(entity.label))
-                else:
-                    try:
-                        wdvalue = entity.get(client.get(wd))
-                    except DatavalueError:
-                        pass
+                self.wikidata_populate(client, entity, attname, wd)
+            if hasattr(Wikidata, '_supplement'):
+                for attname, wd in Wikidata._supplement(self):
+                    self.wikidata_populate(client, entity, attname, wd)
 
-                self.set_field_from_wikidata(attname, wdvalue)
 
         kwargs.update(force_insert=False, force_update=True)
         super().save(**kwargs)