I can't have object by its id in Django Rest Framework. I have a such model:
class BlogPost(models.Model):
title = models.CharField(max_length=128)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
Then I write a serializer:
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
fields = ('title', 'content', 'created')
In my views.py I have this:
class BlogPostListFilter(dajngo_filter.FilterSet):
blog_post_id = django_filters.NumerFilter(name = 'id')
class Meta:
model = BlogPost
fiields = ['blog_post_id']
class BlogPostList(generics.ListCreateAPIView):
queryset = BlogPost.objects.all()
serializer_class = BlogPostSerializer
permission_classes = (AllowAny,)
filter_class = BlogPostListFilter
paginate_by = 100
And such code in my urls:
url(r'^blogpost/$', ListCreateAPIView.as_view(model=BlogPost), name='blogpost-list'),
But when I write in browser http://example.com/blogpost/?blog_post_id=1 I have all objects
If you really want to use ListCreateAPIView. You need to make some changes:
urls.py:
url(r'^blogpost/(?P<post_id>\w+)$', views.BlogPostList.as_view(),name='blogpost-list'),
views.py
class BlogPostList(generics.ListCreateAPIView):
serializer_class = BlogPostSerializer
permission_classes = (AllowAny,)
filter_class = BlogPostListFilter
paginate_by = 100
def get_queryset(self):
queryset = BlogPost.objects.filter(pk=self.kwargs['post_id'])
return queryset
But I think that Django Rest Framework provides better Class Based Views for your use case, such as RetrieveAPIView. As far as I understand, it seems that you just want to get an object, and this generic view is for a list of objects.
In my case, I stumbled upon this question looking to access the object id while overriding a ModelViewSet's retrieve method. After some research and experimentation, I discovered that the object id is stored in a dictionary called self.kwargs in the 'pk' key.
I am using djangorestframework==3.11.0.
class MealItemViewSet(viewsets.ModelViewSet):
queryset =MyModel.objects.all()
serializer_class = serializers.MyModelSerializer
def retrieve(self, request, *args, **kwargs):
# The Primary Key of the object is passed to the retrieve method through self.kwargs
object_id = self.kwargs['pk']
I hope this answer helps another forlorn StackOverflow wanderer at some point!
Related
I want to serialize ManyToManyField but at the same time, I am looking for something which updates the same using ModelViewSet. I am able to serialize it but when I am updating it I am not able to. I know I can make a separate API for that but due to some requirements, I need to stick to one endpoint. Here is my code
class ComponentSerializers(serializers.ModelSerializer):
class Meta:
model = coreModel.Component
fields = '__all__'
class MonitorSerializers(serializers.ModelSerializer):
device = ComponentSerializers(read_only=True, many=True)
class Meta:
model = models.Monitor
fields = '__all__'
read_only_fields = ('id', 'created_at', 'updated_at',)
and views.py is
class MonitorViewSet(viewsets.ModelViewSet):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
queryset = models.Monitor.objects.all()
filter_backends = (DjangoFilterBackend,OrderingFilter,SearchFilter)
filter_class = superFilter.MonitorFilters
serializer_class = serializers.MonitorSerializers
If you want update ManytoMany or Related objects
Override
def update(self, validated_data):
in MonitorSerializers
class MonitorSerializers(serializers.ModelSerializer):
device = ComponentSerializers(read_only=True, many=True)
device_ids = serializers.ListField(write_only=True,
child = serializers.IntegerField(min_value = 1))
....
def update(self, instance, validated_data):
# Remove component data and save in variable
iscomponentdataexists = 'device_ids' in validated_data
if iscomponentdataexists :
componentdata= validated_data.pop('device_ids')
instance = super().update(instance, validated_data) # Update Monitor Data
# looping through new device_ids list
if iscomponentdataexists :
for deviceid in componentdata:
try:
obj = coreModel.Component.objects.get(id=deviceid)
instance.devices.add(obj)
except coreModel.Component.DoesNotExist:
pass
instance.save()
return instance
Remove read_only=True from device
I want to pass user_id from view to serializer
I have model Answer
class Answer(models.Model) :
text = models.CharField(max_length=500)
question_id = models.CharField(max_length=25)
user_id = models.CharField(max_length=25, default=1)
This is my Serializer
class CreateAnswer(generics.CreateAPIView) :
def get_serializer_context(self):
context = super().get_serializer_context()
context["id"] = self.request.user.id
return context
serializer_class = AnswerQuestionSerializer
queryset = Answer.objects.all()
What I need to write in my view to take user_id and create model with this user_id ?
You can override the perform_create method & pass the user_id field to save method of the serializer.
class CreateAnswerView(generics.CreateAPIView) :
serializer_class = AnswerQuestionSerializer
def perform_create(self, serializer):
serializer.save(user_id=self.request.user.id)
You can use serializers.Hiddenfield to get current user in serializer class
https://www.django-rest-framework.org/api-guide/fields/#hiddenfield
There are multiple ways to do this task. One of them is to override create in your serializer.
Following is the code snippet:
class BlogSerializer(serializers.Serializer):
def create(self, validated_data):
user = self.context['request'].user
blog = Blog.objects.create(
user=user,
**validated_data
)
return blog
Explanation: A context is passed to the serializer which contains the request by default. So you can access the user easily with self.context['request'].user
Trying to filter objects in my view.py to only show items (in my case Buckets) owned by Users.
I implemented the code below in my original Model [my model.py code is at the bottom of post]
class PostObjects(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status=Bucket.owner)
But I'm not sure if that is the correct procedure to list all items?
Here is the view.py where I'm trying to filter data by User aka owner. Users should ONLY be allowed to view their own items. (I will deal with permissions later)
class BucketList(generics.ListCreateAPIView):
queryset = Bucket.objects.all() #INSERT FILTER HERE
pass
Here is the model I'm referring too.
class Bucket(models.Model):
options = (
('personal', 'Personal'),
('social', 'Social'),
)
class PostObjects(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status=Bucket.owner)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='buckets')
users = models.ManyToManyField(settings.AUTH_USER_MODEL)
category = models.CharField(max_length=30, choices=options)
name = models.CharField(max_length=35)
created = models.DateTimeField(default=timezone.now)
slug = models.SlugField(unique=True, blank=True)
stock_count = models.IntegerField(blank=True, null=True)
stock_list = ArrayField(models.CharField(max_length=6),size=10)
objects = models.Manager()
postobjects = PostObjects()
class Meta:
ordering = ('-created',)
def total_stocks_calc(self):
self.stock_count = Bucket.objects.aggregate(Sum('stock_list', distinct=True))
self.save()
def get_absolute_url(self):
return reverse("bucket:bucket-view", kwargs={"slug": self.slug})
def __str__(self):
return self.stock_list
To re-state my question, how can I filter objects owned by users in class BucketList for their private view only?
UPDATE:
from django.db.models import Q
from rest_framework import generics
from bucket.models import Bucket
from .serializers import BucketSerializer
class OwnerOrUserFilterBackend(filters.BaseFilterBackend):
queryset = Bucket.postobjects.all() # wondering if you didnt write this for brevity reasons or because its not need due to the class?
def filter_queryset(self, request, queryset, view):
return queryset.filter(
Q(owner=request.user) | #do I not need to change one of the filters too request.owner?
Q(users=request.user)
)
class BucketList(generics.ListCreateAPIView):
model = Bucket
filter_backends = [OwnerOrUserFilterBackend]
You can override the get_queryset method and filter with self.request.user:
class BucketList(generics.ListCreateAPIView):
model = Bucket
def get_queryset(self, *args, **kwargs):
return super.get_queryset(*args, **kwargs).filter(
owner=self.request.user
)
For an API view, it probably is however better to define a filter, and then use this over all the API views where you want to apply this:
class IsOwnerFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(owner=request.user)
Then you can use this in your ListCreateAPIView with:
class BucketList(generics.ListCreateAPIView):
model = Bucket
filter_backends = [IsOwnerFilterBackend]
The advantage of this is that if you have other views that require the same filtering, you only need to add the IsOwnerFilterBackend as filter_backend.
Another filter could include both the owner and the users as people who can see the BucketList:
from django.db.models import Q
class OwnerOrUserFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(
Q(owner=request.user) |
Q(users=request.user)
)
then we thus filter with this filter:
class BucketList(generics.ListCreateAPIView):
model = Bucket
filter_backends = [OwnerOrUserFilterBackend]
For example i have a few models:
class Parent(Model):
api_key = CharField(max_length=250)
class Child(Model):
parent = ForeignKey(Parent, related_name='children')
status = CharField(max_length=250)
I wrote view based on ListAPIView, and serializer:
class ChildSerializer(ModelSerializer):
class Meta:
fields = ['id']
I need to take all Children with parent by find Parent by api_key and return this as:
{
children:[
{'id':1},
{'id':2}
]}
But i take this:
[
{'id':1},
{'id':2}
]
Well, you can define a ParentSerializer which will also return the child objects data with it:
class ParentSerializer(ModelSerializer):
child = ChildSerializer(many=True)
class Meta:
fields = ['api_key', 'child']
Then you need to find the parent by api_key and pass the Parent object to this serializer. Like this:
class ParentView(RetrieveAPIView):
queryset = Parent.objects.all()
lookup_field = 'api_key'
serializer_class = ParentSerializer
lookup_url_kwarg = 'api_key'
Finally set the url to:
path('/parent/<str:api_key>/', ParentView.as_view(), name='parent-detail')
Update
If you need to get the data from request.GET(url querystring), then it much simpler. Try like this:
class ParentView(ListAPIView):
queryset = Parent.objects.all()
serializer_class = ParentSerializer
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
api_key = request.GET.get('api_key')
if api_key:
return queryset.filter(api_key=api_key)
return queryset
I have two serializers, one of which is nested:
class PaperSerializer(serializers.ModelSerializer):
class Meta:
model = Paper
class AuthorSerializer(serializers.ModelSerializer):
papers = PaperSerializer(
many=True,
read_only=True,
source='paper_set'
)
class Meta:
model = Author
I want to get a list of Authors which shows only their published Papers (Boolean field exists on the model).
I would like to call the API like /api/v1/authors/?show_published_only=true.
After some digging around, I discovered that you can pass the context from the ViewSet to the Serializer:
views.py
class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
filter_fields = (
'show_published_only',
)
def get_serializer_context(self):
return {'request': self.request}
Now, create a new serializer FilteredPaperSerializer which inherits from serializers.ListSerializer, then override the to_representation() method to filter the queryset:
serializers.py
class FilteredPaperSerializer(serializers.ListSerializer):
def to_representation(self, data):
# Get the parameter from the URL
show_published_only = self.context['request'].query_params['show_published_only']
data = data.filter(is_published=show_published_only)
return super(FilteredPaperSerializer, self).to_representation(data)
class AuthorSerializer(serializers.ModelSerializer):
papers = FilteredPaperSerializer(
many=True,
read_only=True,
source='paper_set'
)
class Meta:
model = Author
NB: Don't forget to convert the fetched URL parameter to a Boolean or relevant data type for your model, I neglected to do it in the write-up above.