I’ve been working on a form which corresponds to a model ActionAnswers. One field, id_action, is a foreign key to the Actions model and has consistently given me the same error no matter what I do: Cannot assign “16”: “ActionAnswers.id_action” must be a “Actions” instance.
I understand the concept of using an instance like this as I successfully did it on another form in this project, but I can’t make this one work. Using print statements, I can see that the error happens at form.is_valid(), but even when I remove that statement the same error comes up. I’ve made lots of changes and variations to the POST code, but any suggestions will be helpful.
views.py
def actions(request):
if request.method == 'POST':
form = ActionsForm(request.POST)
if form.is_valid():
id_action_value = form.cleaned_data['id_action']
action_instance = Actions.objects.get(pk=id_action_value)
print(action_instance)
action_answer_instance = form.save(commit=False)
action_answer_instance.id_action = action_instance
action_answer_instance.save()
return redirect('actions')
else:
scores = Scores.objects.filter(id_company=request.user.company_id)
matching_actions = {}
for score in scores:
answer = score.answer
score_id_question = score.id_question
conditions = QuestionsActions.objects.filter(
action_condition=answer, id_question=score_id_question
)
for condition in conditions:
action = condition.id_action
theme_id = action.id_theme.id
theme_name = action.id_theme
question = Questions.objects.get(id=score_id_question.id)
action_nb = action.actionnb
existing_actions = [entry['action'].actionnb for theme_actions in matching_actions.values() for entry in theme_actions]
if action_nb not in existing_actions:
if theme_id not in matching_actions:
matching_actions[theme_id] = []
matching_actions[theme_id].append({
'action': action,
'matching_condition': condition.action_condition,
'id_question': score_id_question.id,
'score': score.score,
'question': question,
'theme_name': theme_name
})
# Calculate the total count of actions for each theme
total_actions = Actions.objects.values('id_theme').annotate(total_count=Count('id_theme'))
theme_counts = {theme: len(actions) for theme, actions in matching_actions.items()}
# Calculate the completion percentage for each theme
action_percentage = {theme['id_theme']: (theme_counts.get(theme['id_theme'], 0) / theme['total_count']) * 100 for theme in total_actions}
remaining_percentage = {theme_id: 100 - percentage for theme_id, percentage in action_percentage.items()}
# Sort the dictionary after the loop
sorted_matching_actions = OrderedDict(sorted(matching_actions.items(), key=lambda x: x[0]))
form_data = {}
for theme_name, entries in matching_actions.items():
for entry in entries:
# Use the 'actionnb' as the key for the initial data dictionary
form_data[entry['action'].actionnb] = entry['action'].name
form_data[entry['action'].id] = Actions.objects.get(pk=entry['action'].id)
# Create the form instance with initial data
form = ActionsForm(initial={'form_data': form_data})
return render(request, 'planActions.html', {'form': form, 'matching_actions': sorted_matching_actions, 'remaining_percentage': remaining_percentage})
return render(request, 'planActions.html', {'form': form, 'matching_actions': sorted_matching_actions, 'remaining_percentage': remaining_percentage})
form.py
class ActionsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'p-2 border'
self.fields['note'].widget.attrs['class'] = 'h-11 p-2 border'
self.fields['id_action'].required = False
class Meta:
model = ActionAnswers
fields = ['id_action', 'priority', 'state', 'startdate', 'duedate', 'enddate', 'managergender', 'managerfirstname', 'managerlastname', 'note']
widgets = {
'startdate': forms.DateInput(attrs={'type': 'date'}),
'duedate': forms.DateInput(attrs={'type': 'date'}),
'enddate': forms.DateInput(attrs={'type': 'date'}),
'managerfirstname': forms.TextInput(attrs={'placeholder': 'John'}),
'managerlastname': forms.TextInput(attrs={'placeholder': 'Doe'}),
'note': forms.Textarea(attrs={'placeholder': 'Notes'}),
}
id_action = forms.IntegerField(widget=forms.HiddenInput(), required=False)
priority = forms.ChoiceField(
choices=(('faible', 'Faible'), ('elevee', 'Elevée'), ('urgente', 'Urgente'),),
required=False
)
state = forms.ChoiceField(
choices=(('A réaliser', 'A réaliser'), ('En cours', 'En cours'), ('Terminé', 'Terminé'),),
required=False
)
managergender = forms.ChoiceField(
choices=(('M.', 'M.'), ('Mme', 'Mme')),
required=False
)
def clean(self):
cleaned_date = super().clean()
start = cleaned_date.get('startdate',None)
due = cleaned_date.get('duedate',None)
end = cleaned_date.get('enddate',None)
if end and start and end < start:
self.add_error('due','La date de fin ne peut pas être avant la date de début.')
models.py
class ActionAnswers(models.Model):
id_company = models.ForeignKey('Companies', models.DO_NOTHING, db_column='id_company')
id_action = models.ForeignKey('Actions', models.DO_NOTHING, db_column='id_action')
priority = models.CharField(max_length=255)
state = models.CharField(max_length=255, blank=True, null=True)
completion = models.IntegerField()
initialcompletion = models.IntegerField(db_column='initialCompletion')
startdate = models.DateTimeField(db_column='startDate')
duedate = models.DateTimeField(db_column='dueDate', blank=True, null=True)
enddate = models.DateTimeField(db_column='endDate', blank=True, null=True)
managergender = models.CharField(db_column='managerGender', max_length=255)
managerfirstname = models.CharField(db_column='managerFirstName', max_length=255, blank=True, null=True)
managerlastname = models.CharField(db_column='managerLastName', max_length=255, blank=True, null=True)
note = models.TextField(blank=True, null=True)
createdat = models.DateTimeField(db_column='createdAt')
updatedat = models.DateTimeField(db_column='updatedAt')
Relevant section of planActions.html:
{% for key, value in form.initial.form_data.items %}
{% if entry.action.name == value and entry.score is Null %}
<h2 class="font-semibold text-xl"> {{ key }}: {{ value }}</h2>
<br>
{% comment %} FORM FIELDS {% endcomment %}
<form action="{% url 'actions' %}" class="flex flex-col flex-wrap justify-evenly w-full" method="POST" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
<input type="hidden" name="id_action" value="{{ entry.action.id }}">
<div class="flex">
<div class="p-2">
{{ form.startdate.errors }}
<label for="{{ form.startdate.id_for_label }}">Date de début :</label>
{{ form.startdate }}
</div>
and the full traceback:
Traceback (most recent call last):
File "C:\Users\jay.bury\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jay.bury\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jay.bury\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\contrib\auth\decorators.py", line 23, in _wrapper_view
return view_func(request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jay.bury\Desktop\rgpd_django\app\views.py", line 109, in actions
if form.is_valid():
^^^^^^^^^^^^^^^
File "C:\Users\jay.bury\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\forms\forms.py", line 201, in is_valid
return self.is_bound and not self.errors
^^^^^^^^^^^
File "C:\Users\jay.bury\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\forms\forms.py", line 196, in errors
self.full_clean()
^^^^^^^^^^^^^^^^^
File "C:\Users\jay.bury\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\forms\forms.py", line 435, in full_clean
self._post_clean()
^^^^^^^^^^^^^^^^^^
File "C:\Users\jay.bury\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\forms\models.py", line 479, in _post_clean
self.instance = construct_instance(
File "C:\Users\jay.bury\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\forms\models.py", line 83, in construct_instance
f.save_form_data(instance, cleaned_data[f.name])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jay.bury\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\db\models\fields\__init__.py", line 1035, in save_form_data
setattr(instance, self.name, data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\jay.bury\AppData\Local\Programs\Python\Python312\Lib\site-packages\django\db\models\fields\related_descriptors.py", line 266, in __set__
raise ValueError(
^
Exception Type: ValueError at /actions/
Exception Value: Cannot assign "1": "ActionAnswers.id_action" must be a "Actions" instance.