How to automaticaly delete images linked to a page after deleting the page? - wagtail

I have an Annonce page with this model:
class AnnoncePage(Page):
date = models.DateField("Date de publication", blank=True, null=True)
description = RichTextField(features=['h2', 'h3', 'bold', 'italic', 'link', 'hr', 'ol', 'ul'], blank=True)
lieu = models.CharField(blank=True, max_length=200)
surface = models.PositiveSmallIntegerField(blank=True, null=True)
nb_pieces = models.PositiveSmallIntegerField(blank=True, null=True)
prix_affiché_index = models.CharField(blank=True, max_length=200, null=True)
conditions_vente_et_prix = RichTextField(blank=True, features=['h2', 'h3', 'bold', 'italic', 'link', 'hr', 'ol', 'ul'])
def main_image(self):
gallery_item = self.gallery_images.first()
if gallery_item:
return gallery_item.image
else:
return None
content_panels = Page.content_panels + [
FieldPanel('date'),
InlinePanel('gallery_images', label="Gallery images"),
FieldPanel('lieu', classname="full"),
FieldPanel('surface', classname="full"),
FieldPanel('nb_pieces', classname="full"),
FieldPanel('description', classname="full"),
FieldPanel('prix_affiché_index', classname="full"),
FieldPanel('conditions_vente_et_prix', classname="full")
]
promote_panels = []
settings_panels = []
max_count = 20
class AnnoncePageGalleryImage(Orderable):
page = ParentalKey(AnnoncePage, on_delete=models.SET_NULL, related_name='gallery_images', null=True)
image = models.ForeignKey(
'wagtailimages.Image', on_delete=models.SET_NULL, related_name='+', null=True
)
panels = [
ImageChooserPanel('image')
]
How can I automaticaly delete the images of the Annonce page after deleting the Annonce page itself ? Do I need to change the model ?

The Wagtail image object can be used in other places. Deleting those automagically might have nasty side effects for other content.
You can add a delete method to AnnoncePage or use a pre_delete signal. You need to iterate over all related gallery images and delete the related Wagtail images. Django leaves the file on disk, so you might want to handle that too.
class AnnoncePage(Page):
...
def delete(self):
for gallery_image in self.gallery_images.all():
# Delete the file on disk here ...
# After, delete the db obj.
gallery_image.image.all().delete()
self.gallery_images.all().delete()
super().delete()
Disclaimer: untested code.
https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-model-methods
https://docs.djangoproject.com/en/dev/ref/signals/#pre-delete
https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.fields.files.FieldFile.delete
Note that the delete method and pre_delete signal are not called on bulk actions. Your milage may vary.

Related

not able register my model to wagtail admin using wagtail_hooks.py

