【问题标题】:How to modify values in m2m field in form? (Django)如何在表单中修改 m2m 字段中的值? (姜戈)
【发布时间】:2022-01-24 10:49:58
【问题描述】:

我的models.py 中有 2 个模型。他们是DoctorSpecialty

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 实例时,我看到了我的 Doctor 模型字段,我应该在其中填充数据。在这些字段中,有一个字段 specialties ("Cпециальности"),它是 m2m 字段,包含 names(正如我设置的 __str__(self) 方法在 Specialty 模型中返回 self.name) 以获取之前添加到 Specialty 模型 (see a screen) 的 specialties

我只想在这个列表中看到专业名称,以及 Specialty 模型的 mc_id 字段的值,但它我不应该以这样的代价来做到这一点:

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 + ')'

我认为我可以通过在admin.py 中覆盖我的管理模型(与Doctor 模型相关联)中的formfield_for_manytomany() 方法来实现:

@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)

没有用。我想知道如何解决我的问题。提前谢谢你。

更新:我还想以用户相关的形式制作 specialties 字段的内容:

  • 如果 superuser 尝试添加新医生,他将在表单值的 specialties 字段中看到,例如:
    • 内科 (mc1_id)
    • 神经科 (mc1_id)
    • 心脏病学 (mc2_id)
  • 如果普通用户名称为“mc1_id”尝试添加新医生,他将在按其姓名过滤的表单值中看到不带“(mc_id)”部分的值。喜欢:
    • 内科
    • 神经内科

【问题讨论】:

    标签: python django


    【解决方案1】:

    我认为覆盖表单字段中的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
        # ....
    

    更新:

    为了使字段文本用户依赖,您可以在Admin Modelget_form 方法中返回不同的类:

    
    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__'
    
    
    class SimpleDoctorModelForm(forms.ModelForm):
        
        class Meta:
            model = Doctor
            fields = '__all__'
    
    
    @admin.register(Doctor)
    class DoctorAdmin(admin.ModelAdmin):
        
        def get_form(self, request, obj=None, **kw):
            if request.user.is_superuser:
                return DoctorModelForm
            return SimpleDoctorModelForm
    
    

    如果您需要更多,您可以在表单中设置属性并在构造函数中使用它:

    
    class DoctorModelForm(forms.ModelForm):
        
        class Meta:
            model = Doctor
            fields = '__all__'
    
        @classmethod
        def set_user(cls, user):
            cls.__user = user
    
        def __init__(self, *args, **kw):
            super(DoctorModelForm, self).__init__(*args, **kw)
            if self.__user.condition:
                self.fields['specialties'] = MyCustomField
    
    
    @admin.register(Doctor)
    class DoctorAdmin(admin.ModelAdmin):
        form = DoctorModelForm
    
        def get_form(self, request, obj=None, **kw):        
            form_class = super(DoctorAdmin, self).get_form(request, obj, **kw)
            form_class.set_user(request.user)
    
            return form_class
    

    【讨论】:

    • 完美运行,谢谢!您能否澄清一下,是否有办法使结果依赖于用户?我用我的意思的描述更新了我的问题。
    • 非常感谢您的帮助!这是一个完美的解决方案。我还考虑了对这个想法的稍微不同的实现。我在一个单独的答案中分享了它。
    【解决方案2】:

    只需在模型中使用 format()

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

    【讨论】:

    • 不幸的是,我无法以这种方式解决我的问题。我更新了起始帖子,并在其中描述了原因。
    【解决方案3】:

    我想分享 Tonio 的好主意的另一种实现方式。

    # 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() 方法的情况下它会很有用。但是在这里,如果超级用户的请求查询集形成了两次,因此 Tonio 的答案肯定会更好。

    【讨论】:

      猜你喜欢
      • 2016-01-19
      • 2019-09-27
      • 1970-01-01
      • 2016-05-16
      • 2019-07-31
      • 1970-01-01
      • 2021-03-10
      • 2016-12-16
      • 1970-01-01
      相关资源
      最近更新 更多