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)