I've got two models: User and Ticket. Ticket has one User, User has many Tickets
I've accomplished that when i go to url /users/1/tickets, i'm getting the list of user's tickets.
I want to use hyperlinked relations, and here is what i see in my User model representation:
"tickets": [
"http://127.0.0.1:8000/tickets/5/",
"http://127.0.0.1:8000/tickets/6/"
]
But I want it to be like
"tickets": "http://127.0.0.1:8000/users/1/tickets"
Is there a way to do that with DRF?
The url:
url(r'^users/(?P<user_pk>\d+)/tickets/$',
views.TicketsByUserList.as_view(),
name='myuser-tickets'),
The view:
class TicketsByUserList(generics.ListAPIView):
model = Ticket
serializer_class = TicketSerializer
def get_queryset(self):
user_pk = self.kwargs.get('user_pk', None)
if user_pk is not None:
return Ticket.objects.filter(user=user_pk)
return []
User serializer (i tried to play with tickets field definition, changing type, view_name, but with no effect):
class UserSerializer(serializers.HyperlinkedModelSerializer):
tickets = serializers.HyperlinkedRelatedField(many=True, view_name='ticket-detail')
class Meta:
model = MyUser
fields = ('id', 'nickname', 'email', 'tickets')
Ticket serializer:
class TicketSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.HyperlinkedRelatedField(view_name='myuser-detail')
liked = serializers.Field(source='liked')
class Meta:
model = Ticket
fields = ('id', 'user', 'word', 'transcription', 'translation', 'liked', 'created', 'updated')
You can use a SerializerMethodField to customize it. Something like this:
class UserSerializer(serializers.HyperlinkedModelSerializer):
tickets = serializers.SerializerMethodField('get_tickets')
def get_tickets(self, obj):
return "http://127.0.0.1:8000/users/%d/tickets" % obj.id
class Meta:
model = MyUser
fields = ('id', 'nickname', 'email', 'tickets')
I hard-wired the URL in there for brevity, but you can do a reverse lookup just as well. This basically just tells it to call the get_tickets method instead of the default behavior in the superclass.
For the record, here is an example of the full solution based on Joe Holloway's answer:
from rest_framework.reverse import reverse
class WorkProjectSerializer(serializers.CustomSerializer):
issues = drf_serializers.SerializerMethodField()
def get_issues(self, obj):
request = self.context.get('request')
return request.build_absolute_uri(reverse('project-issue-list', kwargs={'project_id': obj.id}))
class Meta:
model = WorkProject
fields = '__all__'
Related
I serialize the field named "product" with ProductSerializer() inside OrderItemSerializer().
That's what I want.
class OrderItemSerializer(serializers.ModelSerializer):
product = ProductSerializer()
class Meta:
model = models.OrderItem
fields = ('id','order', 'product', 'quantity')
The output is;
But when I try to request with POST Method needs to send Product as a dictionary, just giving the id value is not enough.
How can I POST by sending only the id value?
I haven't written anything about the operation yet. Default ModelViewSet
class OrderItemViewSet(ModelViewSet):
queryset = OrderItem.objects.all()
serializer_class = serializers.OrderItemSerializer
permission_classes = (IsOwnerOrNot, IsAuthenticated)
def get_queryset(self):
user = self.request.user
return self.filter_queryset(queryset=self.queryset.filter(order__user=self.request.user))
If you're supporting writable nested representations you'll need to write .create() or .update() methods that handle saving multiple objects.
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ['username', 'email', 'profile']
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user
I am trying to check if the user id not equal to 1 then he should not be able to update few fields. I tried something similar to the following code but it did not work because of the following issues
self.user.id don't actually return the user I need to get the authenticated user in different why?
the def function maybe should have a different name like update?
also the general way maybe wrong?
class ForAdmins(serializers.ModelSerializer)):
class Meta:
model = User
fields = '__all__'
class ForUsers(serializers.ModelSerializer)):
class Meta:
read_only_fields = ['email','is_role_veryfied','is_email_veryfied']
model = User
fields = '__all__'
class UsersSerializer(QueryFieldsMixin, serializers.ModelSerializer):
def customize_read_only(self, instance, validated_data):
if (self.user.id==1):
return ForAdmins
else:
return ForUsers
class Meta:
# read_only_fields = ['username']
model = User
fields = '__all__'
You can make the decision which serializer you want to pass from your views
or
you can do it inside modelSerializer update method.
for getting user from Serializer class Try:
request = self.context.get('request', None)
if request:
user = request.user
for getting user from View class Try:
user = self.request.user
I created a serializer which the "user" below is from another Serializer which i imported, now the imported serializer(PubliceProfileSerializer) works fine on its own but it does not display the content of USER when i call it my browser from this serializer. Every other item shows except the user. Please help
from rest_framework import serializers
from users.api.serializers import PublicProfileSerializer
from blog.models import Post
class PostSerializer(serializers.ModelSerializer):
user = PublicProfileSerializer(source='users.profile', read_only=True)
category = serializers.SerializerMethodField()
label = serializers.SerializerMethodField()
class Meta:
model = Post
fields = '__all__'
def get_category(self, obj):
return obj.get_category_display()
def get_label(self, obj):
return obj.get_label_display()
Add you your serializer the list of fields. Replace
fields = '__all__'
with
fields = ('id', 'user', 'category', 'label')
because:
fields = '__all__'
will only populate id, category and label from the Post model, but will not provide the nested user serializer, so it becomes:
class Meta:
model = Post
fields = ('id', 'user', 'category', 'label')
or
class PostSerializer(serializers.ModelSerializer):
user = PublicProfileSerializer(many=False,
source='users.profile',
read_only=True)
category = serializers.SerializerMethodField()
label = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('id', 'user', 'category', 'label')
def get_category(self, obj):
return obj.get_category_display()
def get_label(self, obj):
return obj.get_label_display()
I use below solution to check is the user viewed the post or not.
Best way to make "viewed" attribute for messages inside user group?
and in django-rest-framework, i create a ListApiView to get all posts:
class PostListView(ListAPIView):
serializer_class = PostSerializer
permission_classes = (IsAuthenticated, )
pagination_class = PostListPagination
def get_queryset(self):
return Post.objects.filter(state='published').order_by('-created')
and the serializers:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields= '__all__'
now i want a boolean field named "viewed" for each post in PostListView to show that is the authenticated user viewed this post or not.
something like this:
class PostSerializer(serializers.ModelSerializer):
viewed = serializers.BooleanField(read_only=True)
class Meta:
model = Post
fields= '__all__'
def check_is_viewed(current_user, post_instance):
# if user viewed this post:
viewed.value = True
# else:
viewed.value = False
You could use MethodField.
class PostSerializer(serializers.ModelSerializer):
viewed = serializers.SerializerMethodField()
class Meta:
model = Post
fields= '__all__'
def get_viewed(self, obj):
return obj.viewers.exist()
I have question about Django REST-full framework.
When products have rendered into remote client, each of product takes a filed with filtered data.
For example, model may be like this.
class Product(models.Model):
name = models.CharField()
class Like(models.Model):
product = models.ForeignKey(Product, related_name="likes")
On the client, each likes of product counted with true value, not false.
So I tried with below code in the serializer.
class ProductSerializer(serializers.ModelSerializer):
likes = serializers.PrimaryKeyRelatedField(many=True, queryset=Like.objects.filter(whether_like=True))
class Meta:
model = Product
fields = ('id', 'name', 'likes')
But, that is not working as I wanted.
What should I do?
The following is extra view code.
#api_view(['GET'])
def product_list(request, user_id, format=None):
if request.method == 'GET':
products = Product.objects.all()
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
How about something like this:
class ProductSerializer(serializers.ModelSerializer):
likes = serializers.SerializerMethodField()
def get_likes(self, product):
qs = Like.objects.filter(whether_like=True, product=product)
serializer = LikeSerializer(instance=qs, many=True)
return serializer.data
class Meta:
model = Product
fields = ('id', 'name', 'likes')
**LikeSerializer omitted for brevity.
Instead of SerializerMethodField, which causes one additional database query per object, you can now (starting with Django 1.7) use Prefetch objects in the queryset of your DRF ViewSet:
from rest_framework import viewsets
from django.db.models import Prefetch
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.prefetch_related(Prefetch(
'likes',
queryset=Like.objects.filter(like=True)))
The prefetch needs just one query, ensuring vastly superior performance compared to SerializerMethodField.