I have been having a problem working with formsets in my project and I’ve been trying to get to the bottom of this. While doing so, a couple of different errors have been appearing. Generally, what I want to do is create an object of entity A (workout) and get redirected to a template/url that lets me “fill” it with objects of entity B, which I will be making at that point dynamically using model formsets. The problem seems to be revolving around the form, more specifically: if I write the fields one by one, as in :
CycleFormSet = modelformset_factory(
Cycle, fields=('reps', 'place_in_workout', 'exercise', 'number_of_times', 'break_inbetween'), extra=1
)
Then, I get the error: Unknown field(s) (place_in_workout, break_inbetween, reps, number_of_times) when I attempt to run the server. If I use exclude for some field, or do fields = ‘all‘ , then I don’t get an error at this point. However, I get the error : (‘ManagementForm data is missing or has been tampered with’) when I try to post the data of the workout object. Me code:
models.py
class Exercise(models.Model):
name = models.CharField(max_length=150)
description = models.TextField(max_length=500)
def __str__(self):
return self.name
class Workout(models.Model):
name = models.CharField(max_length=150, null=True)
created_by_user = models.ForeignKey(User, null=True, on_delete=models.RESTRICT)
description = models.TextField(max_length=1000, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Cycle(models.Model):
place_in_workout = models.IntegerField
exercise = models.ManyToManyField(Exercise)
number_of_times = models.IntegerField
reps = models.IntegerField
break_inbetween = models.IntegerField
workout = models.ManyToManyField(Workout)
class WorkoutCompleted(models.Model):
datetime = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.RESTRICT)
forms.py
class CreateUserForm(UserCreationForm):
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2')
class WorkoutForm(forms.ModelForm):
class Meta:
model = Workout
fields = ('name', 'description')
class ExerciseForm(forms.ModelForm):
class Meta:
model = Exercise
fields = ('name', 'description')
CycleFormSet = modelformset_factory(
Cycle, fields='__all__', extra=1
)
urls.py
urlpatterns = (
path('register/', views.register_page, name='register'),
path('login/', views.login_page, name='login'),
path('logout', views.logout_page, name='logout'),
path('', views.index, name='index'),
path('browse/', views.browse, name='browse'),
path('workouts/<str:user_id>/', views.workouts, name='workouts'),
path('add_exercise/', views.add_exercise, name='add_exercise'),
path('create_workout/<str:user_id>/', views.fill_workout, name='fill_workout')
)
views.py
@login_required(login_url='login')
def workouts(request, user_id):
context = {}
if request.method == 'POST':
form = WorkoutForm(request.POST)
if form.is_valid():
workout = form.save(commit=False)
workout.created_by_user = request.user
workout.save()
workout_id = workout.id
context = {'workout_id': workout_id}
return render(request, 'Trainmate/fill_workout.html', context)
else:
form = WorkoutForm()
workout_programs = Workout.objects.all()
user_workouts = workout_programs.filter(created_by_user=user_id)
context = {'user_workouts': user_workouts, 'form': form}
return render(request, 'Trainmate/workouts.html', context)
@login_required(login_url='login')
def fill_workout(request, user_id):
if request.method == 'POST':
# workouts = Workout.objects.filter(created_by_user__exact=request.user).order_by('-created_at')
# current_workout = workouts(0)
# pk_workout = current_workout.id
pk_workout = 1
formset = CycleFormSet(request.POST)
if formset.is_valid():
for form in formset:
cycle = form.save(commit=False)
cycle.workout = Workout.objects.get(pk=pk_workout)
cycle.save()
context = {}
return render(request, 'Trainmate/home.html', context)
else:
formset = CycleFormSet(queryset=Cycle.objects.none())
context = {'formset': formset}
return render(request, 'Trainmate/fill_workout_with_sets', context)
(there are more views, I didn’t include some views about login/logout, if asked, I will, I didn’t want to make the post even bigger than it’s already going to be). Also, I have run the views with the commented section, I believe I am doing some mistake with queryset, therefore I gave the pk_workout=1 so that the fault in the query set is not relevant. There is at least a workout object in the database at all times.
workouts.html
{% extends 'Trainmate/main.html' %}
{% block content %}
<h1>My Workouts</h1>
<div>
{% for workout in user_workouts %}
<tr>
<td>{{ workout.name }}</td>
<td><a class="btn btn-sm btn-info" href="">Update</a></td>
<td><a class="btn btn-sm btn-info" href="">Delete</a></td><br>
</tr>
{% endfor %}
</div>
<h1>Create new Workout</h1>
<form method="POST" action="{% url 'fill_workout' request.user.id %}">
{% csrf_token %}
{{ form }}
<input type="submit" value="Create Workout">
</form>
{% endblock %}
fill_workout.html
{% extends 'Trainmate/main.html' %}
{% block content %}
<h1>Fill workout with sets</h1>
<form id="form_container" method="POST" action="">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<div class="set_form">
{{ form.as_p }}
</div>
{% endfor %}
<button id="add-form" type="button">Add another set</button>
<button type="submit"> Create Cycle</button>
</form>
let set_form = document.querySelectorAll(".set_form")
let container = document.querySelector("#form_container")
let add_button = document.querySelector("#add_form")
let total_forms = document.querySelector("#id_form-TOTAL-FORMS")
let form_num = set_form.length -1
add_button.addEventListener('click',add_form)
function add_form(e){
e.preventDefault()
let new_form = set_form(0).cloneNode(true)
let form_regex = RegExp(`form-(\d){1}-`,'g')
form_num ++
new_form.innerHTML = new_form.innerHTML.replace(form_regex, `form-${form_num}-`)
container.insertBefore(new_form, add_button)
total_forms.setAttribute('value', `${form_num + 1}`)
}
{% endblock %}
I tried to run the server and complete the form with the name and description of the workout object without the javascript part of the template above, I still get the same error.
Sorry for the long post and the , I have been trying to narrow down my problem as much as I can before posting, but it seems I get nowhere.