diff --git a/shared/migrations/0006_octopusperson.py b/shared/migrations/0006_octopusperson.py
new file mode 100644
index 0000000000000000000000000000000000000000..d675ab605cda90c3de845ae5289ec7ae433603ae
--- /dev/null
+++ b/shared/migrations/0006_octopusperson.py
@@ -0,0 +1,39 @@
+# Generated by Django 5.0.7 on 2024-07-29 20:18
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('shared', '0005_auto_20240513_0955'),
+        ('wagtailimages', '0026_delete_uploadedimage'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='OctopusPerson',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('email', models.EmailField(max_length=254, verbose_name='Veřejný email')),
+                ('phone', models.CharField(verbose_name='Telefonní číslo')),
+                ('facebook_url', models.CharField(blank=True, null=True, verbose_name='Facebook URL')),
+                ('flickr_url', models.CharField(blank=True, null=True, verbose_name='Flickr URL')),
+                ('instagram_url', models.CharField(blank=True, null=True, verbose_name='Instagram URL')),
+                ('mastodon_url', models.CharField(blank=True, null=True, verbose_name='Mastodon URL')),
+                ('twitter_url', models.CharField(blank=True, null=True, verbose_name='Twitter URL')),
+                ('tiktok_url', models.CharField(blank=True, null=True, verbose_name='TikTok URL')),
+                ('web_url', models.CharField(blank=True, null=True, verbose_name='Web URL')),
+                ('youtube_url', models.CharField(blank=True, null=True, verbose_name='YouTube URL')),
+                ('more_info_url', models.URLField(blank=True, null=True, verbose_name='URL profilu v Chobotnici')),
+                ('profile_type', models.CharField(choices=[('political', 'Politický'), ('pirate', 'Pirátský')], max_length=9, verbose_name='Typ medailonku')),
+                ('short_text', models.TextField(blank=True, null=True, verbose_name='Krátký popis')),
+                ('long_text', models.TextField(blank=True, null=True, verbose_name='Dlouhý popis')),
+                ('degree_before', models.CharField(max_length=64, verbose_name='Titul (před)')),
+                ('degree_after', models.CharField(max_length=64, verbose_name='Titul (za)')),
+                ('display_name', models.CharField(max_length=128, verbose_name='Zobrazované jméno')),
+                ('photo', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='wagtailimages.image', verbose_name='Profilový obrázek')),
+            ],
+        ),
+    ]
diff --git a/shared/migrations/0007_octopusperson_octopus_id.py b/shared/migrations/0007_octopusperson_octopus_id.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce0396fd8c863cdd79e4e8751265a75870919b46
--- /dev/null
+++ b/shared/migrations/0007_octopusperson_octopus_id.py
@@ -0,0 +1,19 @@
+# Generated by Django 5.0.7 on 2024-07-29 20:35
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('shared', '0006_octopusperson'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='octopusperson',
+            name='octopus_id',
+            field=models.CharField(default=None, max_length=32, verbose_name='ID v chobotnici'),
+            preserve_default=False,
+        ),
+    ]
diff --git a/shared/migrations/0008_alter_octopusperson_degree_after_and_more.py b/shared/migrations/0008_alter_octopusperson_degree_after_and_more.py
new file mode 100644
index 0000000000000000000000000000000000000000..3dedeec18755a0eebd7fac1c566f6022cb62fb3c
--- /dev/null
+++ b/shared/migrations/0008_alter_octopusperson_degree_after_and_more.py
@@ -0,0 +1,45 @@
+# Generated by Django 5.0.7 on 2024-07-29 21:14
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('shared', '0007_octopusperson_octopus_id'),
+        ('wagtailimages', '0026_delete_uploadedimage'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='octopusperson',
+            name='degree_after',
+            field=models.CharField(blank=True, max_length=64, null=True, verbose_name='Titul (za)'),
+        ),
+        migrations.AlterField(
+            model_name='octopusperson',
+            name='degree_before',
+            field=models.CharField(blank=True, max_length=64, null=True, verbose_name='Titul (před)'),
+        ),
+        migrations.AlterField(
+            model_name='octopusperson',
+            name='display_name',
+            field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Zobrazované jméno'),
+        ),
+        migrations.AlterField(
+            model_name='octopusperson',
+            name='email',
+            field=models.EmailField(blank=True, max_length=254, null=True, verbose_name='Veřejný email'),
+        ),
+        migrations.AlterField(
+            model_name='octopusperson',
+            name='phone',
+            field=models.CharField(blank=True, null=True, verbose_name='Telefonní číslo'),
+        ),
+        migrations.AlterField(
+            model_name='octopusperson',
+            name='photo',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='wagtailimages.image', verbose_name='Profilový obrázek'),
+        ),
+    ]
diff --git a/shared/models/main.py b/shared/models/main.py
index d96f092976aa69d9061f0db8d48e6d6becbedfb2..e1556a1875634e74e5cc85448e55c244c54d3f28 100644
--- a/shared/models/main.py
+++ b/shared/models/main.py
@@ -2085,3 +2085,122 @@ class PdfPageMixin(models.Model):
 
 
 # --- END Whole page mixins ---
