1
votes

I have two different page models (no subclassing, separate apps with only similar fields being common ones like "model name", "id", "parts") , let's say, cars and motorcycles. I'm making a separate page with a table of their parts (which also needs to contain columns like "id" which i assume can be a pk,"web store link", and "used by" which will show all Bike and Car models that use the part); Since they can share a few of the same parts, I want for them to be connected to the same Tag model (instead of having "CarPageTag" and "BikePageTag"); What I've tried so far :

  • I tried to make a separate app for "parts". I figured how to include that class from other app and it works with either car or motorcycle, but not both, because of this error:

AssertionError: ParentalKey(['CarsBlogApp.CarDetailPage', 'BikesBlogApp.BikeDetailPage']) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string 'self'

  • I had a simple solution working in plain Django app via ManyToManyField (but I need the wagtail autofill tag selection page in admin)
  • I looked all over django, wagtail and taggit docs
  • I looked through all youtube tutorials

Edit: adding models.py the way I thought It'll work:

PartsApp/models.py:

from django.db import models
from wagtail.core.models import Page
from wagtail.admin.edit_handlers import FieldPanel
from modelcluster.fields import ParentalKey
from taggit.models import TaggedItemBase

class PartsPage(Page):

    templates = "parts/parts_page.html"
    subpage_types = []
    max_count = 1
    parent_page_type = ['home.HomePage']
    paragraph = models.CharField(
        max_length=100,
        blank=False,
        null=False,
        help_text='Overwrites the default title',
    )

    content_panels = Page.content_panels + [
        FieldPanel("paragraph"),
    ]

    class Meta:
        verbose_name = "Needed supplies Page"
        verbose_name_plural = "Needed supplies Pages"

class PartTagPage(TaggedItemBase):
    content_object = ParentalKey(
        ['CarApp.CarDetailPage','BikeApp.BikeDetailPage'],
        related_name='tagged_items',
        on_delete=models.CASCADE,
    )

And CarsApp/models.py:

from django.db import models
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
from wagtail.core.fields import StreamField
from wagtail.core.models import Page
from wagtail.images.edit_handlers import ImageChooserPanel
from blocks import blocks
from modelcluster.fields import ParentalKey
from modelcluster.contrib.taggit import ClusterTaggableManager
from taggit.models import TaggedItemBase
from PartsApp.models import PartTagPage

class CarListingPage(Page):
    template = "CarsApp/car_listing_page.html"
    subpage_types = ['CarsApp.CarDetailPage']
    parent_page_type = ['home.HomePage']
    max_count = 1
    paragraph = models.CharField(
        max_length=100,
        blank=False,
        null=False,
        help_text='Overwrites the default title',
    )

    content_panels = Page.content_panels + [
        FieldPanel("paragraph"),
    ]

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        all_cars = CarsDetailPage.objects.live().public().order_by('-first_published_at')

        if request.GET.get('tag', None):
            tags = request.GET.get('tag')
            all_cars = all_cars.filter(tags__slug__in=[tags])

        context["cars"] = all_animals
        return context

class CarDetailPage(Page):
    subpage_types = []
    parent_page_types = ['CarsApp.CarsListingPage']
    tags = ClusterTaggableManager(through='PartsApp.PartTagPage', blank=True)
    name = models.CharField(
        max_length=100,
        blank=False,
        null=False,
    )
    model = models.CharField(
        max_length=100,
        blank=False,
        null=False,
    )
    car_image = models.ForeignKey(
        "wagtailimages.Image",
        blank=False,
        null=True,
        related_name="+",
        on_delete=models.SET_NULL,
    )
    description = models.TextField(max_length=10000)

    content_panels = Page.content_panels + [
        FieldPanel("model"),
        FieldPanel("name"),
        FieldPanel("description"),
        FieldPanel("tags"),
        ImageChooserPanel("car_image"),
    ]

The BikesApp/models.py is pretty much the same. Again, these do not work.

1
Your ParentalKey definition isn't right. Just like ForeignKey, ParentalKey can only point to one model. Without seeing more of what you're trying to do (you should post your models.py), it's hard to help, but if you had ManyToManyField working in plain Django, then you might need Wagtail's ParentalManyToManyField: docs.wagtail.io/en/latest/releases/…Dan Swain
@DanSwain thank you, I added my models.py. Sorry I'm relatively new to programming in general as well as python, django and wagtail, so my questions may sound facepalm-y, but: it seems like ParentalManyToManyField is for categories, not tags? I need the admin functionality to have pretty wagtail autocomplete picker\deleter but also to have a popup for filling additional tag fields like in simplest django.illevens

1 Answers

4
votes

Setting up separate CarPageTag and BikePageTag models is the solution you want (assuming they're subclasses of TaggedItemBase, as per the pattern shown in the Wagtail documentation).

TaggedItemBase is not a tag model itself - it just defines the relation between the page model and the tag model (which, in this case, is taggit.Tag, the standard tag model provided as default by the django-taggit library). Therefore, CarPageTag is setting up a relation between CarPage and taggit.Tag, and BikePageTag is setting up a relation between BikePage and taggit.Tag - both are using the same tag model.

If you did want cars and bikes to maintain their own independent set of tags, you'd need a different pattern - custom tag models.