form.is_valid() gives error on foreignkey

I have seen multiply topics about this error on this form but i didn’t find the answer for my problem.
I have a form with a selectfield that dynamicly change, with a AJAX call, and got shown with a modal.
When closed the form must be saved. I already have it working with a textfield. but with a Selectfield i got the error

ValueError: Cannot assign “2”: “SubTeam_User.team” must be a “Team” instance.

Is there someone with a idea for a solution?

model.py

class SubTeam(models.Model):
    title = models.CharField(max_length=32)
    team = models.ForeignKey(Team, on_delete=models.CASCADE)
    color = ColorField(format="hexa",default='#b3b3b3ff')
    
    class Meta:
        ordering = ['title']

    def __str__(self):
        return self.title

class SubTeam_User(models.Model):
    subteam = models.ForeignKey(SubTeam, on_delete=models.CASCADE, related_name='subteams')
    team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='subteam_teams')
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='subteam_users')
    default = models.BooleanField()

    def __str__(self):
        return f"{self.user} - {self.subteam}"

forms.py

class SubteamUserForm (forms.ModelForm):
    team = forms.IntegerField(widget=forms.HiddenInput)
    teammember = forms.IntegerField(widget=forms.HiddenInput)
    subteam = forms.ModelChoiceField(queryset=None,widget=forms.Select(attrs={"class":"form-control"}),initial="0")

    class Meta:
        model = SubTeam_User
        fields = ['subteam', 'team', 'teammember']

    def __init__(self, team, *args, **kwargs):
        super(SubteamUserForm, self).__init__(*args, **kwargs)
        self.fields['subteam'].queryset = SubTeam.objects.filter(team=team)

views.py

def subteam_user (request, team='', teammember=''):
    if request.method == 'POST':
        form = SubteamUserForm(team, request.POST)
        if form.is_valid():
            team = get_object_or_404(Team, id=form.cleaned_data['team'])
            teammember = get_object_or_404(CustomUser, id=form.cleaned_data['teammember'])
            subteam = form.cleaned_data['subteam']
            reg, created = SubTeam_User.objects.update_or_create(
                user=teammember,
                team=team,
                default=True,
                defaults={
                    'subteam': subteam,
                    }
            )
            response = {'subteam': reg.subteam, 'team': reg.team.id, 'teammember':reg.user.id}
            return JsonResponse(response, status=200)
        else:
            errors = form.errors.as_json()
            return JsonResponse({"errors": errors}, status=400)

In your view, you are assigning team=form.cleaned_data[‘team’]

but this returns an integer ID, not a Team object.

You already have this line:

team = get_object_or_404(Team, id=form.cleaned_data['team'])

That’s correct — but later when you use update_or_create(), make sure you’re passing that team object, not the integer.

So the corrected part in your views.py should be:

def subteam_user(request, team=‘’, teammember=‘’):

if request.method == 'POST':

    form = SubteamUserForm(team, request.POST)

    if form.is_valid():

        team_obj = get_object_or_404(Team, id=form.cleaned_data\['team'\])

        member_obj = get_object_or_404(CustomUser, id=form.cleaned_data\['teammember'\])

        subteam_obj = form.cleaned_data\['subteam'\]

        reg, created = SubTeam_User.objects.update_or_create(

            user=member_obj,

            team=team_obj,   # pass Team instance here

            default=True,

            defaults={'subteam': subteam_obj}

        )

        response = {'subteam': reg.subteam.id, 'team': reg.team.id, 'teammember': reg.user.id}

        return JsonResponse(response, status=200)

Thanks for your reply.

I did make the change, but sadly it still get the same error

django_hrsp  | Internal Server Error: /planner/subteam_user/2
django_hrsp  | Traceback (most recent call last):
File "/usr/local/lib/python3.13/site-packages/django/core/handlers/exception.py", line 55, in inner
	response = get_response(request)
File "/usr/local/lib/python3.13/site-packages/django/core/handlers/base.py", line 197, in _get_response
	response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python3.13/site-packages/django/contrib/auth/decorators.py", line 59, in _view_wrapper
	return view_func(request, *args, **kwargs)
File "/app/planner/views.py", line 351, in subteam_user
	if form.is_valid():
    ~~~~~~~~^^