+
+
+# --- BEGIN Django-only models ---
+
+
+class OctopusPerson(models.Model):
+    octopus_id = models.CharField(
+        "ID v chobotnici",
+        max_length=32
+    )
+
+    email = models.EmailField(
+        "Veřejný email",
+        blank=True,
+        null=True,
+    )
+    phone = models.CharField(
+        "Telefonní číslo",
+        blank=True,
+        null=True,
+    )
+
+    facebook_url = models.CharField(
+        "Facebook URL",
+        blank=True,
+        null=True,
+    )
+    flickr_url = models.CharField(
+        "Flickr URL",
+        blank=True,
+        null=True,
+    )
+    instagram_url = models.CharField(
+        "Instagram URL",
+        blank=True,
+        null=True,
+    )
+    mastodon_url = models.CharField(
+        "Mastodon URL",
+        blank=True,
+        null=True,
+    )
+    twitter_url = models.CharField(
+        "Twitter URL",
+        blank=True,
+        null=True,
+    )
+    tiktok_url = models.CharField(
+        "TikTok URL",
+        blank=True,
+        null=True,
+    )
+    web_url = models.CharField(
+        "Web URL",
+        blank=True,
+        null=True,
+    )
+    youtube_url = models.CharField(
+        "YouTube URL",
+        blank=True,
+        null=True,
+    )
+    more_info_url = models.URLField(
+        "URL profilu v Chobotnici",
+        blank=True,
+        null=True,
+    )
+
+    class ProfileTypes(models.TextChoices):
+        POLITICAL = "political", "Politický"
+        PIRATE = "pirate", "Pirátský"
+
+    profile_type = models.CharField(
+        "Typ medailonku",
+        max_length=9,
+        choices=ProfileTypes.choices,
+    )
+
+    photo = models.ForeignKey(
+        "wagtailimages.Image",
+        on_delete=models.PROTECT,
+        verbose_name="Profilový obrázek",
+        blank=True,
+        null=True,
+    )
+
+    short_text = models.TextField(
+        "Krátký popis",
+        blank=True,
+        null=True,
+    )
+    long_text = models.TextField(
+        "Dlouhý popis",
+        blank=True,
+        null=True,
+    )
+
+    degree_before = models.CharField(
+        "Titul (před)",
+        max_length=64,
+        blank=True,
+        null=True,
+    )
+    degree_after = models.CharField(
+        "Titul (za)",
+        max_length=64,
+        blank=True,
+        null=True,
+    )
+
+    display_name = models.CharField(
+        "Zobrazované jméno",
+        max_length=128,
+        blank=True,
+        null=True,
+    )
+
+
+# --- END Django-only models ---
\ No newline at end of file
diff --git a/shared/people_import.py b/shared/people_import.py
index 1d72905e8e7e44cb713fcc6274da51cb550a4064..7debcc05752106c287e42fb383e9f65b2d04c059 100644
--- a/shared/people_import.py
+++ b/shared/people_import.py
@@ -1,6 +1,7 @@
 from gql import gql, Client
 from gql.transport.aiohttp import AIOHTTPTransport
 from django.conf import settings
+from shared.models import OctopusPerson
 import os
 
 
@@ -93,7 +94,6 @@ class PeopleGroupImporter:
                           degreeAfterName
                           degreeBeforeName
                           displayName
-                          profilePhoto
                         }}
                       }}
                     }}
@@ -113,25 +113,63 @@ class PeopleGroupImporter:
 
 
     def perform_import(self):
-        people_ids = self.get_people_ids_from_group()
+        try:
+            people_ids = self.get_people_ids_from_group()
 
-        people_profiles = {}
+            people_profiles = {}
 
-        for person_id in people_ids:
-            prirotizied_profiles = []
+            for person_id in people_ids:
+                prirotizied_profiles = []
 
-            prirotizied_profiles.append(self.get_person_profile_from_id(person_id, "POLITICAL"))
-            prirotizied_profiles.append(self.get_person_profile_from_id(person_id, "PIRATE"))
+                prirotizied_profiles.append(self.get_person_profile_from_id(person_id, "POLITICAL"))
+                prirotizied_profiles.append(self.get_person_profile_from_id(person_id, "PIRATE"))
 
-            for profile in prirotizied_profiles:
-                if profile is None:
-                    continue
+                for profile in prirotizied_profiles:
+                    if profile is None:
+                        continue
 
-                people_profiles[person_id] = profile
+                    people_profiles[person_id] = profile
 
-            if person_id not in people_profiles:
-                people_profiles[person_id] = None
-        
-        print(people_profiles)
+                if person_id not in people_profiles:
+                    people_profiles[person_id] = None
 
-        os.remove(self.lock_file_name)
\ No newline at end of file
+            for person_id, profile in people_profiles.items():
+                person = OctopusPerson.objects.filter(octopus_id=person_id).first()
+
+                if person is None:
+                    person = OctopusPerson(
+                        octopus_id=person_id,
+
+                        email=profile["email"],
+                        phone=profile["phone"],
+
+                        facebook_url=profile["facebookUrl"],
+                        flickr_url=profile["flickrUrl"],
+                        instagram_url=profile["instagramUrl"],
+                        mastodon_url=profile["mastodonUrl"],
+                        twitter_url=profile["twitterUrl"],
+                        tiktok_url=profile["tiktokUrl"],
+                        web_url=profile["webUrl"],
+                        youtube_url=profile["youtubeUrl"],
+                        more_info_url=profile["url"],
+
+                        # TODO: Assume this is valid for the time being
+                        profile_type=profile["kind"],
+
+                        # TODO
+                        photo=None,
+
+                        short_text=profile["textShort"],
+                        long_text=profile["textLong"],
+
+                        degree_before=profile["person"]["degreeBeforeName"],
+                        degree_after=profile["person"]["degreeAfterName"],
+
+                        display_name=profile["person"]["displayName"],
+                    )
+                    person.save()
+                
+                print(person)
+        finally:
+            if os.path.exists(self.lock_file_name):
+                os.remove(self.lock_file_name)
\ No newline at end of file