Home > Mobile >  Django: update a model with the current user when an non-model form field is changed
Django: update a model with the current user when an non-model form field is changed

Time:02-08

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.

  •  Tags:  
  • Related