Django admin many to many subset

2019-08-05 20:25发布

问题:

I'm trying to integrate in Django admin the next three related models:

# models.py
class Seminar(models.Model):
    title = models.CharField(max_length=128, unique=True)
    start_date = models.DateField(db_index=True)
    end_date = models.DateField(db_index=True)


class Event(models.Model):
    title = models.CharField(max_length=128)
    start_date = models.DateTimeField(db_index=True)
    end_date = models.DateTimeField(db_index=True)
    seminar = models.ForeignKey('Seminar')


class Registration(models.Model):
    name = models.CharField(max_length=128)
    first_name = models.CharField(max_length=128)
    seminar = models.ForeignKey('Seminar')
    events = models.ManyToManyField('Event', null=True)


# admin.py
class EventInline(admin.TabularInline):
    model = Event


class SeminarAdmin(admin.ModelAdmin):
    list_display = ('title', 'start_date', 'end_date')
    inlines = [
        EventInline,
    ]


class RegistrationAdmin(admin.ModelAdmin):
    list_display = ('seminar', 'name', 'first_name')

As you can see, each seminar may have several events, added from the Seminar admin as inline entries.

My problem is with registrations, since they are:

  • related to a seminar
  • can "subscribe" to several events of this seminar

Of course, the admin lists all events and not the subset related to the seminar, so:

  • is it possible to achieve this from the admin (with as low tweaks as possible)?
  • is the Registration M2M on Event appropriate or should I relate this two models in a different way?

Thanks!

回答1:

Well so, just before asking here, I did something similar to Django's auth.User model by using different forms for add/change views. I hoped there was a better solution, obviously not.

So here is what I did:

# models.py
class Registration(models.Model):
    # [...] - add form excludes it, should allow blank on events field
    events = models.ManyToManyField('Event', blank=True, null=True)


# admin.py
class RegistrationAdmin(admin.ModelAdmin):
    change_form = RegistrationChangeForm

    def get_form(self, request, obj=None, **kwargs):
        defaults = {}
        if obj is None:
            defaults.update(exclude=('events',))
        else:
            defaults.update(form=self.change_form)
        defaults.update(kwargs)
        return super(RegistrationAdmin, self).get_form(request, obj, **defaults)


# forms.py
class RegistrationChangeForm(forms.ModelForm):
    class Meta:
        model = Registration
        exclude = ('seminar',)

    def __init__(self, *args, **kwargs):
        super(RegistrationChangeForm, self).__init__(*args, **kwargs)
        self.fields['events'].choices = Event.objects.filter(seminar=kwargs.get('instance').seminar).values_list('pk','title')

So, when adding we simply ignore events (registration is saved with nulled events) and then, on change a list of events can be selected based on the previously defined seminar which is then ignored (since we can't reload the related events list).