如何在 Django 管理页面(在 formfield_for_foreignkey 内)获取实际对象 id?

问题描述 投票:0回答:8

我已经解决了使用此代码编辑对象 ID 的问题:

class CompanyUserInline(admin.StackedInline):
    """
    Defines tabular rules for editing company users direct in company admin
    """
    model = CompanyUser

    def formfield_for_foreignkey(self, db_field, request, **kwargs):

        if db_field.name == "user":
            users = User.objects.filter( Q(is_superuser=False) )
            query = Q()
            for u in users:
                aux = CompanyUser.objects.filter(user=u)
                if aux.count() == 0:
                    query |= Q(pk=u.id)

            try:
                cpu = CompanyUser.objects.filter(company__id=int(request.path.split('/')[4]))
                for p in cpu:
                    query |= Q(pk=p.user.id)
            except:
                pass

            kwargs["queryset"] = User.objects.filter(query).order_by('username')

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

但是, int(request.path.split('/')[4]) 真的很难看。我想知道如何从 Django AdminModel 获取 id。我确定它在里面的某个地方,有人知道吗?

提前谢谢您! ;D

django django-models django-admin
8个回答
27
投票

经过一番挖掘,我们能够获取传递给管理视图的参数(在被 django admin 的 urls.py 解析后)并使用它(self_pub_id)来获取对象:

class PublicationAdmin(admin.ModelAdmin):

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        if db_field.name == "authors":
            #this line below got the proper primary key for our object of interest
            self_pub_id = request.resolver_match.args[0]

            #then we did some stuff you don't care about
            pub = Publication.objects.get(id=self_pub_id)
            kwargs["queryset"] = pub.authors.all()
        return super(PublicationAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)

一个更优雅的解决方案是使用已接受的答案推荐并利用 get_form ModelAdmin 成员函数。像这样:

class ProfileAdmin(admin.ModelAdmin):
    my_id_for_formfield = None
    def get_form(self, request, obj=None, **kwargs):
        if obj:
            self.my_id_for_formfield = obj.id
        return super(ProfileAdmin, self).get_form(request, obj, **kwargs)

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "person":
            kwargs["queryset"] = Person.objects.filter(profile=self.my_id_for_formfield)
        return super(ProfileAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

18
投票

以下代码片段将为您提供对象 ID:

request.resolver_match.kwargs['object_id']

使用示例:(我正在过滤显示的电话号码,仅显示客户的电话号码)

def formfield_for_foreignkey(self, db_field, request, **kwargs):
    if db_field.name == 'preferred_contact_number':
        kwargs['queryset'] = CustomerPhone.objects.filter(customer__pk=request.resolver_match.kwargs['object_id'])
    return super().formfield_for_foreignkey(db_field, request, **kwargs)

P.S:通过调试和遍历可访问变量找到它。


11
投票

据我所知,不可能通过

formfield_for_...
方法访问当前实例,因为它们只会被单个字段实例调用!

挂钩此逻辑的更好点是

get_form
,您可以访问整个实例/表单。您还可以在那里覆盖表单字段的查询集!


4
投票

我通过重写使其工作

change_view()

class CartAdmin(admin.ModelAdmin):

def change_view(self, request, object_id, form_url='', extra_context=None):
    self.object_id = object_id
    return self.changeform_view(request, object_id, form_url, extra_context)


def formfield_for_foreignkey(self, db_field, request, **kwargs):
    print self.object_id
    return super(CartAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

然后你可以在

self.object_id
内拨打
formfield_for_foreignkey()


1
投票

简短回答

如果您真的需要

id
内的对象
formfield_for_foreignkey()
,您可以使用
request.resolver_match.kwargs.get('object_id')
docs),如@mazyar-mk的答案中所建议的。

但是,如果目标是例如要根据正在编辑的对象过滤字段查询集,可能更好

  • 扩展
    ModelForm.__init__()
    ,如 formfield_for_foreignkey()
    文档中的建议,或
  • 扩展
    ModelAdmin.get_form()
    ,覆盖表单类的
    base_fields

请参阅下面的示例。

长答案(带示例)

@BernhardVallant接受的答案建议扩展

ModelAdmin.get_form()
,并且在评论中建议修改
base_fields
属性。 (请注意,
get_form()
返回形式class,而不是绑定形式。)

这很诱人,你可能可以摆脱这样的事情:

def get_form(self, request, obj=None, change=False, **kwargs):
    form_class = super().get_form(request, obj, change, **kwargs)
    if obj:
        form_class.base_fields['my_field'].queryset = my_queryset.filter(
            my_lookup=obj
        )
    return form_class

请注意,Django 的 Forms API 文档警告不要修改

base_fields
(另请参阅这个答案):

注意不要更改

base_fields
属性,因为此修改将影响同一 Python 进程中所有后续
ContactForm
实例:...

但是,我认为此限制不适用于此处,因为管理表单是动态生成的,使用

modelform_factory

@fizxmike答案提供了一个使用

get_form()
的替代示例,无需修改
base_fields
,但它仍然需要
formfield_for_foreignkey()

文档中的解决方案

formfield_for_foreignkey()

文档建议了另一种方法(另请参阅此票据字段文档):

对于更复杂的过滤器,您可以使用[the]

ModelForm.__init__()
方法根据模型的
instance
进行过滤...

表单初始化后,您可以访问

fields
属性来修改查询集。此外,您可以访问实际对象(如
self.instance
),而不仅仅是对象 ID。

例如:

class MyModelAdminForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.instance.pk is not None:
            self.fields['my_field'].queryset = my_queryset.filter(
                my_lookup=self.instance
            )


class MyModelAdmin(admin.ModelAdmin):
    form = MyModelAdminForm
    ...

这里我们检查

self.instance.pk
以查看数据库中是否存在该对象(添加与更改视图)。

此方法也适用于内联。


0
投票

更通用的方法可以是编写一个辅助方法来获取模型实例(如果有),就像您通常使用(有界)ModelForm 所做的那样,并从中检索 id 或任何其他属性:

from django.contrib import admin

class MyModelAdmin(admin.ModelAdmin):

    def get_instance(self, request):
        try:
            object_id = request.resolver_match.kwargs['object_id']
            obj = self.get_object(request, object_id)
        except:
            obj = None
        return obj

-2
投票

我正在处理类似的情况,并意识到我从请求中需要的 id,我可以从模型本身中获取,因为它是该模型的外键。所以它会是这样的:

cpu = CompanyUser.objects.filter(company__id=self.company_id)

或者你的模型的结构决定什么。


-2
投票

我通过在 model.py 中创建一个返回 ID 的 property() 来使其工作

models.py:

class MyModel(models.Model):
    myfield = models.CharField(max_length=75)
    ...
    def get_id(self):
        return str(self.id)
    getid = property(get_id)

admin.py:

from myapp.models import MyModel

class MyModelAdmin(admin.ModelAdmin):
    list_display = ['mylink',]
    def mylink(self, object):
        return '<a href="http://mywebsite/'+object.getid+'/">Edit</a>'
    mylink.allow_tags = True

admin.site.register(MyModel, MyModelAdmin)
© www.soinside.com 2019 - 2024. All rights reserved.