在Django中修改外键查询集创建CBV

Modifying foreign key queryset in Django create CBV

我有 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"

更多信息。

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

我已经能够获得向特定主管报告的员工列表。

每位员工仅向一位主管报告,因此这些员工中的 none 将出现在向另一位主管报告的任何其他员工列表中。

据此,我已经能够将 link 放入(模板)视图,其中上下文是选定的员工,并使用上下文生成特定于该员工的报告,如下所示:

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 但应该是 Instruction 模型中指示的 Profile。

希望这是答案:

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>

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

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

问题是防止主管向其他下属发出指示。

因为我是 Django 的新手,所以我专注于我还不够擅长的方法,而忘记了专注于期望的结果。

使用下面的代码实现第一句中的objective:

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>

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

本章到此结束。