我已经解决了使用此代码编辑对象 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 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)
以下代码片段将为您提供对象 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:通过调试和遍历可访问变量找到它。
据我所知,不可能通过
formfield_for_...
方法访问当前实例,因为它们只会被单个字段实例调用!
挂钩此逻辑的更好点是
get_form
,您可以访问整个实例/表单。您还可以在那里覆盖表单字段的查询集!
我通过重写使其工作
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()
如果您真的需要
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
(另请参阅这个答案):
注意不要更改
属性,因为此修改将影响同一 Python 进程中所有后续base_fields
实例:...ContactForm
但是,我认为此限制不适用于此处,因为管理表单是动态生成的,使用
modelform_factory
。
@fizxmike的答案提供了一个使用
get_form()
的替代示例,无需修改base_fields
,但它仍然需要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
以查看数据库中是否存在该对象(添加与更改视图)。
此方法也适用于内联。
更通用的方法可以是编写一个辅助方法来获取模型实例(如果有),就像您通常使用(有界)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
我正在处理类似的情况,并意识到我从请求中需要的 id,我可以从模型本身中获取,因为它是该模型的外键。所以它会是这样的:
cpu = CompanyUser.objects.filter(company__id=self.company_id)
或者你的模型的结构决定什么。
我通过在 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)