1 from datetime import date
2 from django.db import models
3 from django.db.models.signals import m2m_changed
4 from django.utils.html import format_html
5 from django.utils.translation import gettext_lazy as _
6 from django.dispatch import receiver
7 from wikidata.client import Client
8 from wikidata.datavalue import DatavalueError
11 class WikidataMixin(models.Model):
12 wikidata = models.CharField(
15 help_text=_('If you have a Wikidata ID, like "Q1337", enter it and save.'),
21 def save(self, **kwargs):
23 if self.wikidata and hasattr(self, "Wikidata"):
24 Wikidata = type(self).Wikidata
26 # Probably should getlist
27 entity = client.get(self.wikidata)
28 for attname in dir(Wikidata):
29 if attname.startswith("_"):
31 wd = getattr(Wikidata, attname)
33 model_field = self._meta.get_field(attname)
34 if isinstance(model_field, models.ManyToManyField):
35 if getattr(self, attname).all().exists():
38 if getattr(self, attname):
42 if wd == "description":
43 wdvalue = entity.description.get("pl", str(entity.description))
45 wdvalue = entity.label.get("pl", str(entity.label))
48 wdvalue = entity.get(client.get(wd))
49 except DatavalueError:
52 self.set_field_from_wikidata(attname, wdvalue)
54 kwargs.update(force_insert=False, force_update=True)
55 super().save(**kwargs)
57 def set_field_from_wikidata(self, attname, wdvalue):
60 # Find out what this model field is
61 model_field = self._meta.get_field(attname)
62 if isinstance(model_field, models.ForeignKey):
63 rel_model = model_field.related_model
64 if issubclass(rel_model, WikidataMixin):
65 # welp, we can try and find by WD identifier.
66 wdvalue, created = rel_model.objects.get_or_create(wikidata=wdvalue.id)
67 setattr(self, attname, wdvalue)
68 elif isinstance(model_field, models.ManyToManyField):
69 rel_model = model_field.related_model
70 if issubclass(rel_model, WikidataMixin):
71 wdvalue, created = rel_model.objects.get_or_create(wikidata=wdvalue.id)
72 getattr(self, attname).set([wdvalue])
74 # How to get original title?
75 if isinstance(wdvalue, date):
76 if isinstance(model_field, models.IntegerField):
77 wdvalue = wdvalue.year
78 elif not isinstance(wdvalue, str):
79 wdvalue = wdvalue.label.get("pl", str(wdvalue.label))
80 setattr(self, attname, wdvalue)
83 class WikidataAdminMixin:
84 def save_related(self, request, form, formsets, change):
85 super().save_related(request, form, formsets, change)
88 def wikidata_link(self, obj):
91 '<a href="https://www.wikidata.org/wiki/{wd}" target="_blank">{wd}</a>',
97 wikidata_link.admin_order_field = "wikidata"