modelformset_factory not saving with queryset on fields

If I place a queryset within the ModelForm on a field then the formset fails to save, without the query the formset works correctly.
I’d be grateful of advice on where I’m going wrong with the query as the html form and the POST data looks valid.

view:

def updateRaceParticipants(request, raceid):
    template = loader.get_template('ksail_events/race_participant_update_form.html')
    raceobj = Race.objects.get(id = raceid)
    stageid = getattr(raceobj, 'stage_id')
    stageobj = Stage.objects.get(id = stageid)
    eventid = getattr(stageobj, 'event_id')
    rp1formset = modelformset_factory(RaceParticipant, form=UpdateRaceParticipantForm, extra=0)
    rp2formset = rp1formset(queryset=RaceParticipant.objects.filter(race_id = raceid), form_kwargs={"stageid": stageid})
    myevent = Event.objects.get(id=eventid)
    context = {
        'form': rp2formset,
        'myevent': myevent,
        'myperms':myperms,
    }

    # If this is a POST request then process the Form data
    if request.method == 'POST':
        formset = rp1formset(request.POST)

        #Check if the form is valid:
        if formset.is_valid():
            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
            if (myperms.event_perm == 'WRITE' or request.user.is_staff or request.user.is_superuser):
                formset.save()
        
        else:
            raise Exception(formset.errors)

        # redirect to a new URL:
        return HttpResponseRedirect(reverse('RaceDetailView', args=[eventid]))

    # If this is a GET (or any other method) create the default form.
    else:
        formset = rp1formset(queryset=RaceParticipant.objects.filter(race_id = raceid), form_kwargs={"stageid": stageid})
        return HttpResponse(template.render(context, request))

form:

class UpdateRaceParticipantForm(forms.ModelForm):
    class Meta:
        model = RaceParticipant
        fields = ['boat', 'team', 'race', 'sailor','sched_side', 'finish_position', 'score_code', 'points',]

    def __init__(self, *args, **kwargs):
        stageid = kwargs.pop('stageid', None)  # Extract ID from kwargs
        super().__init__(*args, **kwargs)
        
        #restrict the queries for the fields to just the data from this event
        sf = StageFlightMapping.objects.filter(stage_id = stageid).values_list('stageflights_id')
        self.fields['boat'].queryset = Boat.objects.filter(flight__in=sf).order_by('name')
        st = StageTeamMapping.objects.filter(stage_id = stageid).values('enteredteam_id')
        self.fields['team'].queryset = Team.objects.filter(id__in=st).order_by('name')
        self.fields['race'].queryset = Race.objects.filter(stage_id = stageid).order_by('race_number')

Exception error (repeated 6 times once for each form in set):

