djangomany-to-manymodelformm2mcreate-view

How to update a specific manytomany field in django


My Area model has an exercise attribute with a ManyToManyField to my Exercise model:

class Area(models.Model):
    name = models.CharField(max_length=100)
    exercise = models.ManyToManyField(Exercise)

class Exercise(models.Model):
    name = models.CharField(max_length=100)

My AreaView displays a list of areas, which each link to their own list of specific exercises, shown by AreaDetailView:

class AreaView(ListView):
    model = Area
    template_name = 'workouts/areas.html'

class AreaDetailView(DetailView):
    model = Area
    template_name = 'workouts/exercises.html'

    def get_context_data(self, **kwargs):
        context = super(AreaDetailView, self).get_context_data(**kwargs)
        context['e_form'] = AddExerciseForm
        return context

e.g:

areas.html
- abs
- biceps
- cardio
- legs ...

exercises.html
Abs
- Ab-wheel
- Cable-crunch
- Plank ...

Biceps
- Barbell curl
- Cable curl
- Dumbbell curl

AreaDetailView also displays a form which I would like to allow the user to create their own exercises, which will be specific to their corresponding area.

Here is my form:

class AddExerciseForm(forms.ModelForm):
    class Meta:
        model = Exercise
        fields = ['name']

My template:

<form action="{% url 'exercise_add_new' %}" method="post">
    {% csrf_token %}
    {{ e_form }}
    <button type="submit">Save changes</button>
</form>

My url:

path('exercise-add-new', ExerciseFormView.as_view(), name='exercise_add_new'),

And here is my CreateView which is supposed to handle the logic:

class ExerciseFormView(CreateView):
    form_class = AddExerciseForm
    success_url = '/'

    def form_valid(self, form):
        form.save()
        new_ex = Exercise.objects.latest('id')
        area = Area.objects.get(id=1)
        area.exercise.add(new_ex)
        return super(ExerciseFormView, self).form_valid(form)

This allows me to update the first object in my Area model ok, but I need to adjust the value of the variable area in form_valid so that the current 'id' is updated. For example if I click on 'Biceps' and then complete the form, I want to add an exercise related to 'id=2'

I have tried area = Area.objects.get(id=self.kwargs['id'])and other similar variations but so far nothing I have tried has worked


Solution

  • In ExerciseFormView are you trying to add a new exercise to an area or create a new area?
    If adding a new exercise you will have to pass the area-id from the URL something like add_exercise/<area_id>, if doing the latter it should be straightforward.

    You have pass area-id in URL you can do like below

    path('exercise-add-new/<int:area_id>/', ExerciseFormView.as_view(), 
            name='exercise_add_new')
    

    Then update your view as below

    def form_valid(self, form):
        form.save()
        area = Area.objects.get(pk=self.kwargs["area_id"])
        new_ex = Exercise.objects.latest('id')
        area.exercise.add(new_ex)
        return super(ExerciseFormView, self).form_valid(form)
    

    Also update template as :

    <form method="post">
        {% csrf_token %}
        {{ e_form }}
        <button type="submit">Save changes</button>
    </form>