Как изменить значения в m2m-поле в форме? (Django)

У меня есть 2 модели в моем models.py. Это Doctor и Specialty:

class Doctor(models.Model):
    name = CharField(max_length=255)
    specialties = ManyToManyField('Specialty', related_name='doctors')
    mc_id = CharField(max_length=255, unique=True)

class Specialty(models.Model):
    name = CharField(max_length=255)
    mc_id = CharField(max_length=255, unique=True)

    def __str__(self):
        return self.name

При создании нового экземпляра доктора через интерфейс администратора (шаблон change_form.html) я вижу поля модели Doctor, которые я должен заполнить данными. Среди этих полей есть поле специальности ("Cpeциальности"), которое является m2m полем и содержит имена (как я установил __str__(self) метод в модели Specialty для возврата self.name) для specialties, которые были ранее добавлены в Specialty модель ( см. скрин).

А я хочу видеть в этом списке не только названия специальностей, но и значения поля mc_id модели Specialty, но сделать это за счет чего-то подобного - не мое дело:

class Specialty(models.Model):
    name = CharField(max_length=255)
    mc_id = CharField(max_length=255, unique=True)

    def __str__(self):
        return self.name + '(' + self.mc_id + ')'

Я думал, что смогу сделать это через переопределение метода formfield_for_manytomany() в моей модели администратора (связанной с моделью Doctor) в admin.py:

@admin.register(Doctor)
class DoctorAdmin(admin.ModelAdmin):
    def formfield_for_manytomany(self, db_field, request, **kwargs):
        if db_field.name == "specialties":
            queryset = Specialty.objects.all()
            for specialty in queryset:
                specialty.name += '(' + {specialty.mc_id} + ')'
        return super().formfield_for_manytomany(db_field, request, **kwargs)

Это не сработало. Я хотел бы знать, как я могу решить мою проблему. Спасибо заранее.

Просто используйте format() в моделях

def __str__(self):
    return '{} {}'.format(self.name, self.mc_id)

Я думаю, что перезапись метода label_from_instance в поле формы может сработать для вас.



class SpecialtyModelMultipleChoiceField(forms.ModelMultipleChoiceField):

    def label_from_instance(self, obj):
        obj.name + '(' + obj.mc_id + ')'


class DoctorModelForm(forms.ModelForm):
    specialties = SpecialtyModelMultipleChoiceField(
        Specialty.objecs.all
        # ....
    )
    
    class Meta:
        model = Doctor
        fields = '__all__'


@admin.register(Doctor)
class DoctorAdmin(admin.ModelAdmin):
    form = DoctorModelForm
    # ....

Я хотел бы поделиться альтернативной реализацией отличной идеи Тонио

# fields.py

class SpecialtyModelMultipleChoiceField(forms.ModelMultipleChoiceField):

    def label_from_instance(self, obj):
        obj.name + '(' + obj.mc_id + ')'
# admin.py

class BaseAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        form = super().get_form(request, obj, **kwargs)
        # some logic
        return form

@admin.register(Doctor)
class DoctorAdmin(BaseAdmin):
    def formfield_for_manytomany(self, db_field, request, **kwargs):
        if db_field.name == "specialties":
            queryset = Specialty.objects.all().filter(mc_id=request.user)
            kwargs.update({'queryset': queryset})
        return super().formfield_for_manytomany(db_field, request, **kwargs)

    def get_form(self, request, obj=None, **kwargs):
        form = super().get_form(request, obj, **kwargs)
        if request.user.is_superuser:
            form.base_fields['specialties'] = SpecialtyModelMultipleChoiceField(Specialty.objects.all())
        return form

Я думаю, что это может быть полезно в ситуациях, когда DoctorAdmin наследуется от некоторой базовой модели, например BaseAdmin, и эта базовая модель также имеет переопределенный метод get_form(). Но здесь в случае запроса суперпользователя queryset формируется дважды, поэтому ответ Тонио определенно лучше.

Вернуться на верх