I'm trying to update my user model by adding a profile picture. I'm stuck on the fact that DRF requires a lookup for PUT requests. But the client doesn't know the user pk they just have a login token. The user would normally be obtained in a view using request.user
views.py:
class UploadViewset(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = serializers.ImageUploadSerializer
parser_classes = [MultiPartParser]
models.py
class User(AbstractUser):
profile_image = models.ImageField(upload_to='testuploads/', null=True)
serializers.py
class ImageUploadSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
'profile_image',
)
Posting give the following
"detail": "Method \"PUT\" not allowed.",
Which is caused by the fact that there is no individual resource identifier in the URL. Of course, the resource (a user) could be obtained from the request because it is an authenticated request.
How can I update the user model using DRF?
Update
urls.py
router.register(r'upload', views.UploadViewset)
I'm posting to endpoin upload/
Related
I am building a chat application with django rest framework and I m currently working on messages. This are my models:
from django.db import models
from django.contrib.auth.models import User
class Message(models.Model):
text = models.CharField(max_length=500)
datetime = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE);
I am using the Django auth User model. This is my ModelViewSet for the messages:
class MessageViewSet(ModelViewSet):
queryset = Message.objects.all()
serializer_class = MessageSerializer
And these are my serializers:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username']
class MessageSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Message
fields = '__all__'
And this is my API:
The code I've written so far works really well for the GET functionally I want. I want for each message to get the username of the user it belongs to. But now I want the following thing: when I POST a new message, I want to be able to specify which user it belongs to by specifying the user's id. Right now I have only the "text" field in the POST section. I need to add a "user" field which takes in an integer (the user primary key) to specify which user the message belongs to. How should I refactor my code in order to do that?
Because you've overridden the user field and set it to read_only=True, you cannot set a user when you're creating/updating a model.
If you just need the user's username, I'd suggest you to add a username field into MessageSerializer directly instead:
class MessageSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username', read_only=True)
class Meta:
model = Message
fields = '__all__'
Now you'll get this payload instead:
{
"id": 1,
"user": 1,
"username": "timi",
...
And you should be able to set a user id now.
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/
A newbie here. This is a Django related question.
How can I save a newly registered user to the User Model (auth.model)? Currently, the only account which is seen inside the admin panel -- under Users (Authentication and Authorization section) is the superuser (aka me).
I am using DRF (Rest framework) in order to register a user and not an HTML form.
models.py:
class Register(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
username = models.CharField(max_length = 100)
email = models.EmailField(max_length = 100)
password = models.CharField(max_length = 100)
def __str__(self):
return self.name
views.py:
class RegisterView(APIView):
def post(self, request, format=None):
serializer = RegisterSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response("Thank you for registering", status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py:
from rest_framework import serializers
from .models import Register
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = Register
fields = ['username', 'email', 'password']
When registering a new user via POSTMAN, the data is saved within the Register model (which is fine) but my issue is that it's not seen within the Users model.
Any feedback is highly appreciated. Thank you.
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
I am new to django and very confused. I am using django as the backend API for my angular application.
I want to add few more details to the User model so I added the following to my models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
company_name = models.CharField(max_length=100)
I am using an application to add rest authentication support: https://github.com/Tivix/django-rest-auth
Using this application I can edit a users profile with this URL doing a POST request: http://localhost:8080/rest-auth/user/
Question
How can I update the custom field company_name? while editing a users profile?
What I've tried
I tried to override the UserDetailsSerializer that the application provides but it isn't having any effect.
This is what I tried adding to my applications serializers.py
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserProfile
fields = ('company_name',)
class UserDetailsSerializer(serializers.ModelSerializer):
profile = UserProfileSerializer(required=True)
class Meta:
model = models.User
fields = ('id', 'username', 'first_name', 'last_name', 'profile')
If you are using django rest framework, then you basically want to have an update method on your view class for UserProfile that takes in the user id as a request param. Then in that method you want to use the ORM to get a model object for the given userprofile id, set the property that was also passed as a param, and save the changed model object. Then generate a success response and return it.
You can read more about how to do this here: http://www.django-rest-framework.org/api-guide/views/