Opened 9 years ago

Last modified 7 months ago

#2750 new Bug

ManyToManyField ignores 'default' option

Reported by: bangcok_dangerus@… Owned by: nobody
Component: Database layer (models, ORM) Version:
Severity: Normal Keywords:
Cc: orzel@…, chrischambers, seler, chris+django@…, anubhav9042@…, josh.smeaton@… Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

It doesn't seem possible to use the 'default' option to pre-select values in the admin site for a ManyToManyField. I've tried a string value, a list of strings, and even (following a recommendation from someone on #django) passing a list of the actual objects. It would be nice to either have this functionality added or to update the docs to reflect the fact that 'default' has no effect on ManyToManyFields.

-Basil

Change History (21)

comment:1 Changed 8 years ago by SmileyChris

  • Component changed from Core framework to Database wrapper
  • Triage Stage changed from Unreviewed to Design decision needed

The decision is whether the functionality should be added or the docs should just be updated.

comment:2 Changed 7 years ago by Guilherme M. Gondim (semente) <semente@…>

I need this :

publish_on = models.ManyToManyField(Site, verbose_name=_('publish on'), default=Site.objects.get_current)

:-P

comment:3 Changed 7 years ago by jacob

  • Triage Stage changed from Design decision needed to Someday/Maybe

comment:4 Changed 7 years ago by trbs

I've created a patch for getting some default behavior in the admin for many to many fields.
Actually i had the same use case as semente. When using multiple sites the default site is not automatically selected. And it's quite a nag to have to select it each and every time. So when using the below ManyToManyFieldWithDefault you can use it as:

publish_on = ManyToManyFieldWithDefault(Site, verbose_name=_('publish on'), default=Site.objects.get_current)

And it will automatically select the current_site when no other sites are selected. (Except for when blank=True and null=True)

from django import oldforms
from django.db import models
from django.utils.functional import curry

class SelectMultipleFieldWithDefault(oldforms.SelectMultipleField):
    def __init__(self, default_choice=None, *args, **kwargs):
        self.__default_choice = default_choice
        super(SelectMultipleFieldWithDefault, self).__init__(*args, **kwargs)
        
    def render(self, data):
        if not data and self.__default_choice:
            data = [self.__default_choice.id]
        return super(SelectMultipleFieldWithDefault, self).render(data)

class ManyToManyFieldWithDefault(models.ManyToManyField):
    def __init__(self, *args, **kwargs):
        if not kwargs.get('blank', False) and not kwargs.get('null', False):
            self.__default_choice = kwargs.get('default', None)
        else:
            self.__default_choice = None
        super(ManyToManyFieldWithDefault, self).__init__(*args, **kwargs)
        
    def get_manipulator_field_objs(self):
        if self.rel.raw_id_admin:
            return [oldforms.RawIdAdminField]
        else:
            choices = self.get_choices_default()
            return [curry(SelectMultipleFieldWithDefault, size=min(max(len(choices), 5), 15), choices=choices, default_choice=self.__default_choice)]

comment:5 Changed 7 years ago by Guilherme M. Gondim <semente@…>

I can thus: default=str(Site.objects.get_current().id)

but it is strange, why str?

(in django 1.0)

comment:6 follow-up: Changed 7 years ago by Guilherme M. Gondim <semente@…>

hmm, but the manner above doesn't works when save (obviously), only in admin interface. :-P

comment:7 in reply to: ↑ 6 Changed 7 years ago by Guilherme M. Gondim <semente@…>

Replying to Guilherme M. Gondim <semente@taurinus.org>:

hmm, but the manner above doesn't works when save (obviously), only in admin interface. :-P

Please, ignore this. The tip above works (#5), my problem was with a bug in my code.

comment:8 Changed 6 years ago by orzel

  • Cc orzel@… added

I have the same problem here. I have a set of strings i want the user to select from, in the admin interface. By default i want all strings to be selected. (the user needs to 'opt-out', not 'opt-in').
if in my model i do

stuff = models.ManyToManyField(Stuff, default=Stuff.objects)

or

stuff = models.ManyToManyField(Stuff, default=Stuff.objects.all())

then i get randomly choosen selected items from Stuff. Rather weird : it's not all, it's not none (as where there's no default=), but just random...

comment:9 Changed 4 years ago by subsume

+1 in favor of documentation update to reflect the status of default and null in relation to M2Ms.

With signals and a million other tools its easy to get this default and I'm not certain that I agree with the meaning or obviousness of 'default' when it comes to this field.

comment:10 Changed 4 years ago by chrischambers

  • Cc chrischambers added

comment:11 Changed 4 years ago by lrekucki

  • Severity changed from normal to Normal
  • Type changed from defect to Bug

comment:12 Changed 4 years ago by seler

  • Cc seler added

comment:13 Changed 3 years ago by aaugustin

  • UI/UX unset

Change UI/UX from NULL to False.

comment:14 Changed 3 years ago by aaugustin

  • Easy pickings unset

Change Easy pickings from NULL to False.

comment:15 Changed 3 years ago by gcc

  • Cc chris+django@… added

comment:16 Changed 15 months ago by anubhav9042

  • Cc anubhav9042@… added

comment:17 Changed 7 months ago by jarshwah

https://github.com/django/django/pull/3108

I'm of the opinion that default for ManyToManyField doesn't make sense. The default value of a field is used when a model is saved without a user value. M2M fields can not have a value when being saved (values must be added with the RelatedManager), so the default value would always be applied if implemented.

I think the underlying problem of pre-selecting values in the admin for m2m form fields is orthogonal to whether or not m2m model fields should respect the default option.

comment:18 Changed 7 months ago by jarshwah

  • Has patch set
  • Triage Stage changed from Someday/Maybe to Accepted

comment:19 Changed 7 months ago by jarshwah

  • Cc josh.smeaton@… added

comment:20 Changed 7 months ago by timgraham

  • Patch needs improvement set

There are failing tests and usage of this in Django's test suite that need to be removed.

comment:21 Changed 7 months ago by jarshwah

  • Has patch unset

While I don't think default makes sense for m2m models, it appears that forms make use of the default option, so we can't simply check/warn and document. This will involve some deeper investigation with regards to how forms use the m2m default. I'll try to look into this a little more.

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