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(
17 help_text=_('If you have a Wikidata ID, like "Q1337", enter it and save.'),
23 def save(self, **kwargs):
25 if self.wikidata and hasattr(self, "Wikidata"):
26 Wikidata = type(self).Wikidata
28 # Probably should getlist
29 entity = client.get(self.wikidata)
30 for attname in dir(Wikidata):
31 if attname.startswith("_"):
33 wd = getattr(Wikidata, attname)
35 model_field = self._meta.get_field(attname)
36 if isinstance(model_field, models.ManyToManyField):
37 if getattr(self, attname).all().exists():
40 if getattr(self, attname):
44 if wd == "description":
45 wdvalue = entity.description.get("pl", str(entity.description))
47 wdvalue = entity.label.get("pl", str(entity.label))
50 wdvalue = entity.get(client.get(wd))
51 except DatavalueError:
54 self.set_field_from_wikidata(attname, wdvalue)
56 kwargs.update(force_insert=False, force_update=True)
57 super().save(**kwargs)
59 def set_field_from_wikidata(self, attname, wdvalue):
62 # Find out what this model field is
63 model_field = self._meta.get_field(attname)
64 if isinstance(model_field, models.ForeignKey):
65 rel_model = model_field.related_model
66 if issubclass(rel_model, WikidataMixin):
67 # welp, we can try and find by WD identifier.
68 wdvalue, created = rel_model.objects.get_or_create(wikidata=wdvalue.id)
69 setattr(self, attname, wdvalue)
70 elif isinstance(model_field, models.ManyToManyField):
71 rel_model = model_field.related_model
72 if issubclass(rel_model, WikidataMixin):
73 wdvalue, created = rel_model.objects.get_or_create(wikidata=wdvalue.id)
74 getattr(self, attname).set([wdvalue])
76 # How to get original title?
77 if isinstance(wdvalue, date):
78 if isinstance(model_field, models.IntegerField):
79 wdvalue = wdvalue.year
80 elif not isinstance(wdvalue, str):
81 wdvalue = wdvalue.label.get("pl", str(wdvalue.label))
82 setattr(self, attname, wdvalue)
85 class WikidataAdminMixin:
86 def save_related(self, request, form, formsets, change):
87 super().save_related(request, form, formsets, change)
90 def wikidata_link(self, obj):
92 return format_html('<a href="https://www.wikidata.org/wiki/{wd}" target="_blank">{wd}</a>', wd=obj.wikidata)
95 wikidata_link.admin_order_field = 'wikidata'