Jef*_* Mc 51 python django django-models
我有一个类似于以下的双向外国关系
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)
Run Code Online (Sandbox Code Playgroud)
如何将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"})
Run Code Online (Sandbox Code Playgroud)
但这会导致管理界面不列出任何子项.
Ber*_*Ber 32
我刚刚在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
Run Code Online (Sandbox Code Playgroud)
如果没有选择任何可能的ID,则返回包含所有可能ID的查询非常重要,这样正常的管理页面才能正常工作.
然后将外键字段声明为:
product = models.ForeignKey(
Product,
limit_choices_to={
id__in=BaseModel._product_list,
},
)
Run Code Online (Sandbox Code Playgroud)
问题在于您必须提供信息以通过请求限制选择.我没有看到在这里访问"自我"的方法.
s29*_*s29 31
"正确"的方法是使用自定义表单.从那里,您可以访问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
Run Code Online (Sandbox Code Playgroud)
小智 14
这种新的"正确"方式,至少在Django 1.1之后是覆盖AdminModel.formfield_for_foreignkey(self,db_field,request,**kwargs).
对于那些不想按照以下链接进行操作的人,可以使用上述问题模型的示例函数.
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)
Run Code Online (Sandbox Code Playgroud)
我只是不确定如何获取正在编辑的当前对象.我希望它实际上是在某个地方,但我不确定.
小智 12
这不是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)
Run Code Online (Sandbox Code Playgroud)
如果你试图从父母那里接触孩子,你会这样做
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)
Run Code Online (Sandbox Code Playgroud)