File "/usr/local/lib/python3.13/site-packages/django/forms/forms.py", line 206, in is_valid
	return self.is_bound and not self.errors
											 ^^^^^^
File "/usr/local/lib/python3.13/site-packages/django/forms/forms.py", line 201, in errors
	self.full_clean()
	~~~~~~~~^^
File "/usr/local/lib/python3.13/site-packages/django/forms/forms.py", line 339, in full_clean
	self._post_clean()
	~~~~~~~~~^^
File "/usr/local/lib/python3.13/site-packages/django/forms/models.py", line 491, in _post_clean
	self.instance = construct_instance(
						~~~~~~~~~~~~^
		self, self.instance, opts.fields, opts.exclude
		^^^^^^^^^^^^^^^^^^^^^^^^^^
	)
	^
File "/usr/local/lib/python3.13/site-packages/django/forms/models.py", line 87, in construct_instance
	f.save_form_data(instance, cleaned_data[f.name])
	~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/django/db/models/fields/__init__.py", line 1099, in save_form_data
	setattr(instance, self.name, data)
	~~~~^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.13/site-packages/django/db/models/fields/related_descriptors.py", line 291, in __set__
	raise ValueError(
	...<7 lines>...
	)
ValueError: Cannot assign "2": "SubTeam_User.team" must be a "Team" instance.
[14/Oct/2025 06:44:36] "POST /planner/subteam_user/2 HTTP/1.1" 500 94490

They’re not. I can’t spot where the problem is in the view, but I’m afraid your answer does nothing different.

It does “smell” odd that both team and teammember are apparently being passed into the view function, as well as being values in the form data, but that might be unrelated.

What do you see in the log if you print(team) after getting it?

the script stops after “if form.is_valid():” all the code after it isn’t doing something.

i don’t understand the form.is_valid function fully, is it posible that the select input from the form gives a integer and that the is_valid is checking if it maches the instance?

thanks for your responce btw

Looking at the traceback I see the URL is /planner/subteam_user/2.

Your view starts:

def subteam_user(request, team=‘’, teammember=‘’):

So team will be “2”, and teammember will default to “”.

Then in the form:

self.fields['subteam'].queryset = SubTeam.objects.filter(team=team)

Seems odd to me. Shouldn’t self.fields['subteam'] be a SubTeam object or id?

I’m not quite awake yet :slight_smile: I would try debugging by print()ing variables at each step to see if they’re what you expect them to be.

I have a solution.
I have removed the ‘team’ and ‘teammember’ fields in the form model, than the valid check won’t check them.

forms.py:

class SubteamUserForm (forms.ModelForm):
    team = forms.IntegerField(widget=forms.HiddenInput)
    teammember = forms.IntegerField(widget=forms.HiddenInput)
    subteam = forms.ModelChoiceField(queryset=None,widget=forms.Select(attrs={"class":"form-control"}),initial="0")

    class Meta:
        model = SubTeam_User
        fields = ['subteam']

    def __init__(self, team_int, *args, **kwargs):
        super(SubteamUserForm, self).__init__(*args, **kwargs)
        self.fields['subteam'].queryset = SubTeam.objects.filter(team=team_int)

Also changed some things in de view and changed back the url so the team id isn’t in the URL (as I want for the post, in the get they are used)

view.py:

def subteam_user (request, team='', teammember=''):
    if request.method == 'POST':
        form = SubteamUserForm(team, request.POST)
        if form.is_valid():
            team_obj = get_object_or_404(Team, id=form.cleaned_data['team'])
            teammember_obj = get_object_or_404(CustomUser, id=form.cleaned_data['teammember'])
            reg, created = SubTeam_User.objects.update_or_create(
                user=teammember_obj,
                team=team_obj,
                default=True,
                defaults={
                    'user': teammember_obj,
                    'team': team_obj,
                    'subteam': form.cleaned_data['subteam'],
                    'default': True,
                    }
            )
            response = {'subteam': reg.subteam.title, 'team': reg.team.id, 'teammember':reg.user.id}
            return JsonResponse(response, status=200)

Thanks for having a look and send some ideas, they have helped to think in the right direction.