【问题标题】:Modifying foreign key queryset in Django create CBV在 Django 创建 CBV 中修改外键查询集
【发布时间】:2021-09-29 18:10:19
【问题描述】:

我有 2 组 5 名员工。在每个组中,其中一名员工是该组的主管。

在创建视图中,一旦主管字段被填充,我希望员工外键字段仅显示属于该主管的员工。

最好根据用户(主管)显示适当的员工,而不必先填充主管字段。

我已经尝试过模型表单来尝试适当地修改员工外键字段查询集,显然无济于事。请帮忙!

代码如下:

class Instruction(models.Model):

    supervisor = models.ForeignKey(
        Profile, on_delete=models.CASCADE,
        related_name="supervisorinstructions"
    )
    employee = models.ForeignKey(
        Profile, on_delete=models.CASCADE,
        related_name="employeeinstructions"
    )
    instruction = models.CharField(max_length=300)
        
    def __str__(self):
        return f"{self.instruction}"

    def get_absolute_url(self):
        return reverse("myapp:instructiondetail", kwargs={"pk": self.pk})

class InstructionCreate(CreateView):

    model = models.Instruction
    fields = [
        "supervisor",
        "employee",
        "instruction",
    ]
    template_name = "myapp/instruction_create_form.html"

更多信息。

更多上下文(请原谅双关语)...

我已经能够获得向特定主管汇报的员工名单。

每个员工只向一位主管汇报,因此这些员工都不会出现在向另一位主管汇报的任何其他员工列表中。

据此,我能够放置一个链接到(模板)视图,其中上下文是选定的员工,并使用上下文生成特定于该员工的报告,如下所示:

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    profile = models.Profile.objects.get(**kwargs)
    context["profile"] = profile
    employee = profile.employee
    context["employee"] = employee
    supervisor = profile.supervisor
    context["supervisor"] = supervisor

    # report queries

    return context

由于只有该主管可以生成该报告,因此也应该只有该主管能够为员工创建指令,甚至无需在表单中指定谁(主管)为其创建指令谁(员工),以及从上下文数据中自动设置我的模型所需的适当属性。

这是我有问题的地方。我正在使用 CBV,并且只有一名主管 可以为员工创建对象。

当主管想要为员工创建指令时,我希望能够使用上下文(就像在报告中所做的那样)来确定指令是针对谁以及谁在给出指令。

目前,当创建表单打开时,无论是否确定主管,所有员工都会出现,从而使主管可以为其他主管的员工创建指令,这就是我试图阻止的。

我在 Django 中太新了,无法自己解决这个问题。在我的新手看来,应该可以使用上下文中的主管和员工详细信息预先填充创建表单,从而消除下拉问题。

新建,按要求的员工模型---`

class Profile(models.Model):

    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, 
        related_name="userprofiles")
    employee = models.ForeignKey(
        "self", on_delete=models.CASCADE, null=True, blank=True,
        related_name="employeeprofiles",
    )
    supervisor1 = models.ForeignKey(
        "self", on_delete=models.CASCADE, null=True, blank=True,
        related_name="supervisorprofiles",
    )

    def __str__(self):
        return f"{self.user}" 

    def get_absolute_url(self):
        return reverse("myapp:profiledetail", kwargs={"pk": self.pk})`

更正了最后一个模型的名称,它是 Employee,但应该是 Profile,如 Instruction 模型中所示。

希望这是答案:

class InstructionCreateForm(ModelForm):
    class Meta:
        model = models.Instruction
        fields = ["supervisor", "employee", "instruction"]

    def clean(self):
        cleaned_data = super(InstructionCreateForm, self).clean()
        supervisor = cleaned_data.get('supervisor')
        employee = cleaned_data.get('employee')
        # check if the supervisor is the employee's supervisor and raise error if not
        if supervisor != models.Profile.objects.filter(employee=employee).supervisor:
            self.add_error(None, ValidationError('The employee is not your direct subordinate.'))
        return cleaned_data
    

class InstructionCreate(CreateView):

    model = models.Instruction
    template_name = "internalcontrol/instruction_create_form.html"
    form_class = forms.InstructionCreateForm

成功了!但仅在此更改之后:

我替换了if supervisor != models.Profile.objects.filter(employee=employee).supervisor:

if employee != models.Profile.objects.filter(employee=employee, supervisor=supervisor):

让它工作。

CBV 创建模板:

    <form method="post">

    {% csrf_token %}

    {{ form|crispy }}

    <div class="btn-group">
        <input type="submit" class="btn btn-primary btn-sm" value="Add">
        <a href="{% url 'company:instructionlist' %}" class="btn btn-secondary btn-sm">List</a>
    </div>

</form>

【问题讨论】:

  • 这能回答你的问题吗? how to display dependent drop-down in django form
  • 很遗憾没有,阿卜杜勒。我添加了更多信息来阐明我需要的帮助。
  • 啊,我误解了,因为“一旦主管字段被填充,我希望员工外键字段只显示属于该主管的员工。”但是相反,您希望主管甚至不是该表单上的字段,而只想显示过滤后的选择?见How do I filter ForeignKey choices in a Django ModelForm?
  • 你会添加 Supervisor 和 Employee 模型吗?
  • @KayaKwinana ModelChoiceField 是外键的默认字段(默认为您提供选择输入),您希望将这些选择限制为当前主管下的员工,该链接问题会显示给您如何过滤表单选择...

