1

What is the right way to set the default value of a many2many field? This is what I tried below but it is not working. I know that I could override the save method, but wouldn't that be called every time the model gets updated? I want to only set the initial values of the model every time an instance is created.

def default_values():
     return [c.id for c in SomeOtherModel.objects.filter(otherfield__isnull = True)]


class SomeModel(models.Model):
    somefield = models.ManyToManyField('SomeField', default=default_values)
    semeotherfield = models.ForeignKey('SomeOtherField')

I am using django 1.8

Jiyda Moussa
  • 925
  • 2
  • 9
  • 26

4 Answers4

4

The method, mentioned in the question, is not working because ManyToManyField can not have a value when being saved (values must be added with the RelatedManager). So the default option for a ManyToManyField is not used when saving the object (when calling <object>.save(), or other): even if it is set (e.g. to a callable returning an object list), the field will be set to an empty list when saving.

According to this ticket:

default option value is used in Django Form set up to edit this object. In particular, in Django Admin, the multivalue drop-down will include for the field, that will contain all the objects returned by the callable defined as the default value - and they will be all selected. If the user saves the object without any edit (by pushing the form's "save" button), then the field will indeed be set to all the values.

Simon B
  • 101
  • 1
  • 3
2

You can override save method, and inside that insert a check if primary key is empty or not. If it's empty - this is creation of object.

Also you can connect to post save signal - there is attribute telling you if this is creation of object or not.

GwynBleidD
  • 20,081
  • 5
  • 46
  • 77
1

You can override the init method of the model but that wouldnot allow you to set m2m.

you can override the init of the modelform like:

def __init__(self, *args, **kwargs):
    if 'initial' not in kwargs:
        kwargs['initial'] = {}
    kwargs['initial'].update(product_template_admin_initial_values())
    super(ProductTemplateAdminForm, self).__init__(*args, **kwargs)

But that will not allow to use the request.

If you depend on the request you can use the admin get_changeform_initial_data

def get_changeform_initial_data(self, request):
    return {'name': 'custom_initial_value'}

You can call a method with the request as argument and return a dictionary. the m2m default should be a list

0

If you just want to set a default in the admin page for a M2M list, you can do something like this. For example, to set the default of managed_by to the current user:

class EventAdmin(admin.ModelAdmin):
    def get_changeform_initial_data(self, request: HttpRequest):
        defaults = super().get_changeform_initial_data(request)
        defaults['managed_by'] = [request.user]
        return defaults

Here we also call super().get_changeform_initial_data so that we don't loose the defaults that Django sets.

winrid
  • 89
  • 2
  • 10