[{'boat': ['Select a valid choice. That choice is not one of the available choices.'], 'team': ['Select a valid choice. That choice is not one of the available choices.'], 'race': ['Select a valid choice. That choice is not one of the available choices.']}, 

POST data (only first 2 forms shown):

csrfmiddlewaretoken	
'mBbE6rl2Syz8ZeDHcilBTxQkRrOYver6JWgPQHlm0Bkwy55xCDP9VagA6wClXbvS'
form-TOTAL_FORMS	
'6'
form-INITIAL_FORMS	
'6'
form-MIN_NUM_FORMS	
'0'
form-MAX_NUM_FORMS	
'1000'
form-0-boat	
'19'
form-0-team	
'1'
form-0-race	
'947'
form-0-sailor	
''
form-0-sched_side	
'LEFT'
form-0-finish_position	
'1'
form-0-score_code	
'NON'
form-0-points	
'1'
form-0-id	
'4368'
form-1-boat	
'19'
form-1-team	
'1'
form-1-race	
'947'
form-1-sailor	
''
form-1-sched_side	
'LEFT'
form-1-finish_position	
'2'
form-1-score_code	
'NON'
form-1-points	
'2'
form-1-id	
'4369'

Many thanks

Paul

What does the RaceParticipant model look like? (Specifically, what data types are boat, team, and race?)

What widgets are used for the entry of data for those three fields?

You have:

and

Is the query in the code I quoted from your code above going to include a Boat object with pk = 19?

Here are the models (with some extra fields removed):

class RaceParticipant(models.Model):
    race = models.ForeignKey(
        'race',
        related_name = 'participant_in_race',
        on_delete=models.CASCADE,
    )
    sailor = models.ForeignKey(
        'sailor',
        related_name = 'sailor_in_raceparticipant',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )
    team = models.ForeignKey(
        'team',
        related_name = 'team_in_raceparticipant',
        on_delete=models.PROTECT,
    )
    boat = models.ForeignKey(
        'boat',
        related_name = 'boat_in_racepartcipant',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )


class StageFlightMapping(models.Model):
    event = models.ForeignKey(
            'event',
            related_name='stageflightmap_in_event',
            on_delete=models.PROTECT
            )
    stage = models.ForeignKey(
            'stage',
            related_name = 'stageflightmap_in_stage',
            on_delete=models.PROTECT,
        )
    stageflights = models.ForeignKey(
            'flight',
            related_name='stageflightmap_in_stage',
            on_delete=models.PROTECT
            )
    templateflight = models.ForeignKey(
        'scheduleTemplateFlight',
        related_name = 'stageflightmap_in_templateflight',
        on_delete = models.PROTECT
    )

class Flight(models.Model):
    name = models.CharField(max_length=255)
    no_boats = models.IntegerField()
    descriptor = models.CharField(max_length=255, blank=True)
    club = models.ForeignKey(
        'Club',
        related_name = 'flights_in_club',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )
    def __str__(self):
        return self.name  

class Boat(models.Model):
    name = models.CharField(max_length=255)
    descriptor = models.CharField(max_length=255, blank=True)
    sailNo = models.CharField(max_length=255, blank=True)
    flight = models.ForeignKey(
        'Flight',
        related_name = 'boats_in_flight',
        on_delete = models.PROTECT
    )

    def __str__(self):
        return self.name    

class Stage(models.Model):
    name = models.CharField(max_length=255)
    order = models.IntegerField()
    group = models.IntegerField()
    event = models.ForeignKey(
            'event',
            related_name='stage_in_event',
            on_delete=models.PROTECT
            )

The data entry for the fields are input select
I can confirm that the form is using valid and correct keys.
If I remove the query then the form validates and saves but with the filter it fails.

Is the query in the code I quoted from your code above going to include a Boat object with pk = 19?

Yes there is a ‘Boat’ object that exists with pk=19
When I submitted this formset the only value that I changed was that one you picked out (it’s initial value was ‘18’). All other values remained unchanged.

Thanks for your time.

Paul

This actually doesn’t answer my question.

We have two specific lines of code that are going to be executed within the context of a particular invocation of a view.

First line:

What are the contents of sf going to be when this instance of this form is submitted, based upon the url being used?

Second line:

What are the pks for the Boat objects being returned by this query?

The fact that you have an instance of Boat with a pk=19 does not mean that that instance is being returned here.

I’m not quite sure what you are driving at here.
When the formset is created in the view and sent to the template, the boat select field contains the expected key-value pairs. When the form is submitted the POST has the expected key having picked the value.

If I comment out the queryset on the boat field then the form saves, therefore I know the view and template must function correctly.
Without the queryset in place, the boat select field on the template has the key-value pairs for every record in the Boat table.

The objective of the queryset is to reduce the number of boats in the select field to only those relevant for the user and limit the data returned by the database.

sf is a list of keys e.g. ‘5’, ‘6’, ‘7’, ‘8’

This query returns a queryset of objects of type Boat with the pks of ‘17’, ‘18’, ‘19’ which is what is expected.

Thanks

Paul

The basic cause for this error is that you’re trying to submit a value in the form, where that value does not exist in the queryset being generated.

We’re talking about a specific instance of a view that was called with a specific url parameter (raceid)

That parameter is used to get one instance of Race:

In that instance of Race, there is one specific value for the stage_id being retrieved:

That stageid attribute is passed to the formset class creating the instances of the form:

The formset creates multiple instances of the form with that stageid parameter:

So at the point now where we’re discussing this, what is the url being used for this specific test case?

What then is the value of stageid that gets passed to the form?

What is the value of sf from the following statement, when you’re using that value of stageid?

What then are the values of the pks in the Boat objects when the following query is executed?

Again, I’m not talking in terms of generalities or concepts here. I’m looking for the specific values being returned by these statements.

Ahh, the penny has clicked!
What I hadn’t realised is that when I was getting the POST data I was creating a new formset to store that data and that formset needed to be able to match against the original dataset and I hadn’t sent the stageid variable from the view to the new form instance and so it was missing data.

The solution was to adjust the view as follows:

I was assuming that as long as the required data was in the POST it would just accept it and save the record, the is_valid() is clearly doing more checking and processing than I expected.

Thanks for your time, best wishes

Paul