Джанго использовать многие ко многим с помощью в форме - PullRequest
0 голосов
/ 26 июня 2018

У меня есть модель данных, в которой я использую промежуточную таблицу вручную для отношения m2m. Опираясь на классический пример из django doc:

from django.db import models

INSTRUMENT_CHOICES = (
    ('guitar', 'Guitar'),
    ('bass', 'Bass Guitar'),
    ('drum', 'Drum'),
    ('keyboard', 'Keyboard'),
)

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Leadership')

    def __str__(self):
        return self.name

    def get_leadership():
        return self.leadership_set.first()


class Leadership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    instrument = models.CharField('Playing Instrument', choices=INSTRUMENT_CHOICES, 
                                   max_length=15, 
                                   null=True, 
                                   blank=False)
    class Meta:
        unique_together = ('person', 'group')

Когда я создаю новую группу, я также хочу указать, кто будет лидером, и для этого отношения также укажите, на каком инструменте он будет играть в этой группе.

Что действительно смущает меня, учитывая также отсутствие документации по этой теме, так это то, как обрабатывать такого рода отношения в формах.

Это форма, с которой я пришел:

class InstrumentField(forms.ChoiceField):
    def __init__(self, *args, **kwargs):
        super().__init__(INSTRUMENT_CHOICES, *args, **kwargs)


class GroupForm(forms.ModelForm):
    instrument = InstrumentField(required=False)

    class Meta:
        model = Group
        fields = ['name', 
                  'members'
                  'instrument']  # This works but it's not correctly initalized in case of edit form
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.instance.pk is not None:  # editing 
            # PROBLEM: this doesn't work
            self.fields["instrument"].initial = self.instance.get_leadership().instrument

    def save(self, commit=True):
        group = super().save(commit=False)
        if commit:
            group.save()
            if 'instrument' in self.changed_data:
                leader = self.cleaned_data.get('members').first()  
                instrument = self.cleaned_data['instrument']

                Leadership.objects.update_or_create(person=leader, group=group, defaults={'instrument': instrument})

        return group

Как указано в django doc, я вручную создаю Leadership объекты (см. Форму save метод).

Я не смог решить, как заполнить поле instrument в случае редактирования формы. Я пытаюсь сделать это в __init__: сначала я проверяю, что мы находимся в режиме «редактирования» (экземпляр имеет pk), затем я получаю соответствующий объект Leadership (см. Group.get_leadership), и из этого я извлекаю instrument и я назначаю его на fields["instrument"].initial.

Это не работает.

Я мог бы проверить, что значение initial было установлено, но затем, когда я отображаю форму, отображается значение выбора по умолчанию (первое значение INSTRUMENT_CHOICES).

Что мне здесь не хватает? Есть ли лучший способ или лучший документ о том, как обрабатывать m2m с моделями от до в формах?

...