I'm building a page that allows users to edit Task and related Activity records (one task can have many activities), all on the same page. I want to allow the user to "adopt" one or more activities by ticking a box, and have their user record linked to each activity via a ForeignKey. Here are extracts from my code...
models.py
from django.contrib.auth.models import User
class Task(models.Model):
category = models.CharField(max_length=300)
description = models.CharField(max_length=300)
class Activity(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
notes = models.TextField(blank=True)
owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
The activity "owner" is linked to a User from the Django standard user model.
I added an extra field in the form definition for the adopt field - I don't want to add it to the model as I don't need to save it once it's done it's job.
forms.py
class ActivityForm(forms.ModelForm):
adopt = forms.BooleanField(required=False)
class Meta:
model = Activity
fields = '__all__'
views.py
def manage_task(request, pk):
task = Task.objects.get(pk = pk)
TaskInlineFormSet = inlineformset_factory(Task, Activity,
form = ActivityForm)
if request.method == "POST":
form = TaskForm(request.POST, instance = task)
formset = TaskInlineFormSet(request.POST, instance = task)
if form.has_changed() and form.is_valid():
form.save()
if formset.has_changed() and formset.is_valid():
## ? DO SOMETHING HERE ? ##
formset.save()
return redirect('manage_task',pk=task.id)
else:
form = TaskForm(instance = task)
formset = TaskInlineFormSet(instance = task)
context = {'task': task, 'task_form': form, 'formset': formset}
return render(request, 'tasks/manage_task.html', context)
When the adopt field is ticked on the form, I want to be able to set the owner field in that form to the current user before the associated model instance is updated and saved.
I just can't figure out how to do that - if it was a single form (rather than an InlineFormSet), I think I could put code in the view to change the owner value in the form field before it was saved (I haven't tried this). Or try save(commit = False) and update the model instance then save() it.
Maybe I have to iterate through the formset in the view code and try one of those options when I find one that had adopt=True?
CodePudding user response:
When the adopt field is ticked on the form, I want to be able to set the owner field in that form to the current user before the associated model instance is updated and saved.
formset = TaskInlineFormSet(request.POST, instance = task)
if formset.adopt:
# If True
formset.user = request.user
formset.save()
I think I could put code in the view to change the owner value in the form field before it was saved (I haven't tried this).
You should give it a try.
CodePudding user response:
I'm not happy with this solution but it does work. I iterate through the forms and change the object instance if my adopt field is set.
views.py
def manage_task(request, pk):
task = Task.objects.get(pk = pk)
TaskInlineFormSet = inlineformset_factory(Task, Activity,
form = ActivityForm)
if request.method == "POST":
form = TaskForm(request.POST, instance = task)
formset = TaskInlineFormSet(request.POST, instance = task)
if form.has_changed() and form.is_valid():
form.save()
if formset.has_changed() and formset.is_valid():
## HERE'S WHAT I ADDED ##
for form in formset:
if form.cleaned_data['adopt'] is True:
form.instance.owner = request.user
## END OF ADDITIONS ##
formset.save()
## return redirect('manage_task',pk=task.id) # CHANGED THIS BECAUSE I WASN'T RETURNG ERRORS!
if not form.errors and not formset.total_error_count():
return redirect('manage_task',pk=task.id)
else:
form = TaskForm(instance = task)
formset = TaskInlineFormSet(instance = task)
context = {'task': task, 'task_form': form, 'formset': formset}
return render(request, 'tasks/manage_task.html', context)
I wish I could find more in the docs about how the form saving works but I think I'll have to look into the code if I want more detail.