标签: django django-queryset


【解决方案1】:

问题是防止主管向别人的下属下达指示。

因为我是 Django 的新手,所以我专注于方法我还不够好,忘记了关注预期的结果。

使用下面的代码实现了第一句话的目的:

class Instruction(models.Model):
    
    employee = models.OneToOneField(Profile, on_delete=models.CASCADE, blank=True, null=True,
        related_name="employeeinstructions")
    supervisor = models.ForeignKey(Profile, on_delete=models.CASCADE, blank=True, null=True,
        related_name="supervisorinstructions")

class InstructionCreateForm(ModelForm):
    
    class Meta:
        model = models.Instruction
        fields = ["employee", "instruction"]

    def clean(self):
        cleaned_data = super(InstructionCreateForm, self).clean()
        supervisor = cleaned_data.get('supervisor')
        employee = cleaned_data.get('employee')
        if supervisor != models.Profile.objects.filter(employee=employee).supervisor:
            self.add_error(None, ValidationError('The employee is not a subordinate of this supervisor.'))
        return cleaned_data



class InstructionCreate(CreateView):

    model = models.Instruction
    template_name = "internalcontrol/instruction_create_form.html"
    form_class = forms.InstructionCreateForm

    def form_valid(self, form):
        user = self.request.user.id
        profile = models.Profile.objects.get(user=user, employee=True)
        form.instance.supervisor = profile
        return super().form_valid(form)

The CBV create template is:

    
    <form method="post">

        {% csrf_token %}

        {{ form|crispy }}

        <div class="btn-group">
            <input type="submit" class="btn btn-primary btn-sm" value="Add">
            <a href="{% url 'company:instructionlist' %}" class="btn btn-secondary btn-sm">List</a>
        </div>

    </form>

【讨论】:

  • 请编辑您的问题以包含模板。
【解决方案2】:

我最初的问题的正确答案——我一直在寻找的一等奖——在 Django 文档 (https://docs.djangoproject.com/en/3.2/ref/forms/fields/#fields-which-handle-relationships) 和 Django-Filter (https://django-filter.readthedocs.io/en/stable/guide/usage.html#filtering-the-related-queryset-for-modelchoicefilter) 中

本章结束。

【讨论】:

  • 很高兴您找到了解决方案。当您有时间时,请在此答案中添加一些代码以准确显示您所做的事情。这将有助于将来遇到同样问题的访问者。
【解决方案3】:

在你更新你的问题之前,我会假设你有一个类似的模型:

class Employee(models.Model):
    # some fields not related to the current question
    supervisor = models.ForeignKey(
        'Employee', on_delete=models.CASCADE,
        related_name="direct_reports"
    )

现在,如果您有一个名为supervisorEmployee 对象,您可以执行supervisor.direct_reports 来获取Employee 对象的查询集,其主管是supervisor。出于您的目的,您可能希望在表单的上下文中发送它以填充下拉列表。

或者,只在上下文中传递supervisor,然后直接在模板中访问supervisor.direct_reports

【讨论】:

  • @KayaKwinana 其实你可以直接把supervisor传到模板上下文中,直接在模板中使用supervisor.employ_direct_reports
  • 谢谢你们,你们花时间帮助我。鉴于我可以从所示的上下文中获取适当的主管和员工属性,这似乎是采取的适当途径。如果我已经具备了这些属性,那么追求下拉路线似乎毫无意义。我不使用模型表单,员工模型只有“用户”、“员工”和“主管”字段,后两个是自我的外键。按主管过滤该模型会产生向该主管报告的员工,并且每个人的 href 将数据发送到报告视图的上下文,如图所示。
  • 嗨,代码学徒。我现在注意到了一些事情。我从用于报告的 get_context_data 方法中获得了我需要的上下文属性。 CBV CreateViews 接受 get_context_data 方法。如何在 CBV 创建视图中合并上下文数据(基本上与我为报告所做的相同)以预填充创建表单,甚至可能只显示指令字段?
  • 我的最后一个建议不起作用。 CBV CreateView 不允许 id、pk 或任何第二个参数。我想知道 FBV CreateView 是否可以解决问题。不幸的是,我对这个分数完全一无所知。我猜想应该有可能考虑到更严格的控制,并且可以编辑和删除视图。简而言之,是否可以创建一个以pk为参数的FBV CreateView?
  • 显然我不知道会创建什么 pk,我参考的是员工资料 pk。无论如何,即使我认为我没有意义。
猜你喜欢
  • 2015-03-09
  • 2013-09-30
  • 2015-01-25
  • 2013-09-17
  • 2013-09-27
  • 1970-01-01
  • 1970-01-01
  • 2017-01-02
  • 1970-01-01
相关资源
最近更新 更多