Django:根据相关对象限制外键选择

时间:2015-05-29 15:54:48

标签: python django foreign-keys

我不确定解释我需要什么的正确术语,我认为最简单的方法是通过示例。

我有以下模型 - 公司和软件资产。

class Company(models.Model):
    name = models.CharField(max_length=200)

class SoftwareAsset(models.Model):
    name = models.CharField(max_length=200)

然后,我想将各种公司映射到他们拥有的软件资产。例如,大多数公司可能拥有“Microsoft Office”资产,但只有少数公司可能拥有“Adobe Photoshop”。我使用连接表(再次,我不确定这是否是正确的术语,或者更重要的是,正确的解决方法)。

class CompanyAssets(models.Model):
    company = models.ForeignKey(Company)
    asset = models.ForeignKey(SoftwareAsset)

然后,我需要将员工与他们所雇用的公司一起定义:

class Employee(models.Model):
    name = models.CharField(max_length=200)
    company = models.ForeignKey(Company)

最后,我需要定义每个员工可以访问的应用程序:

class EmployeeSoftware(models.Model):
    employee = models.ForeignKey(Employee)
    asset = models.ForeignKey(SoftwareAsset)

现在,所有这一切都运行正常,但有一个例外。当我在管理界面中,并且我将记录添加到EmployeeSoftware表时,我能够选择公司不拥有的SoftwareAssets。 SoftwareAsset下拉列表允许我选择SoftwareAsset表中定义的任何软件包。我想将此限制为公司拥有的资产,如CompanyAssets中所定义。

我的偏好是在模型中执行此操作 - 如果可以防止为员工分配公司在数据库级别不拥有的资产,但是从我已经完成的阅读中这是不可能的。我已经搞乱了ForeignKey.limit_choices_to参数,但没有太多运气。

我尝试使用formfield_for_foreignkey为EmployeeSoftwareAdmin类编辑admin.py:

class EmployeeSoftwareAdmin(admin.ModelAdmin):

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

        if db_field.name == 'asset':
           kwargs['queryset'] = CompanyAssets.objects.filter(company__name="XXXXX") #works but is obviously static 

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

我无法找到一种方法来访问formfield_for_foreignkey方法中允许进行正确过滤的任何对象。

1 个答案:

答案 0 :(得分:1)

将SoftwareAsset赋予公司FK。

class SoftwareAsset(models.Model):
    name = models.CharField(max_length=200)
    company = models.ForeignKey(Company)

因此,公司可以拥有许多软件资产。现在,您可以轻松过滤自定义模型中的软件资产。

EmployeeSoftwareForm(forms.ModelForm):
    class Meta:
        model = EmployeeSoftware

    def __init(self, *args, **kwargs):
        super(EmployeeSoftwareForm, self).__init__(*args, **kwargs)
        if 'instance' in kwargs:
        self.fields['assets'].queryset = SoftwareAsset.objects.filter(company = kwargs['instance'].employee.company)

最后将这个自定义表单用于EmployeeSoftware modeladmin:

class EmployeeSoftwareAdmin(admin.ModelAdmin):
    form = EmployeeSoftwareForm

这消除了对CompanyAsset模型的需求。

更新:好的,您希望EmployeeSoftware的每个新实例根据员工公司了解它可以拥有的资产。但是因为在你选择它之前你不知道你的员工是什么,所以在表单init上不可能做到。您必须使用javascript根据员工选择过滤选择。像这样(使用JQuery):

$("#employee").change(function(){

    $.post(<your assets select url>, $("employeeform").serialize(), function (data) {
    //populate your returned JSON into the asset select
}, 'json');

})

显然,您的帖子会根据所选员工过滤资产,这应该很简单。