django rest framework: hypelinking referenced entitites - python

In my project i expose all BlogArticle entities via public API using Django rest framework.
Each BlogArticle ha an author field that refers to a Django user. I want this relations to be modeled using hyperlinks.
to achieve this i added:
in urls.py:
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'blogarticles', BlogArticleViewSet)
in views.py:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class BlogArticleViewSet(viewsets.ModelViewSet):
queryset = BlogArticle.objects.all()
serializer_class = BlogArticleSerializer
in serializers.py:
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
class BlogArticleSerializer(serializers.HyperlinkedModelSerializer):
author = serializers.HyperlinkedIdentityField(view_name='user-detail', format='json')
class Meta:
model = BlogArticle
fields = ('title', 'blog_content', 'author')
everything works correctly but not the hyperlink url.
The result is this:
HTTP 200 OK
Content-Type: application/json
Vary: Accept
Allow: GET, POST, HEAD, OPTIONS
{
"count": 21,
"next": "http://127.0.0.1:8000/api/blogarticles/?page=2",
"previous": null,
"results": [
{
"title": "text",
"blog_content": "content",
"author": "http://127.0.0.1:8000/api/users/1/"
},
{
"title": "boh",
"blog_content": "aa",
"author": "http://127.0.0.1:8000/api/users/2/"
},
[---]
First 2 BlogArticles apparently are written by 2 different users but the user is always the same(as conseguence by calling the second url i get a 404 response).
What i'm expecting is this:
HTTP 200 OK
Content-Type: application/json
Vary: Accept
Allow: GET, POST, HEAD, OPTIONS
{
"count": 21,
"next": "http://127.0.0.1:8000/api/blogarticles/?page=2",
"previous": null,
"results": [
{
"title": "text",
"blog_content": "content",
"author": "http://127.0.0.1:8000/api/users/1/"
},
{
"title": "boh",
"blog_content": "aa",
"author": "http://127.0.0.1:8000/api/users/1/"
},
[---]

When you need to hyperlink against related objects, you should use HyperlinkedRelatedField. When you need to hyperlink against the current object (the url field on the serializer), you should use HyperlinkedIdentityField.
class BlogArticleSerializer(serializers.HyperlinkedModelSerializer):
author = serializers.HyperlinkedRelatedField(view_name='user-detail', format='json')
class Meta:
model = BlogArticle
fields = ('title', 'blog_content', 'author')

Related

Django DRF - nested serializer (level>2) does not show up in the response

We have the follwing structure (library->books->pages)
the first serializer
class Library(serializers.ModelSerializer):
books = BookSerializer(many=True)
class Meta:
model = Library
fields = '__all__'
#transaction.atomic
def create(self, validated_data):
# create logic here
the second serializer
class BookSerializer(serializers.ModelSerializer):
pages = PageSerializer(many=True, required=False)
class Meta:
model = Book
fields = '__all__'
we have an endpoint library/, where we post the payload of the following format
{
"ref": "43a0c953-1380-43dd-a844-bbb97a325586",
"books": [
{
"name": "The Jungle Book",
"author": "Rudyard Kipling",
"pages": [
{
"content": "...",
"pagenumber": 22
}
]
}
]
}
all the objects are created in the database, but the response does not contain pages key. It looks like this
{
"id": 27,
"ref": "43a0c953-1380-43dd-a844-bbb97a325586",
"books": [
{
"id": 34,
"name": "The Jungle Book",
"author": "Rudyard Kipling"
}
]
}
depth attribute does not seem to work. What do I have to do to make pages appear in the responce?
We can achieve the desired behavior using depth in the class Meta of the BookSerializer.
class BookSerializer(serializers.ModelSerializer):
...
class Meta:
...
depth = 3
Copied from documentation
The depth option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.
Another way to get this behavior would be to use serializer.SerializerMethodField for getting the pages of the book serializer.
class BookSerializer(serializers.ModelSerializer):
pages = serializers.SerializerMethodField()
def get_pages(self, obj):
return PageSerializer(obj.page_set.all(), many=True,).data
...

Trouble sorting JSON results with django rest framework and dynamic rest

I am trying to sort my results by either date or primary key, newer entries in the database first.
models.py
class Notifications(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateTimeField('date transacted')
read = models.BooleanField(default=False)
message = models.CharField(max_length=300)
views.py
class getnotifications(viewsets.ModelViewSet):
# Database model
queryset = User.objects.all()
# Serializer - this performs the actions on the queried database entry
serializer_class = NotificationsSerializer
# What field in database model will be used to search
lookup_field = 'username'
serializers.py
class NotificationsSerializer(DynamicModelSerializer):
notifications_set = DynamicRelationField('ListNotificationsSerializer', many=True, embed=True)
class Meta:
model = User
fields = ['notifications_set']
class ListNotificationsSerializer(DynamicModelSerializer):
class Meta:
model=Notifications
name='notifications_set'
fields=['pk','date','read','message']
URL accessing request
http://localhost:8000/pm/getnotifications/<omitted>/?sort[]=-notifications_set.pk
JSON results
{
"notifications_set": [
{
"pk": 1,
"date": "2022-10-10T17:11:33.821757Z",
"read": false,
"message": "A user with the phone <omitted> has submitted a restock request for 5 of airpods"
},
{
"pk": 2,
"date": "2022-10-10T00:00:00Z",
"read": false,
"message": "A user with the phone <omitted> has submitted a restock request for 5 of airpods"
},
{
"pk": 3,
"date": "2022-10-10T17:25:11.824385Z",
"read": false,
"message": "A user with the phone <omitted> has submitted a restock request for 5 of airpods"
}
],
"links": {
"notifications_set": "notifications_set/"
}
}
Whether I use -notification_set.pk or notification_set.pk it displays the results in Postman the same way, with no changed order
Using this plugin: https://github.com/AltSchool/dynamic-rest
for sorting
This plugin was linked in the rest framework docs

Django : This field is required on POST request

I'm trying to create a serializer which outputs the Report and also the User information.
My task is accomplished by this serializer:
class ReportSerializer(serializers.ModelSerializer):
latitude = serializers.CharField()
longitude = serializers.CharField()
city = serializers.IntegerField()
type = serializers.IntegerField()
# We have created a field which returns a value from get_marker_icon_url
marker_icon = serializers.SerializerMethodField('get_marker_icon_url')
status_str = serializers.SerializerMethodField('convert_status_toStr')
type_str = serializers.SerializerMethodField('convert_type_toStr')
status_color = serializers.SerializerMethodField('get_status_color')
likes = serializers.SerializerMethodField('random_likes')
user = ReportUserSerializer()
class Meta:
model = Reports
fields = [
'user',
'id',
'type',
'city',
'latitude',
'longitude',
'likes',
'type_str',
'status_str',
'status_color',
'attached_message',
'marker_icon',
'attached_photo',
'date_created'
]
...
With this code my serializer returns a response like this:
[
{
"user": {
"id": 1,
"username": "3nematix",
"profile_pic": "http://192.168.0.29:8000/frontend/static/frontend/images/reports/user_profile_pic.jpg",
"verified": false
},
"id": 1,
"type": 9,
"city": 0,
"latitude": "6.5123333",
"longitude": "51.512586",
"likes": 27,
"type_str": "OTHER",
"status_str": "PENDING",
"status_color": "orange",
"attached_message": "test",
"marker_icon": "OTHER",
"attached_photo": "http://192.168.0.29:8000/frontend/static/frontend/images/reports/user_profile_pic_VRjIYTs.jpg",
"date_created": "2020-10-21T23:19:06.899302Z"
},
......
]
And this is exactly what I need, but the problem is that when I'm trying to create a new object by a POST request, I get this response:
{
"user": [
"This field is required."
]
}
If I would remove 'user' from Meta and user = ReportUserSerializer() from the ReportSerializer class, then I can create a new object, but when I wish to get the Reports I with the Users information I need to add these two again, how can I fix it?
You need to fill the user field yourself before calling save.
Here's the easy way:
class YourView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Or slightly differently:
class YourView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.validated_data['user'] = self.request.user
return super().perform_create(serializer)

Django REST framework serializer for custom form data

I've started learning how to use the Django REST Framework along with React and I have a quick question. I made a form and use CreateAPIView and UpdateAPIView to create/update items, respectively. But how do I get the contents to populate my <select> field if the list comes from a variable in one of my models?
from model_utils import Choices
class Author(models.Model):
GENDER = Choices('male', 'female', "I don't know really") # How do I get this?
gender = models.CharField(max_length=10, choices=GENDER)
What will the serializer and views for Author.GENDER look like since it's not a model?
At the moment, this is what I have for now.
Django (nothing out of the ordinary here, I think.).
# Serializer.
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ('id', 'gender')
# View
class AuthorUpdateView(UpdateAPIView):
queryset = Author.objects.filter(deleted_at__isnull=True)
serializer_class = AuthorSerializer
React.
componentDidMount() {
const pk = this.props.match.params.pk
axios.get(`http://localhost:8000/api/authors/${pk}`)
.then(response => {
const pk = response.data.id
const gender = response.data.gender
this.setState({gender})
})
.catch(err => console.log(err))
}
I'm open to any direction or concept you might have when using DRF so I can also learn from how you would do it.
Use viewset to combine the logic for a set of your related views (list, create, update)
class AuthorViewSet(ModelViewSet):
queryset = Author.objects.filter(deleted_at__isnull=True)
serializer_class = AuthorSerializer
Make a OPTIONS request to get the metadata for your resource, including the list of valid choices of gender field
Request
OPTIONS /api/authors/
Response
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"name": "Author List",
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"actions": {
"POST": {
"id": {
"type": "integer",
"required": false,
"read_only": true,
"label": "Id"
},
"gender": {
"type": "choice",
"required": true,
"read_only": false,
"label": "Gender",
"choices": [
{
"value": "male",
"display_name": "male"
},
{
"value": "female",
"display_name": "female"
},
{
"value": "I don't know really",
"display_name": "I don't know really"
}
]
}
}
}
}

Get full fields in nested model. Django Rest Framework

I have 2 model classes:
class CustomUser(AbstractUser):
username = models.CharField(max_length=30, unique=True)
tags = models.ManyToManyField('events.Tag', related_name='user_tag', blank=True)
class Tag(models.Model):
name = models.CharField(unique=True, max_length=50)
And serializers:
class UserSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True)
class Meta:
...
class TagSerializer(serializers.ModelSerializer):
class Meta:
lookup_field = 'name'
model = Tag
fields = ('id', 'name')
When I do a get query I get something like this:
"data": {
"type": "CustomUser",
"id": "6",
"attributes": {
"username": "mercer",
},
"relationships": {
"tags": {
"data": [
{
"type": "Tag",
"id": "1"
}
]
},
}
}
What I want is to get Tag 'name' field in user representation:
"type": "Tag",
"id": "1",
"name":"name"
And I want to make patch query for adding tag to user.
I can use SerializerMethodField(), but this way I will not able to add tags
The problem was with settings of rest framework. I wrote there custom json render classes and recived this form of output. Now i removed it and everything is fine.

Categories