I have some issues with updating my database, in my serializer I use extended via OneToOneField django user model with two extra fields with user image and his motto. So I think the problem with instance in my serializer, but I can't figure out how to do that.
#core.models
class MangaUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
user_image = models.ImageField(upload_to='upicks')
user_motto = models.CharField(max_length=256)
#api.serializers
class UserSerializer(serializers.ModelSerializer):
#mangauser_set = serializers.SerializerMethodField()
user_image = serializers.ImageField(source='mangauser.user_image')
user_moto = serializers.CharField(source='mangauser.user_motto')
class Meta:
model = User
fields = ['id', 'username', 'email', 'password', 'user_image', 'user_motto']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
def update(self, instance, validated_data):
for attr, value in validated_data.items():
if attr == 'password':
instance.set_password(value)
else:
setattr(instance, attr, value)
instance.save()
return instance
#api.view
class GetUserInfo(APIView):
permission_classes = (IsAuthenticated,)
serializer_class = UserSerializer
def get(self, request, *args, **kwargs):
# serializer to handle turning our `User` object into something that
# can be JSONified and sent to the client.
serializer = self.serializer_class(request.user, context={"request":request})
return Response(serializer.data, status=status.HTTP_200_OK)
def put(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
#response
ValueError at /api/v1/userinfo/
Cannot assign "{'user_motto': 'js говно'}": "User.mangauser" must be a "MangaUser" instance.
What you did seems pretty complicated to me.
Did you read : https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#extending-the-existing-user-model ?
This is what I use :
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
def get_profile(self):
return Profil.objects.get(user=self)
class Meta(AbstractUser.Meta):
db_table = 'auth_user'
class Profil (models.Model):
user = models.OneToOneField (settings.AUTH_USER_MODEL, unique=True, editable=False, help_text="1-to-1 to user", on_delete=models.CASCADE)
image_one = models.ImageField(upload_to="upload_to/images/", blank=True, null=True, help_text=_(u"Image qui sera accolée à votre profil."))
class Meta:
order_with_respect_to = 'user'
Related
cannot manage a POST method-query to url /users/{pk}/subscribe
Accesing this url should insert a record in db using Subscription model. No data is passed in body when accessing this url
model.Subscription
class Subscription(models.Model):
user = models.ForeignKey(User,
on_delete=models.CASCADE,
related_name='subscriber',
)
author = models.ForeignKey(User,
on_delete=models.CASCADE,
related_name='subscribing'
)
a router to users
router.register(r'users', CustomUserViewSet, basename='users')
and the additional URL that accepts POST query in the CustomUserViewSet
class CustomUserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = CreateUserSerializer
#action(
detail=True,
methods=['post', 'delete'],
permission_classes=[IsAuthenticated],
)
def subscribe(self, request, *args, **kwargs):
user = request.user
author_pk = int(kwargs.get('pk'))
author_to_subscribe = get_object_or_404(User, id=author_pk)
if request.method == 'DELETE':
return Response(status=status.HTTP_204_NO_CONTENT)
if user.pk == author_pk:
data = {
"errors": "Cannot subscribe to yourself"
}
return Response(status=status.HTTP_400_BAD_REQUEST,
data=data)
# q = Subscription.objects.filter(user=user)
serializer = SubscriptionCreateSerializer(data=request.data)
serializer.is_valid()
serializer.save(user=user, author=author_to_subscribe)
return Response(
status=status.HTTP_200_OK)
Method shoould return serialized data from another serializer (SubscriptionSerializer. So i have 2 serializer for subscription: for viewing and creating
At this point in Postman getting error
class SubscriptionCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields = '__all__'
def create(self, validated_data):
pass
class SubscriptionSerializer(serializers.ModelSerializer):
email = serializers.EmailField(source='author.email')
id = serializers.IntegerField(source='author.id')
username = serializers.CharField(source='author.username')
first_name = serializers.CharField(source='author.first_name')
last_name = serializers.CharField(source='author.last_name')
is_subscribed = serializers.BooleanField(default=True)
recipes = serializers.SerializerMethodField()
def get_recipes(self, obj):
qs = obj.author.recipes.all()
return RecipePublicSerializer(qs, many=True).data
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['recipes_count'] = instance.author.recipes.count()
return representation
def to_internal_value(self, data):
recipes_data = data['recipes']
return super().to_internal_value(recipes_data)
class Meta:
model = Subscription
fields = ('email',
'id',
'username',
'first_name',
'last_name',
'is_subscribed',
'recipes',)
class CreateUserSerializer(serializers.Serializer):
"""Serializer for creating user objects"""
username = serializers.CharField(
validators=[
UniqueValidator(queryset=User.objects.all())
]
)
email = serializers.EmailField(
validators=[
UniqueValidator(queryset=User.objects.all())
]
)
first_name = serializers.CharField()
last_name = serializers.CharField()
password = serializers.CharField()
is_subscribed = serializers.SerializerMethodField()
#classmethod
def get_is_subscribed(self, object):
return True
def validate_username(self, value):
if value.lower() == "me":
raise serializers.ValidationError("Username 'me' is not valid")
return value
def create(self, validate_data):
user = User(**validate_data)
user.set_password(validate_data['password'])
user.save()
return user
class Meta:
fields = ('username',
'email',
'first_name',
'last_name',
'id',
'is_subscribed',)
read_only_fields = ('id',)
model = User
As I understood from question you would like to get user from request and id of author to subscribe from url. You pass to serializer empty body of your post request, but according to your serializer you should pass user and author fields, so to get serializer works you need to pass this parameters as data to serializer instance.
subscription_data = {
'user': request.user,
'author': author_to_subscribe
}
serializer = SubscriptionCreateSerializer(data=subscription_data)
serializer.is_valid(raise_exception=True)
serializer.save()
And also remove def save() method from your SubscriptionCreateSerializer it overwrites method which should put info about subscription in DB, so in your case it does nothing instead.
I don't feel like I get an idea on how it should work...
I do have 2 models:
Group is my custom model I wanna save.
class Group(models.Model):
name = models.CharField(max_length=40)
subject = models.CharField(max_length=40)
user = models.ForeignKey('User', on_delete=models.CASCADE)
User is a standard user model for the application. I'm skipping UserManager for now. Simply said User can have multiple groups.
class User(AbstractBaseUser, PermissionsMixin):
name = models.CharField(max_length=40, null=True, blank=True)
surname = models.CharField(max_length=40, null=True, blank=True)
objects = UserManager()
A serializer for the custom model:
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ('id', 'name', 'subject', 'user')
And a viewset with overwritten create method:
class GroupViewSet(viewsets.ModelViewSet):
serializer_class = GroupSerializer
def create(self, request, *args, **kwargs):
group = group.objects.create(user=self.request.user)
serializer = GroupSerializer(group)
return Response(serializer.data)
When calling a POST a new Group is created. The Group has relation to the User, but other fields (name, subject) are empty.
On the other hand, when doing the request serialization, the User on the object is empty.
def create(self, request, *args, **kwargs):
serializer = GroupSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
How do I connect those 2 to make it work?
That makes sense since you never used the serializer to deserialize the request, and thus read the details passed in the POST request. You can work with:
def create(self, request, *args, **kwargs):
serializer = GroupSerializer(data=request.data)
if serializer.is_valid():
serializer.save(user=self.request.user)
else:
# return response when the serializer rejects the input
pass
# return a response
pass
In the serializer you mark the user field as read_only, to prevent the serializer from failing in case the user was not part of the request:
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ('id', 'name', 'subject', 'user')
read_only_fields = ('user', )
I was using a standard Django user model but then requirements changed and I had to resort to an abstract user model. I updated my serializer to reflect the changes in model fields as well as form fields but I'm now getting an error when attempting to creating a user object. The error message is KeyError: 'unl_email' but also shows up for all the other fields. The validated_data for the create() function in serializers.py appears to be empty when I print. What am I doing wrong?
Views.py
class SignUp(CreateAPIView):
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
serializer = SignUpSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
return Response({"success": "Registration succesful"}, status=status.HTTP_201_CREATED)
def perform_create(self, serializer):
serializer.save()
Models.py
class User(AbstractUser):
unl_num = models.AutoField(primary_key=True)
unl_usu_num = models.IntegerField(blank=True,null =True)
unl_date_added = models.DateField(default=timezone.now)
unl_name = models.CharField(max_length=256)
unl_surname = models.CharField(max_length=256)
unl_email = models.EmailField(max_length=200, unique=True)
unl_mobile = models.CharField(max_length=256)
unl_identity= models.CharField(max_length=256)
unl_oss_num= models.CharField(max_length=256)
unl_race= models.CharField(max_length=32)
unl_gender= models.CharField(max_length=32)
unl_disability= models.CharField(max_length=32)
unl_location= models.CharField(max_length=256)
unl_province= models.CharField(max_length=256)
unl_avatar= models.CharField(max_length=256)
USERNAME_FIELD = 'unl_email'
Serializers.py
class SignUpSerializer(serializers.Serializer):
class Meta:
model = User
fields = ('unl_email','password','unl_name',
'unl_surname','unl_mobile','unl_identity',
'unl_oss_num','unl_race','unl_gender','unl_disability',
'unl_location','unl_province','unl_avatar'
)
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user = User.objects.create(
unl_email=validated_data['unl_email'],
unl_name=validated_data['unl_name'],
unl_surname=validated_data['unl_surname'],
unl_mobile=validated_data['unl_mobile'],
unl_identity=validated_data['unl_identity'],
unl_oss_num=validated_data['unl_oss_num'],
unl_race=validated_data['unl_race'],
unl_gender=validated_data['unl_gender'],
unl_disability=validated_data['unl_disability'],
unl_location=validated_data['unl_location'],
unl_province=validated_data['unl_province'],
unl_avatar=validated_data['unl_avatar']
)
user.set_password(validated_data['password'])
user.save()
return user
You're using serializers.Serializer and then specifying a model attribute for that serializer, which it does not use. Change it to serializers.ModelSerializer instead.
I' extending the default Django user model to make a customised user profile with additional fields.The following are the related components.
models.py
class CandidateProfile(models.Model):
user = models.OneToOneField(
User, on_delete=models.CASCADE, related_name="user")
exp = models.IntegerField(null=True, blank=True)
serilaizers.py
class CandidateProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source='pk', read_only=True)
username = serializers.CharField(source='user.username')
email = serializers.CharField(source='user.email')
groups = serializers.RelatedField(read_only=True)
password = serializers.CharField(max_length=128, source='user.password,read_only=True')
class Meta:
model = CandidateProfile
fields = ('id', 'username', 'password', 'email', 'groups')
depth = 1
def update(self, instance, validated_data):
print("In Update" + '*' * 50)
user = User.objects.get(pk=instance.user.pk)
user = instance.user
user.email = validated_data.get('user.email', user.email)
user.first_name = validated_data.get('user.first_name',
user.first_name)
user.last_name = validated_data.get('user.last_name', user.last_name)
user.save()
instance.gender = validated_data.get('gender', instance.gender)
instance.save()
return instance
def create(self, validated_data):
print('*' * 100)
print(validated_data)
user_data = validated_data.pop('user')
print(user_data)
user = User.objects.create_user(**user_data)
g = Group.objects.get(name="Candidate")
g.user_set.add(user)
user.save()
print(validated_data)
print('*' * 100)
profile = CandidateProfile.objects.create(user=user, **validated_data)
return user
views.py
class CandidateRegister(APIView):
def get(self, request, format=None):
candidate_list = User.objects.filter(groups=Group.objects.get(
name="Candidate"))
serializer = CandidateProfileSerializer(candidate_list, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = CandidateProfileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I've succcessfully created the user profile as well as the extended Candidate profile.But i'm encoutering an error on doing the same as follows :
Got AttributeError when attempting to get a value for field `username` on serializer `CandidateProfileSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'CandidateProfile' object has no attribute 'username'.
Even with this execpiton the User profile and the related Candidate profile is created.
You can use the SerializerMethodField from docs like -
class CandidateProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source='pk', read_only=True)
username = serializers.SerializerMethodField()
def get_username(self, obj):
return obj.user.username
first, in models.py
class UserComment(models.Model):
user = models.ForeignKey(User)
rate = models.IntegerField()
description = models.CharField(max_length=512)
createTime = models.DateTimeField(auto_now=True)
def __unicode__(self):
return '<UserComment {%s %d}>' % (self.user.username, self.rate)
then, serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', )
class UserCommentSerializer(serializers.ModelSerializer):
user = UserSerializer(required=False)
class Meta:
model = UserComment
views.py
class UserCommentViewSet(viewsets.ModelViewSet):
queryset = UserComment.objects.all()
serializer_class = UserCommentSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.DATA, files=request.FILES)
serializer.is_valid()
print serializer.errors
print serializer.data
return super(UserCommentViewSet, self).create(request, *args, **kwargs)
then i post json data
{"user":{"id":"1","username":"watsy"},"rate":"5","description":"hello"}
i think,it will work. and insert it to db, but i get errors.
{"user": [{"username": ["User with this Username already exists."]}]}
>_<, I have no idea.
You need to make few changes to your serializer:
class UserCommentSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = UserComment
depth = 1
Now pass this JSON dict in your request:
{"user":"1", "rate":"5", "description":"hello"}