Fix user creation with group_ids via API (#12965)

* Fix user creation with group_ids via API

Creating a user via POST /api/user/ with the 'group_ids' field raised
TypeError: User() got unexpected keyword arguments: 'group_ids'.
The write-only 'group_ids' field was only handled on update.

- Declare 'group_ids' in SKIP_CREATE_FIELDS so it is not passed to the model
- Assign the groups after the user instance is created
- Add API test covering user creation with and without groups

* Bump API version
This commit is contained in:
Fran Quinto authored and GitHub committed 2026-10-05 11:33:07 +12:00
1 parent 6c4d2ef347
commit 575fbdc907
3 files changed
+50 -1

No files matched your search

@@ -1,11 +1,14 @@
"""InvenTree API version information.""" """InvenTree API version information."""
# InvenTree API version # InvenTree API version
INVENTREE_API_VERSION = 552 INVENTREE_API_VERSION = 553
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" """Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """ INVENTREE_API_TEXT = """
v553 -> 2026-10-04 : https://github.com/inventree/InvenTree/pull/12965
- Fix user creation with 'group_ids' via the User API endpoint
v552 -> 2026-10-02 : https://github.com/inventree/InvenTree/pull/12967 v552 -> 2026-10-02 : https://github.com/inventree/InvenTree/pull/12967
- Fix transitions endpoint schema to indicate that it returns a list - Fix transitions endpoint schema to indicate that it returns a list
@@ -336,6 +336,9 @@ class ExtendedUserSerializer(UserSerializer):
read_only_fields = [*UserSerializer.Meta.read_only_fields, 'groups'] read_only_fields = [*UserSerializer.Meta.read_only_fields, 'groups']
# 'group_ids' is a write-only serializer field which does not exist on the model
SKIP_CREATE_FIELDS = ['group_ids']
groups = GroupSerializer(many=True, read_only=True) groups = GroupSerializer(many=True, read_only=True)
# Write-only field, for updating the groups associated with the user # Write-only field, for updating the groups associated with the user
@@ -529,8 +532,14 @@ class UserCreateSerializer(ExtendedUserSerializer):
base_url = get_base_url() base_url = get_base_url()
# Extract the groups to assign to the new user
groups = validated_data.pop('group_ids', None)
instance = super().create(validated_data) instance = super().create(validated_data)
if groups is not None:
instance.groups.set(groups)
# Create the EmailAddress entry for the user # Create the EmailAddress entry for the user
if instance.email: if instance.email:
EmailAddress.objects.create( EmailAddress.objects.create(
+37
View File
@@ -154,6 +154,43 @@ class UserAPITests(InvenTreeAPITestCase):
self.assertEqual(response.data['username'], 'Superuser') self.assertEqual(response.data['username'], 'Superuser')
self.assertEqual(response.data['is_superuser'], True) self.assertEqual(response.data['is_superuser'], True)
def test_user_create_with_groups(self):
"""Test that groups can be assigned when creating a new user via the API."""
url = reverse('api-user-list')
group_a = Group.objects.create(name='Group A')
group_b = Group.objects.create(name='Group B')
self.user.is_staff = True
self.user.save()
self.assignRole('admin.add')
data = {
'username': 'grouped',
'first_name': 'Grouped',
'last_name': 'User',
'email': 'grouped@example.org',
'group_ids': [group_a.pk, group_b.pk],
}
response = self.post(url, data=data, expected_code=201)
self.assertEqual(response.data['username'], 'grouped')
group_names = {g['name'] for g in response.data['groups']}
self.assertEqual(group_names, {'Group A', 'Group B'})
user = User.objects.get(username='grouped')
self.assertEqual(set(user.groups.all()), {group_a, group_b})
# Creating a user without specifying groups must still work
response = self.post(
url,
data={**data, 'username': 'ungrouped', 'group_ids': []},
expected_code=201,
)
self.assertEqual(response.data['groups'], [])
def test_user_detail(self): def test_user_detail(self):
"""Test the UserDetail API endpoint.""" """Test the UserDetail API endpoint."""
user = User.objects.first() user = User.objects.first()