Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#30260 closed Bug (invalid)

ManyToManyField with a non-list default value has different behavior in an inlined Admin vs a standalone Admin

Reported by: Merlijn Owned by: nobody
Component: contrib.admin Version: 2.1
Severity: Normal Keywords: ManyToManyField, Inline, Admin
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

First off, I'm new to this bug-tracker and I'm by no means a Django expert, I just use Django casually in my free time. So, I'm sorry if this doesn't belong in the bug tracker. Anyway, on to the problem I encountered.

In short: there seems to be a different behavior for a ManyToManyField with a non-list (integer) default value when it's in an Admin Inline vs when it's in its own Admin page. In fact, in an Inline Django crashes on save whereas on a non-inline it saves just fine.

I encountered this problem in a personal project and to check this problem I made a small test-project to test it and also help explain the problem.

Consider the following model.py:

from django.db import models


class Author(models.Model):
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name


class Series(models.Model):
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name


class Book(models.Model):
    title = models.CharField(max_length=255)
    series = models.ForeignKey(to=Series, on_delete=models.CASCADE)
    author = models.ManyToManyField(to=Author, default=3)

    def __str__(self):
        return self.title

And the following admin.py:

from django.contrib import admin

from stack.models import Book, Series, Author


class BookInline(admin.StackedInline):
    model = Book
    extra = 0


class SeriesAdmin(admin.ModelAdmin):
    inlines = [
        BookInline
    ]


admin.site.register(Book)
admin.site.register(Series, SeriesAdmin)
admin.site.register(Author)

(Note: the above example assumes you have at least 3 authors already created in the database to work properly).

Now if I were to run the above application and go to the Book admin page and create a new Book, the default value of Author works fine; the 3rd author is visibly selected and on saving it gets saved no problem whatsoever. However... If I were to go the the Series admin page (which has a Book inline) and create a new Series, the Author field seems to work just fine; the 3rd Author is selected. But, on save it crashes with the following error:

object of type 'int' has no len()

Which originates from django\forms\models.py in has_changed, line 1351.

And it doesn't matter whether I manually select another Author, the save always crashes (unless I don't any any Book/Inline to the Series object).
Not that editing and saving a Series object with already existing Book inlines works fine (unless I try to add a new Book to the Series).

The solution to the crash is simple. Instead of default=3 on the Book model, we can change it to default=[3] and everything works like a charm.

Now, I don't know if a non-list default value for a ManyToManyField is something that should be possible or is actually something that should not be possible, but happens to work in some cases nonetheless. However, I do think the difference in behavior between the non-inline and the inline should be the same, whether that is a crash or a functioning save.

Change History (2)

comment:1 by Carlton Gibson, 5 years ago

Resolution: invalid
Status: newclosed

Hi. Thanks for the report.

Instead of default=3 on the Book model, we can change it to default=[3] and everything works like a charm.

You've hit the nail on the head here. A scalar value doesn't make sense as the default for a ManyToManyField.

Possibly we could add some kind of check that default values had sensible values, but it's not at all clear that would be worth the effort, we're in a dynamic language so this kind of thing can happen and, short of a concrete suggestion this isn't really actionable as is.

As such I'm going to close this. I hope that makes sense.

comment:2 by Tim Graham, 5 years ago

Component: Uncategorizedcontrib.admin

See also #2750 which aims to clarify how a default works for ManyToManyField.

Note: See TracTickets for help on using tickets.
Back to Top