wagtail 4.1 It seems that I did everything as stated in the documentation, but my models do not appear in the admin panel.path
my model:
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
cover_photo = models.ForeignKey(
'wagtailimages.Image',
null=True, blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
panels = [
FieldPanel('title'),
FieldPanel('author'),
FieldPanel('cover_photo')
]
my wagtail_hooks.py
class BookAdmin(ModelAdmin):
model = Book
menu_label = 'Book' # ditch this to use verbose_name_plural from model
menu_icon = 'pilcrow' # change as required
menu_order = 201 # will put in 3rd place (000 being 1st, 100 2nd)
add_to_settings_menu = True # or True to add your model to the Settings sub-menu
exclude_from_explorer = True # or True to exclude pages of this type from Wagtail's explorer view
list_display = ('title', 'author')
list_filter = ('author',)
search_fields = ('title', 'author')
# Now you just need to register your customised ModelAdmin class with Wagtail
modeladmin_register(BookAdmin)
I also registered my applications
INSTALLED_APPS = [
...
"news",
"app",
"wagtail.contrib.modeladmin",
]
what can i miss?

Using Sorted on a set of a QuerySet

I have this Page model:
class BlogPostPage(Page):
date_published= models.DateField('Published date', blank=True, null=True)
intro= models.CharField(max_length=250)
body = RichTextField(blank=True)
header_image = models.ForeignKey('wagtailimages.Image', blank=True, null=True,
on_delete=models.SET_NULL, related_name='+')
tags = ClusterTaggableManager(through='blog.BlogPageTag', blank=True)
categories = ParentalManyToManyField('blog.BlogCategory', blank=True)
def get_context(self, request):
context = super().get_context(request)
all_categories = []
for post in self.get_siblings(inclusive=True):
all_categories +=post.specific.categories.all()
all_categories = sorted(set(all_categories))
context['all_categories']= all_categories
return context
When the page is access I get a TypeError:
TypeError at /blog/xxx/
'<' not supported between instances of 'BlogCategory' and 'BlogCategory'
Inputs on why this is happening and how it can be avoided are requested.
You can define the key parameter for sorted, according to key functions.
But I am not sure, is + allowed in: all_categories +=post.specific.categories.all()?

How add a contact form on homepage wagtail

Hello I am beginner learning wagtail and Django, on my design the contact form is on the homepage and only I founded tutorial for beginners to include a contact form in a dedicated contact page, how I can include a contact form on my homepage?
My model homepage looks like this:
class HomePage(Page):
description = models.CharField(max_length=255, blank=True,)
content_panels = Page.content_panels + [ FieldPanel('description', classname="full") ]
in a tutorial they use this model:
class FormField(AbstractFormField):
page = ParentalKey('FormPage', related_name='custom_form_fields')
class FormPage(AbstractEmailForm):
thank_you_text = RichTextField(blank=True)
content_panels = AbstractEmailForm.content_panels + [
InlinePanel('custom_form_fields', label="Form fields"),
FieldPanel('thank_you_text', classname="full"),
MultiFieldPanel([
FieldRowPanel([
FieldPanel('from_address', classname="col6"),
FieldPanel('to_address', classname="col6"),
]),
FieldPanel('subject'),
], "Email Notification Config"),
]
def get_form_fields(self):
return self.custom_form_fields.all()
but this creates another page that I don't need. How I can add the form on my homepage?
Thank you very much.
(Untested) Instead of this from the tutorial:
class FormField(AbstractFormField):
page = ParentalKey('FormPage', related_name='custom_form_fields')
do:
class FormField(AbstractFormField):
page = ParentalKey('HomePage', related_name='custom_form_fields')
and then change the HomePage definition to:
class HomePage(AbstractEmailForm, Page):
description = models.CharField(max_length=255, blank=True,)
thank_you_text = RichTextField(blank=True)
content_panels = AbstractEmailForm.content_panels + Page.content_panels + [
FieldPanel('description', classname="full"),
InlinePanel('custom_form_fields', label="Form fields"),
FieldPanel('thank_you_text', classname="full"),
MultiFieldPanel([
FieldRowPanel([
FieldPanel('from_address', classname="col6"),
FieldPanel('to_address', classname="col6"),
]),
FieldPanel('subject'),
], "Email Notification Config"),
]
def get_form_fields(self):
return self.custom_form_fields.all()

Second page = ParentalKey() definition, to use one snippen in different Page models

I defined two Relationship(Orderable, models.Model) to be able to use one modelsnippet inside differen Page models like:
class GroupstageTournamentModel(models.Model):
...
class GroupstageTournamentRelationship(Orderable, models.Model):
page = ParentalKey('TournamentPage',
related_name='groupstage_tournament_relationship')
match = models.ForeignKey('GroupstageTournamentModel',
related_name='match_tournament_relationship')
panels = [
PageChooserPanel('match')
]
class MatchesScreencastRelationship(Orderable, models.Model):
page = ParentalKey('ScreencastPage',
related_name='groupstage_screencast_relationship')
match = models.ForeignKey('GroupstageTournamentModel',
default="", related_name='match_screen_relationship')
panels = [
PageChooserPanel('match')
]
class TournamentPage(Page):
starts_at = models.DateTimeField(blank=True)
ends_at = models.DateTimeField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('title'),
FieldPanel('starts_at'),
FieldPanel('ends_at'),
InlinePanel(
'groupstage_tournament_relationship', label="Group game:",
panels=None, min_num=1),
]
def __str__(self):
return self.title
class ScreencastPage(Page):
content_panels = Page.content_panels + [
FieldPanel('title'),
InlinePanel(
'groupstage_screencast_relationship', label="Playing First",
panels=None, max_num=1),
]
parent_page_types = ['home.HomePage']
subpage_types = []
def __str__(self):
# return self.title
return '{} \n Nächste: {}'.format(self.groupstage_relationship, self.final_phase_relationship)
As you can see my idea was to use one of them insite TournamentPage and another inside ScreencastPage. If i do that this way i get this error:
ERROR:
django.core.exceptions.FieldError: Local field 'id' in class 'GroupstageTournamentRelationship' clashes with field of the same name from base class 'GroupstageTournamentModel'.
how can this problem be solved? Is it possible to add somehow the second ParentalKey for relationship with ScreencastPage and use it direct inside GroupstageTournamentModel like you did in backerydemo?
UPDATE
I changed GroupstageTournamentModel from model.Model to ClusterableModel and i changed related_name so that it different in both of related_name's
related_name='groupstage_tournament_relationship' and related_name='groupstage_screencast_relationship'. I did migrations again and got the same error. Here is my GroupstageTournamentRelationship class:
class GroupstageTournamentModel(ClusterableModel):
number = models.PositiveSmallIntegerField(
help_text="Add the unique number of this Match.")
starts_at = models.DateTimeField()
# Team 1
team_1 = models.ForeignKey(
TeamRooster,
null=True, blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
team_1_dress = ColorField(default='#ff0000', blank=True)
team_1_first_halftime_score = models.PositiveSmallIntegerField(blank=True, default="")
# Team 2
team_2 = models.ForeignKey(
TeamRooster,
null=True, blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
team_2_dress = ColorField(default='#0066ff', blank=True)
team_2_first_halftime_score = models.PositiveSmallIntegerField(blank=True, default="")
panels = [
FieldPanel('number', classname="6"),
FieldPanel('starts_at', classname="6"),
MultiFieldPanel([
FieldRowPanel([
FieldPanel('team_1', classname="6"),
FieldPanel('team_1_dress', classname="6"),
FieldPanel('team_1_first_halftime_score', classname="3"),
]),
], classname="full", heading="Team 1"),
MultiFieldPanel([
FieldRowPanel([
FieldPanel('team_2', classname="6"),
FieldPanel('team_2_dress', classname="6"),
FieldPanel('team_2_first_halftime_score', classname="3"),
]),
], classname="full", heading="Team 2"),
]
def __str__(self):
return '{} vs {} {} - {}'.format(self.team_1, self.team_2, self.starts_at, self.number)
class Meta:
verbose_name = 'Gruppenphase Spiel'
verbose_name_plural = 'Gruppenphase'
UPDATE
In migration file i found following, and Im not sure what happens if I remove that:
UPDATE
I shortened the model and added "TournamentPage" for clarity.
I deleted my migration files included 0001_inicial.py and did again py manage.py makemigrations and it all worked !

Nested categories/InlinePanel(s) in wagtail

I struggle to implement something like "nested categories":
PageA:
- Cat1
- SubCat1
- SubCat2
- ...
- Cat2
- SubCat1
- ...
All categories and subcategories should be orderable and editable by an editor.
My guess was something like this:
class CategoryTestPage(Page):
content_panels = Page.content_panels + [
InlinePanel('categories')
]
class Category(Orderable,ClusterableModel,models.Model):
page = ParentalKey(CategoryTestPage, related_name='category')
category = models.CharField(max_length=250)
def __str__(self):
return "%d %s" % (self.id, self.category)
panels = [
FieldPanel('category'),
InlinePanel('subcategory')
]
class SubCategory(Orderable,models.Model):
category = ParentalKey(ProjektOrdnung, related_name='subcategory')
subcategory = models.CharField(max_length=250)
def __str__(self):
return "%d %s" % (self.id, self.subcategory)
panels = [
FieldPanel('subcategory')
]
But this results in 'CategoryForm' object has no attribute 'formsets'. It seems nested InlinePanels are the problem?
Further I need this "hierarchical taxonomy" for assigning some of these categories/subcategories to other pages:
PageB:
- has Cat1
- has SubCa2
- ...
... which looks a lot like hierarchical tags...
Any ideas how to implement this or what's wrong with my implementation?
Kind regards,
tombreit
PS: I'm on wagtail 1.2rc1
Here's one way to do it, with much room for interface improvements ;) In order to sort the categories at the page level, I'd suggest the use of django-sortedm2m.
from wagtail.wagtailcore.models import Orderable, Page
from wagtail.wagtailsnippets.models import register_snippet
from django.db import models
#register_snippet
class Category(models.Model):
name = models.CharField(
max_length=80, unique=True, verbose_name=_('Category Name'))
slug = models.SlugField(unique=True, max_length=80)
parent = models.ForeignKey(
'self', blank=True, null=True, related_name="children",
help_text=_(
'Categories, unlike tags, can have a hierarchy. You might have a '
'Jazz category, and under that have children categories for Bebop'
' and Big Band. Totally optional.')
)
description = models.CharField(max_length=500, blank=True)
class Meta:
ordering = ['name']
verbose_name = _("Category")
verbose_name_plural = _("Categories")
panels = [
FieldPanel('name'),
FieldPanel('parent'),
FieldPanel('description'),
]
def __str__(self):
return self.name
def clean(self):
if self.parent:
parent = self.parent
if self.parent == self:
raise ValidationError('Parent category cannot be self.')
if parent.parent and parent.parent == self:
raise ValidationError('Cannot have circular Parents.')
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
return super(Category, self).save(*args, **kwargs)
class CategoryPage(models.Model):
category = ParentalKey('Category', related_name="+", verbose_name=_('Category'))
page = ParentalKey('MyPage', related_name='+')
panels = [
FieldPanel('category'),
]
class MyPage(Page):
categories = models.ManyToManyField(Category, through=CategoryPage, blank=True)
content_panels = Page.content_panels + [
FieldPanel('categories'),
]

Resources