I'm working with the django rest framework and the serializer I'm trying to use is creating errors. I'm trying to do something like https://gist.github.com/anonymous/7463dce5b0bfcf9b6767 but I still get the error. the models are
class Visitor(models.Model):
user = models.OneToOneField(User)
check_ins = models.IntegerField(default=0)
#classmethod
def create(cls, username, email, password):
user = User.objects.create_user(username, email, password)
visitor = cls(user=user)
visitor.save()
return visitor
def __str__(self):
return self.user.username
and the default user class and the serializers are
class UserSerializer(serializers.ModelSerializer):
class Meta:
model=User
fields = ('username')
class VisitorSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model=Visitor
fields = ('id','check_ins','user')
I get this error
Got AttributeError when attempting to get a value for field user on serializer VisitorSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the QuerySet instance.
Original exception text was: 'QuerySet' object has no attribute 'user'.
The issue is that you are passing a queryset into your serializer without setting the many flag. The error is telling you that the serializer is trying to access queryset.user when it should be accessing visitor.user, so you need to tell the serializer that there are multiple objects (instead of a single one) by passing many=True.
Related
I am trying to add some annotation in authenticated User using rest-auth
Here is my code in serializers.py
class CustomUserSerializer(UserDetailsSerializer):
test = serializers.IntegerField()
class Meta:
model=User
fields='__all__'
And here is my code in views.py
class CustomUserView(UserDetailsView):
queryset= User.objects.annotate(test=Sum('logs__work_hours'))
serializer_class = CustomUserSerializer
but I am having this error after running the system
**
Got AttributeError when attempting to get a value for field test on
serializer CustomUserSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the User instance.
Original exception text was: 'User' object has no attribute 'test'.
**
In this context, queryset is ignored.
Why? In your program, you're subclassing UserDetailsView from django-rest-auth. That implements get_object() like so:
def get_object(self):
return self.request.user
With no reference to the queryset variable.
You need to override get_object() in CustomUserView, copying the above implementation, and setting a test attribute on the user.
def get_object(self):
user = self.request.user
user.test = ... # put query to get value of test here
return user
I have a database table called Supplier that has a foreign key of User, each User has their own Suppliers. I got the get request working so that it returns all Suppliers in the entire table, but I can not find a way to filter it so I only receive the Suppliers associated with the User requested.
I am accessing this request by this URL:
http://localhost:8000/pm/getsuppliers/primary-key-of-user/
models.py:
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
bio = models.CharField(max_length=200)
email = models.CharField(max_length=200)
class Supplier(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
phone = models.IntegerField()
email = models.EmailField(max_length=200)
views.py:
class getsuppliers(viewsets.ModelViewSet):
queryset = Supplier.objects.all()
serializer_class = GetSuppliersSerializer
lookup_field = 'user'
serializers.py:
class GetSuppliersSerializer(serializers.ModelSerializer):
class Meta:
model=Supplier
fields=['pk','user','name','email','phone']
The error I am receiving:
ERROR: pm.models.Supplier.MultipleObjectsReturned: get() returned more than one Supplier -- it returned 10!
I have done some searching on this error and they are saying to use .filter instead of .all in the view, but I am not sure how to make it return ALL Suppliers for the requested User, this seems like it would only return 1. Maybe I am wrong, hopefully someone has an easy solution!
You'll have to set the serializer's model (inside Meta) to User, and add in a supplier_set field:
class GetUserSuppliersSerializer(serializers.ModelSerializer):
supplier_set = SupplierSerializer(read_only=True, many=True)
class Meta:
model = User
fields = ['supplier_set']
class SuppliersSerializer(serializers.ModelSerializer):
class Meta:
model = Supplier
fields = ['pk','user','name','email','phone']
And also change the viewset queryset to get users.
Edit:
To answer the question in the comment, there are a few different ways to do that based on what you need, one way is to add a to_representation method in your GetUserSuppliersSerializer as such:
def to_representation(self, instance):
response = super().to_representation(instance)
response["supplier_set"] = sorted(response["supplier_set"], key=lambda x: x["pk"])
return response
I am new to django.
I am trying to save User details in another table using One-To-One relation by following Django document but is giving me an error
Models.py
class UserDetails(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE)
phone=models.CharField(max_length=10)
address=models.CharField(max_length=200,default="")
created=models.DateTimeField(auto_now_add=True)
updated=models.DateTimeField(auto_now_add=True)
objects=models.Manager()
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserDetails.objects.create(user=instance,phone="",address="")
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userdetails.save()
Serializer.py
class UserDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = UserDetails
fields= ['phone','address']
class CreateUserSerializer(serializers.ModelSerializer):
user=UserDetailsSerializer()
class Meta:
model =User
fields=['id','url','username','email','password','user']
def create(self, validated_data):
user_data=validated_data.pop('user')
user=User.objects.create(**validated_data)
UserDetails.objects.create(user=user,**user_data)
return user
When i write above in serializer.py user=UserDetailsSerializer(read_only=True) else give me following error
Got AttributeError when attempting to get a value for field user on serializer CreateUserSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the User instance.
Original exception text was: 'User' object has no attribute 'user'.
I found one way to make it work but I have to define every field manually but I want the above serializer to work
Working Serializer.py
class CreateUserSerializer(serializers.HyperlinkedModelSerializer):
last_login=serializers.ReadOnlyField()
date_joined=serializers.ReadOnlyField()
phone=serializers.CharField(source='userdetails.phone')
address=serializers.CharField(source='userdetails.address')
updated=serializers.ReadOnlyField(source='userdetails.updated')
# password=serializers.CharField(style={'input_type':'password'},write_only=True)
class Meta:
model = User
fields=['id','first_name','last_name','username','password','phone','address','url','email','is_superuser','is_staff','last_login','date_joined','updated']
extra_kwargs={
'password':{'write_only':True},
}
def create(self, validated_data):
userdetails_data=validated_data.pop('userdetails')
user=User.objects.create_user(**validated_data)
user.userdetails.phone=userdetails_data.get('phone')
user.userdetails.address=userdetails_data.get('address')
user.save()
return user
Edit1: As suggested by bdbd in comments I replaced user with userdetails in CreateUserSeriailzer it solved Attribute Error but was giving an error while creating user:
(1062, "Duplicate entry '106' for key 'apis_userdetails.user_id'")
So I replaced UserDetails.objects.create(user=user,**user_data)
with user.userdetails.phone=user_data.get('phone') user.userdetails.address=user_data.get('address')
If anyone knows how can I minimize this code further please let me know because I need to create a lot of columns in UserDetails Table so I don't want to add each value in each column manually
Updated Serializer.py (1st One)
class CreateUserSerializer(serializers.ModelSerializer):
#user=UserDetailsSerializer()
userdetails=UserDetailsSerializer()
class Meta:
model =User
fields=['id','url','username','email','password','userdetails']
def create(self, validated_data):
user_data=validated_data.pop('userdetails')
user=User.objects.create_user(**validated_data)
# UserDetails.objects.create(user=user,**user_data)
user.userdetails.phone=user_data.get('phone')
user.userdetails.address=user_data.get('address')
user.save()
return user
Try with this:
If you choose not to use signals:
def create(self, validated_data):
userdetails_data = validated_data.pop('userdetails')
user = User.objects.create_user(**validated_data)
UserDetails.objects.create(user=user, **userdetails_data)
return user
If you choose to use signals:
def create(self, validated_data):
user_data = validated_data.pop('userdetails')
user = User.objects.create_user(**validated_data)
user.userdetails.phone = user_data.get('phone')
user.userdetails.address = user_data.get('address')
user.userdetails.save()
return user
I'm looking to create a model for users to bookmark a recipe. I have the below:
models.py
class RecipeBookmark(models.Model):
recipe = models.ForeignKey(
Recipe, on_delete=models.PROTECT, related_name="bookmarks"
)
bookmarked_by = models.ForeignKey(User, on_delete=models.PROTECT)
bookmarked_at = models.DateTimeField(auto_now_add=True)
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = ["username", "email", "date_joined"]
class RecipeBookmarkSerializer(serializers.ModelSerializer):
bookmarked_by = UserSerializer(read_only=True)
class Meta:
model = models.RecipeBookmark
fields = ["recipe", "bookmarked_by", "bookmarked_at"]
def create(self, validated_data):
request = self.context["request"]
ModelClass = self.Meta.model
instance = ModelClass.objects.create(
**validated_data, **{"bookmarked_by": request.user}
)
return instance
views.py
#permission_classes([IsAuthenticated])
class RecipeBookmarkView(generics.CreateAPIView):
queryset = models.RecipeBookmark.objects.all()
serializer_class = RecipeBookmarkSerializer
urls.py
path("recipes/bookmarks/", PublishedRecipeBookmarkView.as_view()),
I want to perform a lookup, given the recipe id through a POST request, to add the user to the bookmarks field, if the user already exists in the bookmarks field, to remove that user form the field (remove the bookmark). Many users can bookmark a given recipe.
Also, How can a lookup be performed to return recipes that a logged in user has bookmarked via an api endpoint?
Current error with get_or_create():
Error: Internal Server Error
Response body
Download
AttributeError at /api/recipes/bookmarks/
Got AttributeError when attempting to get a value for field recipe on serializer RecipeBookmarkSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the tuple instance.
Original exception text was: 'tuple' object has no attribute 'recipe'.
If you want your serializer to ensure that only one bookmark is created per user per recipe, you can use get_or_create:
def create(self, validated_data):
request = self.context["request"]
ModelClass = self.Meta.model
instance = ModelClass.objects.get_or_create(
**validated_data, **{"bookmarked_by": request.user}
)
return instance
If the bookmark is already present, it will just grab it and return.
Also, How can a lookup be performed to return recipes that a logged in user has bookmarked via an api endpoint?
To support this, you can define ListCreateAPIView to your view and override the queryset like so:
class RecipeBookmarkView(generics.ListCreateAPIView):
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(bookmarked_by=self.request.user)
This will then support getting all the RecipeBookmark that is owned by the current user via GET requests on recipes/bookmarks/
Let us assume I have the following models
class Blog(models.Model):
user = models.ForeignKey(User, null=False, on_delete=models.CASCADE)
class Post(models.Model):
blog = models.ForeignKey(Blog, null=False, on_delete=models.CASCADE)
post = models.TextField()
The problem is that creating a Post will let me set the blog id to anything (that exists). That means that I am able to create a Post object that has a relation to a Blog object that the user does not "own".
However, there are easy and documented ways to prevent the user from accessing objects via the GET method, that are forbidden to them, by filtering the queryset and using check_object_permissions. Example:
class PostViewSet(viewsets.ModelViewSet):
serializer = PostSerializer
def get_queryset(self):
return Post.objects.filter(blog__user=self.request.user)
def check_object_permissions(self, request, obj):
if obj.user != request.user:
raise exceptions.PermissionDenied()
super().check_object_permissions(request, obj)
How do I solve my above issue and prevent creating relations to forbidden objects the smartest/correct way in Django REST framework?
You can add blog validation to the serializer (check Field-level validation doc) and raise error if user dnt have permission to the selected blog:
class PostSerializer(serializers.ModelSerializer):
...
def validate_blog(self, value):
request = self.context['request']
if value.user != request.user:
raise serializers.ValidationError('Blog id doesn't exist')
return value