I use wagtail cms 6.3 on the top of Django 5.1.3. When I try to make migrations I get the following completely unrelated error message:
python manage.py makemigrations
Traceback (most recent call last):
File "/home/roland/Documents/developments/ai_blog2/manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
utility.execute()
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/django/core/management/__init__.py", line 436, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/django/core/management/base.py", line 413, in run_from_argv
self.execute(*args, **cmd_options)
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/django/core/management/base.py", line 454, in execute
self.check()
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/django/core/management/base.py", line 486, in check
all_issues = checks.run_checks(
^^^^^^^^^^^^^^^^^^
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/django/core/checks/registry.py", line 88, in run_checks
new_errors = check(app_configs=app_configs, databases=databases)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/wagtail/admin/checks.py", line 70, in get_form_class_check
if not issubclass(edit_handler.get_form_class(), WagtailAdminPageForm):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/wagtail/admin/panels/base.py", line 134, in get_form_class
return get_form_for_model(
^^^^^^^^^^^^^^^^^^^
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/wagtail/admin/panels/base.py", line 48, in get_form_for_model
return metaclass(class_name, tuple(bases), form_class_attrs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/permissionedforms/forms.py", line 30, in __new__
new_class = super().__new__(mcs, name, bases, attrs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/modelcluster/forms.py", line 259, in __new__
new_class = super().__new__(cls, name, bases, attrs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/roland/.pyenv/versions/3.12.7/envs/ai_blog2/lib/python3.12/site-packages/django/forms/models.py", line 334, in __new__
raise FieldError(message)
django.core.exceptions.FieldError: Unknown field(s) (comment_notifications) specified for BlogPage
The definition of my BlogPage class:
@register_snippet
class Author(TranslatableMixin, models.Model):
"""Authors for the blog
Args:
TranslatableMixin (_type_): To make it translateable
DraftStateMixin (_type_): To make it draftable
RevisionMixin (_type_): Let editors to make revisions
PreviewableMixin (_type_): Let editors to preview
models (_type_): Classical Django model
"""
name = models.CharField(max_length=40, blank=False, help_text="Name of this author")
short_bio = RichTextField(blank=True, help_text="Short bio of this author")
twitter_link = models.URLField(max_length=100, blank=True, help_text="Twitter of this author")
linkedin_link = models.URLField(max_length=100, blank=True, help_text="Linkedin of this author" )
tiktok_link = models.URLField(max_length=100, blank=True, help_text="Tiktok of this author")
medium_link = models.URLField(max_length=100, blank=True, help_text="Medium of this author")
portrait = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text="Portrait of this author"
)
seo_title = models.CharField(max_length=70, blank=False, unique=False, help_text="Title shown on Google search when tag listing page returned. Max length is 70.")
seo_meta = models.CharField(max_length=160, blank=False, unique=False, help_text="Meta description shown on Google search when tag listing page returned. Max length is 160.")
panels = [
FieldPanel('name'),
FieldPanel('twitter_link'),
FieldPanel('linkedin_link'),
FieldPanel('tiktok_link'),
FieldPanel('medium_link'),
FieldPanel('short_bio'),
FieldPanel('portrait'),
FieldPanel('seo_title'),
FieldPanel('seo_meta')
]
class Meta:
verbose_name = "Author"
verbose_name_plural = "Authors"
constraints = [
models.UniqueConstraint(fields=('translation_key', 'locale'), name='unique_translation_key_locale_blog_author'),
]
def get_preview_template(self, request, mode_name):
return "blog/previews/advert.html"
def __str__(self):
return self.name
@register_snippet
class MyTag(TranslatableMixin, index.Indexed):
name = models.CharField(max_length=100)
slug = models.SlugField(blank=True)
seo_description = models.TextField(blank=True)
seo_title = models.CharField(max_length=255, blank=True)
og_image = models.ForeignKey(
get_image_model(),
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
panels = [
FieldPanel('name'),
FieldPanel('slug'),
MultiFieldPanel([
FieldPanel('seo_title'),
FieldPanel('seo_description'),
FieldPanel('og_image'),
], heading="SEO Settings")
]
search_fields = [
index.SearchField('name'),
index.SearchField('seo_description'),
]
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def __str__(self):
return self.name
class Meta:
verbose_name = "Tag"
verbose_name_plural = "Tags"
unique_together = [
('translation_key', 'locale'),
('slug', 'locale')
]
# unique_together = [
# ('translation_key', 'locale'),
# ('slug', 'locale'),
#]
# Custom form for the BlogPage that handles tag filtering
class BlogPageForm(WagtailAdminModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Get the instance's locale if it exists (for editing) or the default locale (for new pages)
locale = self.instance.locale if self.instance and hasattr(self.instance, 'locale') else None
if locale:
# Filter tags by locale
filtered_tags = MyTag.objects.filter(locale=locale)
# Set up the autocomplete widget with filtered queryset
self.fields['tags'].widget = AdminTagWidget(
model=MyTag,
queryset=filtered_tags,
)
# Update the queryset for the field itself
self.fields['tags'].queryset = filtered_tags
class BlogPage(SeoMixin, Page):
author = models.ForeignKey(Author, on_delete=models.SET_NULL,null=True, related_name='blog_pages', help_text="Author of this page")
tags = ParentalManyToManyField(MyTag, blank=True, related_name='blog_pages')
intro = RichTextField(blank=True, help_text="Intro of this blog page")
image = models.ForeignKey(
get_image_model(),
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
help_text="Hero image of this blog page"
)
featured = models.BooleanField(default=False, help_text="Featured blog page")
content = StreamField([('blog_content', BlogContentBlock())],
min_num=1,
use_json_field=True)
# Indicate this is article-style content.
seo_content_type = SeoType.ARTICLE
# Change the Twitter card style.
seo_twitter_card = TwitterCard.LARGE
# Provide intro as a fallback of search_description. It the latter is not defined, intro will be used
seo_description_sources = [
"search_description",
"intro",
]
parent_page_types = ['blog.BlogCategoryPage']
subpage_types = []
# Add some basic validation and debugging
def clean(self):
cleaned_data = super().clean()
# Add validation to ensure tags match page locale
if self.locale and self.tags.all():
mismatched_tags = self.tags.exclude(locale=self.locale)
if mismatched_tags.exists():
raise forms.ValidationError({
'tags': _("All tags must be in the same language as the page. "
"Please remove or translate these tags: %s") %
", ".join(tag.name for tag in mismatched_tags)
})
return cleaned_data
base_form_class = BlogPageForm
content_panels = Page.content_panels + [
FieldPanel('image'),
FieldPanel('author'),
FieldPanel('tags'),
FieldPanel('featured'),
FieldPanel('intro'),
FieldPanel('content')
]
promote_panels = SeoMixin.seo_panels
class Meta:
verbose_name = "Blog Page"
verbose_name_plural = "Blog Pages"
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
# Get the current post's category and tags
current_category = self.category
current_tags = set(self.tags.values_list('name', flat=True))
# Get all published blog posts except the current one
all_posts = BlogPage.objects.live().exclude(id=self.id)
# List to hold the related posts with their similarity score
related_posts = []
# Calculate similarity score for each post
for post in all_posts:
post_category = post.category
post_tags = set(post.tags.values_list('name', flat=True))
# Intersection calculation for categories and tags. Later semantics will be used
category_score = 2 if current_category == post_category else 0 # Category gets 2 times more weight
tag_score = len(current_tags.intersection(post_tags))
# Total similarity score
total_score = category_score + tag_score
if total_score > 0:
related_posts.append((post, total_score))
# Sort the related posts by the score in descending order and limit the number (e.g., top 3)
related_posts = sorted(related_posts, key=lambda x: x[1], reverse=True)[:3]
# Add the related posts to the context
context['related_posts'] = [post for post, score in related_posts]
return context
As you can see there is no field name of comment_notification in BlogPage. I double checked its parents (SeoMixin, Page), none of them has field like this. I have no idea, where this field comes from?