我有一个类似于以下的双向外国关系
class Parent(models.Model): name = models.CharField(max_length=255) favoritechild = models.ForeignKey("Child", blank=True, null=True) class Child(models.Model): name = models.CharField(max_length=255) myparent = models.ForeignKey(Parent)
如何将Parent.favoritechild的选择仅限于父母本身的子女?我试过了
class Parent(models.Model): name = models.CharField(max_length=255) favoritechild = models.ForeignKey("Child", blank=True, null=True, limit_choices_to = {"myparent": "self"})
但这会导致管理界面不列出任何子项.
我刚刚在Django文档中遇到了ForeignKey.limit_choices_to.不确定这是如何工作的,但它可能只是正确的事情.
更新: ForeignKey.limit_choices_to允许指定常量,可调用或Q对象,以限制键的允许选择.一个常数显然在这里没用,因为它对所涉及的对象一无所知.
使用可调用(函数或类方法或任何可调用对象)似乎更有希望.但是,如何从HttpRequest对象访问必要信息的问题仍然存在.使用线程本地存储可能是一种解决方案.
2.更新:以下是对我有用的内容:
我创建了一个中间件,如上面的链接所述.它从请求的GET部分中提取一个或多个参数,例如"product = 1",并将此信息存储在线程本地中.
接下来,模型中有一个类方法,它读取线程局部变量并返回一个id列表以限制外键字段的选择.
@classmethod def _product_list(cls): """ return a list containing the one product_id contained in the request URL, or a query containing all valid product_ids if not id present in URL used to limit the choice of foreign key object to those related to the current product """ id = threadlocals.get_current_product() if id is not None: return [id] else: return Product.objects.all().values('pk').query
如果没有选择任何可能的ID,则返回包含所有可能ID的查询非常重要,这样正常的管理页面才能正常工作.
然后将外键字段声明为:
product = models.ForeignKey( Product, limit_choices_to={ id__in=BaseModel._product_list, }, )
问题在于您必须提供信息以通过请求限制选择.我没有看到在这里访问"自我"的方法.
"正确"的方法是使用自定义表单.从那里,您可以访问self.instance,它是当前对象.示例 -
from django import forms from django.contrib import admin from models import * class SupplierAdminForm(forms.ModelForm): class Meta: model = Supplier fields = "__all__" # for Django 1.8+ def __init__(self, *args, **kwargs): super(SupplierAdminForm, self).__init__(*args, **kwargs) if self.instance: self.fields['cat'].queryset = Cat.objects.filter(supplier=self.instance) class SupplierAdmin(admin.ModelAdmin): form = SupplierAdminForm
这种新的"正确"方式,至少在Django 1.1之后是覆盖AdminModel.formfield_for_foreignkey(self,db_field,request,**kwargs).
见http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey
对于那些不想按照以下链接进行操作的人,可以使用上述问题模型的示例函数.
class MyModelAdmin(admin.ModelAdmin): def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "favoritechild": kwargs["queryset"] = Child.objects.filter(myparent=request.object_id) return super(MyModelAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)
我只是不确定如何获取正在编辑的当前对象.我希望它实际上是在某个地方,但我不确定.
这不是django的工作方式.你只会以一种方式创建关系.
class Parent(models.Model): name = models.CharField(max_length=255) class Child(models.Model): name = models.CharField(max_length=255) myparent = models.ForeignKey(Parent)
如果你试图从父母那里接触孩子,你会这样做
parent_object.child_set.all()
.如果在myparent字段中设置了related_name,那么您将其称为.例如:related_name='children'
那么你会这样做parent_object.children.all()
阅读文档 http://docs.djangoproject.com/en/dev/topics/db/models/#many-to-one-relationships了解更多信息.
如果您只需要Django管理界面中的限制,这可能会有效.我基于另一个论坛的答案 - 虽然它适用于ManyToMany关系,但你应该能够替换formfield_for_foreignkey
它的工作.在admin.py
:
class ParentAdmin(admin.ModelAdmin): def get_form(self, request, obj=None, **kwargs): self.instance = obj return super(ParentAdmin, self).get_form(request, obj=obj, **kwargs) def formfield_for_foreignkey(self, db_field, request=None, **kwargs): if db_field.name == 'favoritechild' and self.instance: kwargs['queryset'] = Child.objects.filter(myparent=self.instance.pk) return super(ChildAdmin, self).formfield_for_foreignkey(db_field, request=request, **kwargs)