I have 2 groups of 5 employees. In each group, one of the employees is that group's supervisor.
In the create view, once the supervisor field is populated, I would like the employee foreign key field to show only those employees belonging to that supervisor.
It would be nice to have the appropriate employees displayed based on the user (supervisor) without the supervisor field having to be populated first.
I have tried model forms to try to appropriately modify the employee foreign key field query set, obviously to no avail. Please help!
The code is as follows:
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"
More information.
For more context (pardon the pun)...
I have already been able to get a list of employees reporting to a particular supervisor.
Each employee reports to only one supervisor, so none of these employees would appear in any other list of employees reporting to another supervisor.
From this, I have been able to put a link to a (template) view where the context is a chosen employee and use the context to produce a report specific to that employee as follows:
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
Since only that supervisor can generate that report, it should also be possible for only that supervisor to be able to create an instruction for the employee, without even having to specify in the form who (the supervisor) is creating the instruction for who (the employee), and for the appropriate attributes required by my model to be set automatically from the context data.
This is where I have a problem. I am using CBVs and only a supervisor can create an object for an employee.
When the supervisor wants to create an instruction for the employee, I want to be able to use the context (as done in the case of the report) to fix who the instruction is for and who is giving the instruction.
At the moment, when the create form opens, regardless of whether the supervisor is identified, all the employees appear, making it possible for a supervisor to create instructions for employees of the other supervisor and that's what I am trying to prevent.
I am too new in Django to figure this out on my own. In my novice opinion, it should be possible to prepopulate the create form with the supervisor and employee details from the context which negates the drop-down problem.
New, the employee model as requested ---`
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})`
Corrected the name of the last model, was Employee but should have been Profile as indicated in the Instruction model.
Hope this is the answer:
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
It worked! But only after this change:
I replaced if supervisor != models.Profile.objects.filter(employee=employee).supervisor:
with if employee != models.Profile.objects.filter(employee=employee, supervisor=supervisor):
for it to work.
The CBV create template:
<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>