I have an "Event" model and it has a many2many field with default user model.
class Event(models.Model):
name = models.CharField(max_length=50)
type = models.CharField(max_length=50)
location = models.CharField(max_length=255)
start_hour = models.CharField(max_length=50)
end_hour = models.CharField(max_length=50)
creator = models.CharField(max_length=50)
info = models.CharField(max_length=255, default='')
users = models.ManyToManyField(User)
def __str__(self):
return self.name
Now, I am trying to update this many2many field like following;
//my serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id',)
class EventSerializer(serializers.ModelSerializer):
users = UserSerializer(many=True)
class Meta:
model = Event
fields = ('id', 'users')
def update(self, instance, validated_data):
submitted_users = validated_data.get('users')
if submitted_users:
for user in submitted_users:
user_instance = User.objects.get(id=user.id)
instance.users.add(user_instance)
instance.save()
return instance
//views.py
class UpdateParticipants(generics.RetrieveUpdateDestroyAPIView):
queryset = Event.objects.all()
serializer_class = EventSerializer
However, I am getting an error like in the belowe image
// this is the APIView that I used
Can you try with this code below?
Models
Example
class UserModel(User):
pass
class Event(models.Model):
name = models.CharField(max_length=50)
type = models.CharField(max_length=50)
location = models.CharField(max_length=255)
start_hour = models.CharField(max_length=50)
end_hour = models.CharField(max_length=50)
creator = models.CharField(max_length=50)
info = models.CharField(max_length=255, default='')
users = models.ManyToManyField(User, related_name='event_user') # Set related name to User object for Rest Framework
def __str__(self):
return self.name
Serializer
class EventSerializer(serializers.ModelSerializer):
users = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), required=False)
class Meta:
model = Event
class Userserializer(serializers.ModelSerializer):
event_user = EventSerializer(many=True)
class Meta:
model = UserModel
def update(self, instance, validated_data):
event_user = validated_data.pop('event_user', None)
print (event_user)
if 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
Views
class EventView(APIView):
permission_classes = (AllowAny,)
def get(self):
qv = User.objects.all()
serializer = Userserializer(qv, many=True)
return Response(data={'users': serializer.data}, status=status.HTTP_200_OK)
def put(self, request, user_id):
instance = Event.objects.get(user=user_id)
serializer = Userserializer(instance=instance,data=request.data, partial=True)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(data={'users': serializer.data}, status=status.HTTP_200_OK)
return Response(data={'users': serializer.errors}, status=status.HTTP_403_FORBIDDEN)
URL-s
urlpatterns = [
url(r'^users/(?P<user_id>\d+)',EventView.as_view())
]
JOSN exaple for put
JSON
{
"id":1,
"event_user":[{
"name":"Changed Name"
}]
}
Related
I am trying to customize get_queryset() in my DocumentViewSet so the GET method will return all Document objects created by request.user (currently logged in user).
However, I am stuck in this error:django.core.exceptions.ValidationError: ['“AnonymousUser” is not a valid UUID.']
I assume this is caused by getting AnonymousUser as my self.request.user. The weird part is that my other APIView that deals with request.user are working flawlessly; The only difference I could find between the two is type of viewset: ModelViewSet vs APIView.
Would appreciate any help!
document.views
class DocumentViewSet(viewsets.ModelViewSet):
model = Document
serializer_class = DocumentSerializer
list_serializer_class = DocumentListSerializer
permission_classes = (AllowAny,)
def get_queryset(self):
user = self.request.user
return Document.objects.filter(user=user)
def get_serializer_class(self):
if self.action == "list":
if hasattr(self, "list_serializer_class"):
return self.list_serializer_class
return super(DocumentViewSet, self).get_serializer_class()
def perform_create(self, serializer):
print(self.request.user)
serializer.save(user=self.request.user)
document.serializers
class DocumentSerializer(serializers.ModelSerializer):
id = HashidSerializerCharField(source_field="documents.Document.id", read_only=True)
question_blocks = QuestionBlockSerializer(many=True)
outline_blocks = OutlineBlockSerializer(many=True)
source_cards = SourceSerializer(many=True, required=False)
user = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault()
)
class Meta:
model = Document
fields = "__all__"
def create(self, validated_data):
question_blocks = validated_data.pop("question_blocks")
outline_blocks = validated_data.pop("outline_blocks")
source_cards = validated_data.pop("source_cards")
document = Document.objects.create(**validated_data)
for qBlock in question_blocks:
QuestionBlock.objects.create(document=document, **qBlock)
for oBlock in outline_blocks:
OutlineBlock.objects.create(document=document, **oBlock)
for sCard in source_cards:
Source.objects.create(document=document, **sCard)
document.save()
return document
class DocumentListSerializer(serializers.ModelSerializer):
id = HashidSerializerCharField(source_field="documents.Document.id", read_only=True)
class Meta:
model = Document
fields = ("id", "title", "template", "updated")
document.models
class Document(models.Model):
id = HashidAutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
title = models.CharField(max_length=100, default="Untitled")
template = models.CharField(max_length=100, default="")
editorState = models.JSONField(default=[])
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
user.models
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
username = None
first_name = models.CharField(max_length=100, default="unknown")
last_name = models.CharField(max_length=100, default="unknown")
profile_pic = models.CharField(max_length=200, default="unknown")
email = models.EmailField(unique=True, db_index=True)
secret_key = models.CharField(max_length=255, default=get_random_secret_key)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
class Meta:
swappable = "AUTH_USER_MODEL"
users.api & selectors
This is the APIView which returns information of the user currently logged-in; It is working flawlessly.
# users.api
class UserMeApi(APIView):
def get(self, request, *args, **kwargs):
return Response(user_get_me(user=request.user))
# users.selectors
def user_get_me(*, user: User):
return {
"id": user.id,
"name": user.name,
"email": user.email,
"first_name": user.first_name,
"last_name": user.last_name,
"profile_pic": user.profile_pic,
}
EDIT: added document.serializer and rest of the viewset code
My mistake! The origin of the problem was on the frontend; I forgot to include withCredentials:true in axios GET request.
axios
.get<DocumentList>(
`${process.env.NEXT_PUBLIC_API_URL}/drafts/?format=json`,
{
withCredentials: true, //forgot this part!
}
)
.then(function (response) {
setDrafts(response.data.results);
});
I don't know why I have this error, I didn't have it befor, I'm sure that I'm logged in before trying call the api, please find below the code :
views.py
class AlbumCreate(generics.CreateAPIView):
serializer_class = AlbumsSerializer
def perform_create(self, serializer):
owner2 = self.request.user
serializer.save(owner=owner2)
class AlbumList(generics.ListAPIView):
permission_classes = [IsAuthenticated]
queryset = Album.objects.all()
serializer_class = AlbumsSerializer
class AlbumDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Album.objects.all()
serializer_class = AlbumsSerializer
Serializer
class AlbumsSerializer(serializers.ModelSerializer):
owner = serializers.StringRelatedField(read_only=True)
class Meta:
model = Album
fields = "__all__"
models
def upload_path(instance, filname):
return '/'.join(['covers', str(instance.title), filname])
class Album(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=30)
cover = models.IntegerField(default=0)
photos_number = models.IntegerField(default=0)
image = models.ImageField(blank=True, null=True, upload_to=upload_path)
def __str__(self):
return self.title
django rest framework gives me this error when i am trying to perform a post request to create a new order even though i specified the usr value in my serializer.save method.
here is my view
class OrderListCreateAPI(generics.ListCreateAPIView):
serializer_class = OrderSerializer
permission_classes = [IsOwner]
def get_queryset(self):
user = self.request.user
return Order.objects.filter(user= user)
def perform_update(self, serializer):
instance = serializer.save(user = self.request.user)
order = Order.objects.get(id = instance['id'].value)
order.item.quantity -= order.quantity
order.save()
my models.py
payment_methods = ((1,'credit card'),(2, 'cash'),(3, 'paypal'))
class Product(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
image = models.ImageField(upload_to = 'images/', height_field=300, width_field=300)
price = models.IntegerField()
quantity = models.IntegerField()
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='products')
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Order(models.Model):
item = models.ForeignKey(Product, on_delete= models.CASCADE, related_name='orders')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders')
quantity = models.IntegerField()
payment_options = models.CharField(choices=payment_methods, max_length=50)
Delivery = models.CharField(max_length=200)
my serializers
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['id', 'title', 'description','image', 'price', 'quantity', 'seller', 'date']
read_only_fields = ['date', 'seller']
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
read_only_fields = ['user']
You need to use perform_create and not perform_update in your view.
class OrderListCreateAPI(generics.ListCreateAPIView):
serializer_class = OrderSerializer
permission_classes = [IsOwner]
def get_queryset(self):
user = self.request.user
return Order.objects.filter(user= user)
def perform_create(self, serializer):
instance = serializer.save(user = self.request.user)
order = Order.objects.get(id = instance['id'].value)
order.item.quantity -= order.quantity
order.save()
I want to create category in django rest framework with serializer.
name will be provided from frontend input field.
I'm getting the user_id from user = request.user and cafe_id from request.user.cafe.
I need to create category with name, user_id and cafe_id.
How can I do this?
here is the model
class Category(models.Model):
user = models.ForeignKey(User, related_name="cat", blank=True, null=True, on_delete=models.CASCADE)
cafe = models.ForeignKey(Cafe, related_name="category", blank=True, null=True, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
class Meta:
verbose_name_plural='Categories'
def __str__(self):
return self.name
Here is the serializer
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['name']
and the views.py
#api_view(['POST'])
#permission_classes((IsAuthenticated,))
def categoryCreate(request):
user = request.user
user_id = Category(user = user)
cafe = request.user.cafe
cafe_id = Category(cafe = cafe)
serializer = CategorySerializer(user_id, cafe_id, data=request.data)
data={}
if serializer.is_valid():
serializer.save()
data["success"] = "Category Has Been Created!"
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Thank You In Advance
you can to this
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
class CafeSerializer(serializers.ModelSerializer):
class Meta:
model = Cafe
fields = "__all__"
class CategorySerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
cade = CafeSerializer(read_only=True)
class Meta:
model = Category
fields = ['name', 'user', 'cafe']
i have the below config and i would like to map the url field in UserProfileView to the related user's username instead of the default pk field
currently the url looks likes below, appreciate any help
{
"user": 23,
"bio": "My bio",
"created_on": "2020-06-12T21:24:52.746329Z",
"url": "http://localhost:8000/bookshop/bio/8/?format=api"
},
what i am looking for is
{
"user": 23, <--- this is the user <pk>
"bio": "My bio",
"created_on": "2020-06-12T21:24:52.746329Z",
"url": "http://localhost:8000/bookshop/bio/username/?format=api"
},
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
bio = models.CharField(max_length=255)
created_on = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.username
views.py
class UserProfileViewSets(viewsets.ModelViewSet):
authentication_classes = [TokenAuthentication, ]
permission_classes = [rest_permissions.IsAuthenticated, permissions.UserProfileOwnerUpdate, ]
queryset = models.UserProfile.objects.all()
serializer_class = serializers.UserProfileSerializer
renderer_classes = [renderers.AdminRenderer, renderers.JSONRenderer, renderers.BrowsableAPIRenderer, ]
# lookup_field = 'user.username'
def perform_create(self, serializer):
serializer.save(user=self.request.user)
serializer.py
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserProfile
fields = ['user', 'bio', 'created_on', 'url']
extra_kwargs = {
'last_updated': {
'read_only': True
},
'user': {
'read_only': True
},
}
after struggling and reading many articles, I did it and posting down the solution if anybody was looking for the same use case.
the fields are being related to each other by OneToOne relationship
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
bio = models.CharField(max_length=255)
created_on = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.username
class User(AbstractBaseUser, PermissionsMixin):
""""
Customizes the default user account
"""
email = models.EmailField(unique=True, help_text='username is the email address')
first_name = models.CharField(max_length=40, blank=False)
last_name = models.CharField(max_length=40, blank=False)
date_joined = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
username = models.CharField(max_length=15, unique=True, null=True, blank=False,
validators=(validators.UnicodeUsernameValidator, ))
is_borrower = models.BooleanField(default=False)
The serializer is a HyperlinkedModelSerializer, as shown below the user SerializerField is PrimaryKeyRelatedField and it is being related to another column/field in the User model user.username - i made this as the default PrimaryKeyRelatedField is the pk and i dont want to expose that on the API
the url key is customized to be HyperlinkedRelatedField to point to the above field - the user with a viewname user-related
serializer.py
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.PrimaryKeyRelatedField(source='user.username', read_only=True)
url = serializers.HyperlinkedRelatedField(read_only=True, view_name='user-detail', )
class Meta:
model = models.UserProfile
fields = ['user', 'bio', 'created_on', 'url']
extra_kwargs = {
'last_updated': {
'read_only': True
},
'user': {
'read_only': True
},
}
on the views, i defined the lookup_field to be user and override the get_object method as now the queryset should be filtered by the username
views.py
class UserProfileViewSets(viewsets.ModelViewSet):
authentication_classes = [TokenAuthentication, ]
permission_classes = [rest_permissions.IsAuthenticated, permissions.UserProfileOwnerUpdate, ]
queryset = models.UserProfile.objects.all()
serializer_class = serializers.UserProfileSerializer
renderer_classes = [renderers.AdminRenderer, renderers.JSONRenderer, renderers.BrowsableAPIRenderer, ]
lookup_field = 'user'
def perform_create(self, serializer):
serializer.save(user=self.request.user)
def get_object(self):
queryset = self.filter_queryset(models.UserProfile.objects.get(user__username=self.kwargs.get('user')))
return queryset
EDIT:
I did the requirements in another approach and think this one is more neat way , so below the modifications.
You need to create anew customized HyperLinkedIdentityField where you over right the kwargs, check the below kwargs, the value is mapped to the related model where a OneToOneForgienKey deifined
class AuthorHyperLinkedIdentityField(serializers.HyperlinkedIdentityField):
def get_url(self, obj, view_name, request, format):
if hasattr(obj, 'pk') and obj.pk is None:
return None
return self.reverse(view_name, kwargs={
'obj_username': obj.author.username
}, format=format, request=request)
on the view you overright the lookup_field with the kwargs defined in the CustomizedField
class AuthorViewSet(viewsets.ModelViewSet):
serializer_class = serializers.AuthorSerializer
queryset = models.Author.objects.all()
renderer_classes = [renderers.JSONRenderer, renderers.BrowsableAPIRenderer, renderers.AdminRenderer]
# the below is not used but i keep it for reference
# lookup_field = 'author__username'
# the below should match the kwargs in the customized HyperLinkedIdentityField
lookup_field = 'obj_username'
The final serializer would look like
class AuthorSerializer(serializers.HyperlinkedModelSerializer):
"""
Serializers Author Model
"""
# first_name = serializers.SlugRelatedField(source='author', slug_field='first_name',
# read_only=True)
# last_name = serializers.SlugRelatedField(source='author', slug_field='last_name',
# read_only=True)
author = serializers.PrimaryKeyRelatedField(queryset=models.User.objects.filter(groups__name='Authors'),
write_only=True)
name = serializers.SerializerMethodField()
username = serializers.PrimaryKeyRelatedField(source='author.username', read_only=True)
# the below commented line is building the URL field based on the lookup_field = username
# which takes its value from the username PrimaryKeyRelatedField above
# url = serializers.HyperlinkedRelatedField(view_name='user-detail', read_only=True)
url = AuthorHyperLinkedIdentityField(view_name='author-detail', read_only=True)
class Meta:
model = models.Author
fields = ['author', 'name', 'username', 'url', ]
def get_name(self, author):
return '%s %s' % (author.author.first_name, author.author.last_name)
below the Author Model for your reference
class Author(models.Model):
"""
A Model to store the Authors info
"""
author = models.OneToOneField(User, on_delete=models.CASCADE, related_name='authors')
is_author = models.BooleanField(default=True, editable=True, )
class Meta:
constraints = [
models.UniqueConstraint(fields=['author'], name='check_unique_author')
]
def __str__(self):
return '%s %s' % (self.author.first_name, self.author.last_name)
def author_full_name(self):
return '%s %s' % (self.author.first_name, self.author.last_name)