【问题标题】:Make ForeignKey optional in Django Admin?在 Django Admin 中将 ForeignKey 设为可选?
【发布时间】:2012-01-30 17:10:03
【问题描述】:

我有一个自定义的 django 管理页面,我想在管理界面中将两个 ForeignKey 字段设为可选。我不想更改底层模型。

这是模型:

class IncorporationTicket(models.Model, AdminURL):
    ordered_by = models.ForeignKey('Organisation', # organisation which ordered this
                                   null = True,
                                   blank = False, # i.e. can only be null as a result of delete
                                   on_delete = models.SET_NULL)
    ordered_by_individual = models.ForeignKey('Individual', # individual at organisation which ordered this
                                               null = True,
                                               blank = False, # i.e. can only be null as a result of delete
                                               on_delete = models.SET_NULL)

(AdminURL 是一个提供 get_absolute_url 的 mixin)

这是模型管理员:

class TicketAdmin(admin.ModelAdmin):
    readonly_fields = ('ordered', 'charge', 'amount_paid', 'submitted_on')

    formfield_overrides = {
        models.ForeignKey: {'required': False},
        }


    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        pk = resolve(request.path).args[0] # the request url should only have one arg, the pk
        instance = self.get_object(request, pk) 
        user = request.user
        kwargs['required'] = False # will be passed to every field

        if db_field.name == "ordered_by_individual":
            # queryset should be a union of (a) individual already set on object (b) individual for current user
            ## None option is provided by admin interface - just need to let field be optional.
            if instance.ordered_by_individual:
                kwargs["queryset"] = (
                    Individual.objects.filter(pk = instance.ordered_by_individual.pk) |
                    user.individual_set.all())
            else: kwargs["queryset"] = user.individual_set.all()
        elif db_field.name == "ordered_by":
            # queryset should be a union of (a) organisation already set (b) any organisations for which user is authorised

           try:
               individual = user.individual_set.all()[0]
               all_orgs = Organisation.all_organisations_for_which_individual_authorised_to_incorporate(individual)
           except:
               all_orgs = Organisation.objects.none()
           if instance.ordered_by:
               kwargs["queryset"] = (
                   Organisation.objects.filter(pk = instance.ordered_by.pk) |
                   all_orgs)
           else: kwargs["queryset"] = all_orgs

        return super(type(self), self).formfield_for_foreignkey(db_field, request, **kwargs)

如您所见,我尝试同时使用formfield_overridesformfield_for_foreignkey 在FormField 上设置required = False,但它没有达到所需的效果:尝试通过管理员保存时没有设置的界面(即该字段保持原来的空白状态),管理界面显示错误'This field is required.'

所以,我的问题是:如何防止底层表单需要某些字段,同时还要在formfield_for_foreignkey 中设置选项?

【问题讨论】:

    标签: django django-admin django-forms


    【解决方案1】:

    虽然我不确定为什么 kwargs['required'] 不起作用,但您始终可以使用自己的表单覆盖管理表单。神奇的 django 管理员行为并没有让我失望,所以这是一个不错的选择。

    class MyForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(MyForm, self).__init__(*args, **kwargs)
            self.fields['my_fk_field'].required = False
    
        class Meta:
             model = MyModel
    
    class MyAdmin(admin.ModelAdmin):
        form = MyForm
    

    这仍然允许您通过formfield_for_foo 方法修改QuerySet

    【讨论】:

    • @Marcin,不是吗?这就是 formfield_for_foreignkey 的具体目的,覆盖表单。
    • 实际测试表明formfield_for_foreignkey中设置的查询集被自定义表单中的默认设置覆盖。
    • @Marcin 它不再是外键,所以我们需要一个不同的钩子。或者上面的怎么样?在覆盖的表单中设置字段必填属性,并让 django 继续将字段与 models.ForeignKey 关联,以便管理员允许您使用 formfield_for_foreignkey 挂钩
    【解决方案2】:

    ...差不多 9 年后,在 Django v3.1.2 ...
    blank=True 对我来说很好:

    from django.contrib.auth.models import User
    
    owner = models.ForeignKey(User, 
                              related_name="notes",
                              on_delete=models.CASCADE, 
                              null=True, 
                              blank=True)
    

    (解决方案取自here

    【讨论】:

      猜你喜欢
      • 2012-11-07
      • 2012-06-29
      • 1970-01-01
      • 2021-04-28
      • 2014-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多