wagtail: Can't add ManytoMany fields to a Page

For example

class BlogPage(Page):
    body = RichTextField()
    categories = models.ManyToManyField(BlogCategory, blank=True)

One gets ValueError “<BlogPage: foo>” needs to have a value for field “blogpage” before this many-to-many relationship can be used.

It’s caused by this https://github.com/torchbox/django-modelcluster/issues/3 https://github.com/torchbox/django-modelcluster/blob/master/modelcluster/forms.py#L260

I’ll keep trying to think of a solution. Seems like a common use case as Wordpress implements categories as a M2M (you can pick multiple, but they are predefined unlike a tag).

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Reactions: 3
  • Comments: 25 (9 by maintainers)

Commits related to this issue

Most upvoted comments

This is now implemented in django-modelcluster: https://github.com/torchbox/django-modelcluster/pull/62

From initial tests, it seems to be working nicely with Wagtail: just use ParentalManyToManyField instead of ManyToManyField, and specify it in a FieldPanel as normal.

Things that need to be done before the release of Wagtail 1.9:

  • release django-modelcluster 3.0 final and update wagtail’s setup.py to require django-modelcluster>=3.0,<4.0
  • add a release note to document the updated django-modelcluster / django-taggit dependencies (since it’s somewhat likely that people will have the old versions hard-coded in their requirements files)
  • ensure that M2M relations are covered by tests
  • document the use of ParentalManyToManyField, possibly through a new ‘categories’ section in the tutorial (which was dropped from https://github.com/wagtail/wagtail/pull/3125#issuecomment-259181336 because it was too messy to implement without real M2M support) as well as http://docs.wagtail.io/en/v1.8/topics/pages.html

I’ve just come up against exactly the scenario bufke raised in the initial comment - the need to provide an interface for adding categories to blog posts. The workaround using an inline is not very pretty, and tags don’t quite fulfil this particular use case. Is there really no proper M2M support in Wagtail? If not, can I add a massive +1 for this to be addressed please.

Hi @ababic,

A nice form field for M2M relations is something that we’re going to want either way, and up to now I’d anticipated that it would be the second stage of development, after the modelcluster updates. However, if you think it’s something that could usefully be done now, using ‘through’ models as an interim solution, then that would certainly be welcome.

On the other hand, it may be that with modelcluster’s M2M support in place, the UI work simply reduces to “use ModelMultipleChoiceField”… in which case it probably is better to wait for modelcluster. That task is gradually rising to the top of my todo list, and will hopefully get done in the next two weeks or so.

I looked at it, but adding M2M support to modelcluster is beyond me. Here is a workaround for anyone with this issue.

You can create something that is database equivalent to a M2M in django by just making the “through” model yourself.

class BlogCategoryBlogPage(Orderable, models.Model):
    category = models.ForeignKey(BlogCategory, related_name="+")
    page = ParentalKey('gkblog.BlogPage', related_name='categories')
    panels = [
        FieldPanel('category'),
    ]

Add it as an inline - InlinePanel(BlogPage, 'categories', label="Categories"),

This works just like in Django admin. The UI is a bit more cumbersome using with method.

@kofic No (but I’m working on it) - there’ll be an update here when there’s any news.

That’s great to hear! Can’t wait for this.

@tomdyson Proper M2M support is definitely a desirable feature - creating the ‘through’ model manually is more of a workaround than a recommended approach. The lack of M2M support in django-modelcluster was also the root cause of #197 - under the hood Taggit’s tags are an M2M relation, and at first glance it seemed that their behaviour was close enough to modelcluster’s foreign key relations that they would Just Work… unfortunately that wasn’t the case, hence the need for the workaround in #500.