Skip to content

Commit 2f7e186

Browse files
committed
Added blog.Entry.social_media_card field
1 parent 0401a13 commit 2f7e186

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

blog/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class EntryAdmin(admin.ModelAdmin):
1515
list_filter = ("is_active",)
1616
exclude = ("summary_html", "body_html")
1717
prepopulated_fields = {"slug": ("headline",)}
18+
raw_id_fields = ["social_media_card"]
1819

1920
def formfield_for_dbfield(self, db_field, **kwargs):
2021
formfield = super().formfield_for_dbfield(db_field, **kwargs)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Django 6.0.dev20250403184043 on 2025-04-11 07:55
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('blog', '0004_imageupload'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='entry',
16+
name='social_media_card',
17+
field=models.ForeignKey(blank=True, help_text='For maximum compatibility, the image should be < 5 Mb and at least 1200x630 px.', null=True, on_delete=django.db.models.deletion.PROTECT, to='blog.imageupload'),
18+
),
19+
]

blog/models.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import mimetypes
12
from urllib.parse import urlparse
23

34
from django.conf import settings
@@ -9,7 +10,7 @@
910
from django.utils.cache import _generate_cache_header_key
1011
from django.utils.formats import date_format
1112
from django.utils.translation import gettext_lazy as _
12-
from django_hosts.resolvers import reverse
13+
from django_hosts.resolvers import get_host, reverse, reverse_host
1314
from docutils.core import publish_parts
1415
from markdown import markdown
1516
from markdown.extensions.toc import TocExtension, slugify as _md_title_slugify
@@ -98,6 +99,26 @@ class ImageUpload(models.Model):
9899
class Meta:
99100
ordering = ("-uploaded_on",)
100101

102+
def __str__(self):
103+
return f"({self.uploaded_on.date()}) {self.title}"
104+
105+
@property
106+
def mimetype(self):
107+
mimetype, _ = mimetypes.guess_type(self.image.name)
108+
return mimetype or "application/octet-stream"
109+
110+
@property
111+
def full_url(self):
112+
"""
113+
Return a full URL (scheme + hostname + path) to the image
114+
"""
115+
p = urlparse(self.image.url)
116+
if p.netloc:
117+
return self.image.url
118+
host = get_host()
119+
hostname = reverse_host(host)
120+
return f"{host.scheme}{hostname}{host.port}{self.image.url}"
121+
101122

102123
class Entry(models.Model):
103124
headline = models.CharField(max_length=200)
@@ -123,6 +144,16 @@ class Entry(models.Model):
123144
body = models.TextField()
124145
body_html = models.TextField()
125146
author = models.CharField(max_length=100)
147+
social_media_card = models.ForeignKey(
148+
ImageUpload,
149+
on_delete=models.PROTECT,
150+
blank=True,
151+
null=True,
152+
help_text=_(
153+
"For maximum compatibility, the image should be < 5 Mb "
154+
"and at least 1200x630 px."
155+
),
156+
)
126157

127158
objects = EntryQuerySet.as_manager()
128159

@@ -179,7 +210,7 @@ def invalidate_cached_entry(self):
179210

180211
@property
181212
def opengraph_tags(self):
182-
return {
213+
tags = {
183214
"og:type": "article",
184215
"og:title": self.headline,
185216
"og:description": _("Posted by {author} on {pub_date}").format(
@@ -196,6 +227,14 @@ def opengraph_tags(self):
196227
"twitter:creator": "djangoproject",
197228
"twitter:site": "djangoproject",
198229
}
230+
if card := self.social_media_card:
231+
tags |= {
232+
"og:image": card.full_url,
233+
"og:image:alt": card.alt_text,
234+
"og:image:type": card.mimetype,
235+
}
236+
237+
return tags
199238

200239

201240
class EventQuerySet(EntryQuerySet):

0 commit comments

Comments
 (0)