-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Support of including blog in the website search #2136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
03c5801
12c377e
837e808
5e22aee
edea21c
e9d3d3b
f3c5a83
4fa01d6
021a57d
e5a9452
bcffdee
a7b3bbe
c1a61a9
8e01cca
629bc6d
c89240a
606d381
396050c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 5.2 on 2025-07-24 07:21 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('blog', '0005_entry_social_media_card'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='entry', | ||
name='is_searchable', | ||
field=models.BooleanField(help_text='Tick to make this entry allow this entry to appear in the Django documentation search.', null=True), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Generated by Django 5.2 on 2025-07-24 07:22 | ||
|
||
from django.db import migrations | ||
|
||
|
||
def set_is_searchable(apps, schema_editor): | ||
Entry = apps.get_model("blog", "Entry") | ||
# If this is large, this should be split into batched updates | ||
Entry.objects.filter(is_searchable__isnull=True).update(is_searchable=False) | ||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('blog', '0006_entry_is_searchable'), | ||
] | ||
|
||
operations = [ | ||
migrations.RunPython(set_is_searchable, reverse_code=migrations.RunPython.noop, elidable=True), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 5.2 on 2025-07-24 07:22 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('blog', '0007_set_is_searchable'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='entry', | ||
name='is_searchable', | ||
field=models.BooleanField(default=False, help_text='Tick to make this entry allow this entry to appear in the Django documentation search.'), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 5.2 on 2025-07-23 16:31 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('docs', '0006_alter_document_metadata_noop'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='documentrelease', | ||
name='support_end', | ||
field=models.DateField(blank=True, help_text='The end of support for this release of Django.', null=True), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,10 +18,13 @@ | |
from django.db import models, transaction | ||
from django.db.models import Q | ||
from django.db.models.fields.json import KeyTextTransform | ||
from django.test import RequestFactory | ||
from django.urls import resolve, reverse as reverse_path | ||
from django.utils.functional import cached_property | ||
from django.utils.html import strip_tags | ||
from django_hosts.resolvers import reverse | ||
|
||
from blog.models import Entry | ||
from releases.models import Release | ||
|
||
from . import utils | ||
|
@@ -31,6 +34,7 @@ | |
START_SEL, | ||
STOP_SEL, | ||
TSEARCH_CONFIG_LANGUAGES, | ||
DocumentationCategory, | ||
) | ||
|
||
|
||
|
@@ -95,6 +99,11 @@ class DocumentRelease(models.Model): | |
on_delete=models.CASCADE, | ||
) | ||
is_default = models.BooleanField(default=False) | ||
support_end = models.DateField( | ||
null=True, | ||
blank=True, | ||
help_text="The end of support for this release of Django.", | ||
) | ||
|
||
|
||
objects = DocumentReleaseQuerySet.as_manager() | ||
|
||
|
@@ -212,6 +221,83 @@ def sync_to_db(self, decoded_documents): | |
) | ||
document.save(update_fields=("metadata",)) | ||
|
||
self._sync_blog_to_db() | ||
self._sync_views_to_db() | ||
|
||
def _sync_blog_to_db(self): | ||
""" | ||
Sync the blog entries into search based on the release documents | ||
support end date. | ||
""" | ||
if self.lang == "en" and self.support_end: | ||
for entry in Entry.objects.published(self.support_end).searchable(): | ||
Document.objects.create( | ||
|
||
release=self, | ||
path=entry.get_absolute_url(), | ||
title=entry.headline, | ||
metadata={ | ||
"body": entry.body_html, | ||
"breadcrumbs": [ | ||
{ | ||
"path": DocumentationCategory.WEBSITE.value, | ||
"title": "News", | ||
}, | ||
], | ||
"parents": DocumentationCategory.WEBSITE.value, | ||
"slug": entry.slug, | ||
"title": entry.headline, | ||
"toc": "", | ||
}, | ||
config=TSEARCH_CONFIG_LANGUAGES.get( | ||
self.lang[:2], DEFAULT_TEXT_SEARCH_CONFIG | ||
), | ||
tim-schilling marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
) | ||
|
||
def _sync_views_to_db(self): | ||
""" | ||
Sync the blog entries into search based on the release documents | ||
support end date. | ||
tim-schilling marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
""" | ||
if self.lang == "en": | ||
tim-schilling marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
# The request needs to come through as a valid one, it's best if it | ||
# matches the exact host we're looking for. | ||
www_hosts = [ | ||
host for host in settings.ALLOWED_HOSTS if host.startswith("www.") | ||
] | ||
if not www_hosts or not (www_host := www_hosts[0]): | ||
return | ||
synced_views = [ | ||
|
||
# Page title, url name, url kwargs | ||
("Django's Ecosystem", "community-ecosystem", {}), | ||
] | ||
for title, url_name, kwargs in synced_views: | ||
absolute_url = reverse(url_name, kwargs=kwargs, host="www") | ||
path = reverse_path(url_name, kwargs=kwargs) | ||
request = RequestFactory().get(path, HTTP_HOST=www_host) | ||
body = resolve(path).func(request).render().text | ||
|
||
# Need to parse the body element. | ||
|
||
Document.objects.create( | ||
release=self, | ||
path=absolute_url, | ||
title=title, | ||
metadata={ | ||
"body": body, | ||
"breadcrumbs": [ | ||
{ | ||
"path": DocumentationCategory.WEBSITE.value, | ||
"title": "Website", | ||
}, | ||
], | ||
"parents": DocumentationCategory.WEBSITE.value, | ||
"slug": url_name, | ||
"title": title, | ||
"toc": "", | ||
}, | ||
config=TSEARCH_CONFIG_LANGUAGES.get( | ||
self.lang[:2], DEFAULT_TEXT_SEARCH_CONFIG | ||
), | ||
) | ||
|
||
|
||
def _clean_document_path(path): | ||
# We have to be a bit careful to reverse-engineer the correct | ||
|
@@ -224,7 +310,9 @@ def _clean_document_path(path): | |
|
||
|
||
def document_url(doc): | ||
if doc.path: | ||
if doc.metadata.get("parents") == DocumentationCategory.WEBSITE.value: | ||
tim-schilling marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
return doc.path | ||
elif doc.path: | ||
kwargs = { | ||
"lang": doc.release.lang, | ||
"version": doc.release.version, | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -65,6 +65,7 @@ class DocumentationCategory(TextChoices): | |||||
TOPICS = "topics", _("Using Django") | ||||||
HOWTO = "howto", _("How-to guides") | ||||||
RELEASE_NOTES = "releases", _("Release notes") | ||||||
WEBSITE = "weblog", _("Django Website") | ||||||
|
WEBSITE = "weblog", _("Django Website") | |
WEBSITE = "website", _("Django Website") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is a bigger question. We need the value to be "weblog"
to make the breadcrumbs work properly in the search area without adding another hack on the search results side to adjust things. I suppose we could just have "www.djangoproject.com/website/" redirect to "www.djangoproject.com/weblog/". However, I have no idea what to call that category of results on the actual page. I think right now I have it as "Django Website" which doesn't seem right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do the breadcrumbs work for you in the first place? I don't think I've ever used that feature and it doesn't seem to work correctly for me locally: even for documents with two or more parents, the links for all the parents are all the same and go to the document's own page.
Not sure I'd want to start adding redirects, that seems like solving the problem at the wrong level to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea what I ran into originally. Looks like we can rename this to be whatever we want.
The breadcrumbs links on the search just point directly to the search result. Not the actual parents. May be a bug to be honest 🫣
Uh oh!
There was an error while loading. Please reload this page.