Home > OS >  DRF: nested Serializer error on create argument after ** must be a mapping, not str
DRF: nested Serializer error on create argument after ** must be a mapping, not str

Time:02-08

I have a model that connects a school model and a user model through a foreign key, and on creating if a student is being created I want the user foreign key to be set as the instance and also a school to be chosen by its id however i can't solve this error

TypeError at /api/users/student_register/
django.db.models.manager.BaseManager._get_queryset_methods.<locals>.create_method.<locals>.manager_method() argument after ** must be a mapping, not str

class User(AbstractBaseUser, PermissionsMixin):
     ....
    email = models.EmailField(
    is_superuser = models.BooleanField(default=False)
    last_login = models.DateTimeField(
        _("last login"), auto_now=True, auto_now_add=False)


class School(models.Model):

    PROVINCE = (
        ...
    )

    SCHOOLTYPE = (
       ....
    )
    name = models.CharField(_("Name"), max_length=150, blank=False, null=True)
    abbr = models.CharField(
        _("Abbrivation"), max_length=10, blank=False, null=True)
    zone = models.CharField(
        _("Zone"), max_length=50, choices=PROVINCE)
    Schooltype = models.CharField(
        _("School type"), max_length=50, choices=SCHOOLTYPE)
    schoolemail = models.EmailField(
        _("School email"), max_length=254, blank=False, null=True) editable=False)

    def __str__(self):
        return self.name

this applied model is what is connecting the user and the student model together and it is what i am using though the nested serializer to create the users

class Applied(models.Model):
    class TYPES(models.TextChoices):
        STUDENT = "STUDENT", "Student"
        WORKER = "WORKER", "Worker"
    type = models.CharField(_("type"), choices=TYPES.choices,
                            max_length=150, blank=False, null=True)
    user = models.ForeignKey(User, verbose_name=_(
        "useris"), related_name='useris', on_delete=models.PROTECT, blank=True, null=True)
    school = models.ForeignKey(School, verbose_name=_(
        'school'), related_name="applied", on_delete=models.CASCADE, blank=True, null=True)

serializers.py

class StudentSchoolSerializer(serializers.ModelSerializer):
    user = serializers.CharField()
    school = serializers.CharField()
    type = serializers.HiddenField(
        default=Applied.TYPES.STUDENT
    )

    class Meta:
        model = Applied
        fields = ['school', 'type', 'user']


# register a student
class RegisterSerializerStudent(serializers.ModelSerializer):
    applied = StudentSchoolSerializer(required=True)
    password = serializers.CharField(
        write_only=True, required=True, validators=[validate_password])
    firstname = serializers.CharField(required=True)
    middlename = serializers.CharField(required=True)
    lastname = serializers.CharField(required=True)

    class Meta:
        model = User
        fields = ('firstname', 'middlename', 'sex', 'grade', 'image', 'room', 'type', 'lastname', 'age', 'password',
                    'phone', 'applied')
        extra_kwargs = {'password': {'write_only': True},
                        'firstname': {'required': True},
                        'middelname': {'required': True},
                        'lastname': {'required': True},
                        'age': {'min_value': 1, 'max_value': 100}
                        }

    def create(self, validated_data):
        def create_new_id_number():
            S = 10
            not_unique = True
            while not_unique:
                unique_id = ''.join(random.choices(
                    string.ascii_uppercase   string.digits, k=S))
                if not User.objects.filter(student_id=unique_id):
                    not_unique = False
                return unique_id
        applied = validated_data.pop('applied')
        user = User.objects.create(
            student_id=create_new_id_number(),
            firstname=validated_data['firstname'],
            middlename=validated_data['middlename'],
            lastname=validated_data['lastname'],
            age=validated_data['age'],
            phone=validated_data['phone'],
            image=validated_data['image'],
            room=validated_data['room'],
            sex=validated_data['sex'],
            is_active='True',
            type=User.TYPES.STUDENT,
        )
        user.set_password(validated_data['password'])
        user.save()
        for applies in applied:
            Applied.objects.create(**applies, user=user)
        return user

create method throws the error, creates a User instance but not the Applied instance

CodePudding user response:

applied = validated_data.pop('applied') contained the value of "applied" in your serializer. You did not call StudentSchoolSerializer with the many=true option. So when you do

for applies in applied:
    Applied.objects.create(**applies, user=user)

applies is actually a str, not a dict. So you can't use **.

If you are sending only one "applied", you can save it directly Applied.objects.create(**applied, user=user)

  •  Tags:  
  • Related