ForiegnKey field serializer in Django Rest framework - python

Before posting this question I've read few questions on SOF. but they are from 2012 and very confusing as well.
for e.g Django Rest Framework - Get related model field in serializer
my question is very straight forward
models.py
class User(models.Model):
username = models.CharField(max_length=100,unique=True)
password = models.CharField(max_length=100,null=False,blank=False)
class Car(models.Model):
user = models.ForeignKey(User)
car_name = models.CharField(max_length=100,null=True,blank=True)
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username','password' )
class CarSerializer(serializers.ModelSerializer):
#user = ?? what should I write or is there any better approach for serializing Car objects
class Meta:
model = Car
fields = ('user','car_name')
views.py
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class CarViewSet(viewsets.ModelViewSet):
queryset = Car.objects.all()
serializer_class = CarSerializer
Please suggest all possible approaches.
1 more query. which one is better ModelSerializer or HyperlinkModelSerializer. as I saw different different answers containing these two.

You just need to do:
class CarSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Car
fields = ('user','car_name')
that is all.
Also, you should take a look to the kindnesses of serializers.Field and serializers.SerializerMethodField, you can play with them and customize your response data as much as you wish.
As to the question around HyperlinkedModelSerializer-ModelSerializer, very clear here:
The HyperlinkedModelSerializer has the following differences from ModelSerializer:
It does not include the pk field by default.
It includes a url field, using HyperlinkedIdentityField.
Relationships use HyperlinkedRelatedField, instead of PrimaryKeyRelatedField.
Hope that helps.

Related

DRF and Django Filters - filter releated objects

I've got two models:
class User(models.Model):
name = models.CharField(max_length=255)
class Note(models.Model):
timestamp = models.DateTimeField()
user = models.ForeignKey(User)
Both have serializers:
class NoteSerializer(ModelSerializer):
class Meta:
model = User
fields = '__all__'
class UserSerializer(ModelSerializer):
class Meta:
model = Note
fields = '__all__'
notes = NoteSerializer(
many=True,
read_only=True,
)
Now, I need to an endpoint that will return a JSON with the list of users with related notes for each one of them.
The trick here is to be able to filter those notes by a date range. I imagine the URL would look like this:
/api/clients/?range=month&date=2019-03
This translates to: "give me a list of all users with their related notes with timestamp between 2019-03-01 and 2019-04-1.
And the question is: what's the best approach to accomplish that using Django Filters?

Django REST POST with PK to HyperlinkedModelSerializer, how to translate PK to URL?

I'm new to Django and Django REST. I have been creating a simple practice project.
Models:
class Locker(models.Model):
locker_owner = models.ForeignKey(User, related_name='lockers', on_delete=models.CASCADE)
locker_desc = models.CharField(max_length=100)
locker_rating = models.IntegerField(default=0)
Serializers:
class LockerSerializer(serializers.ModelSerializer):
locker_owner = serializers.HyperlinkedRelatedField(
view_name='user-detail',
lookup_field='id',
queryset=User.objects.all())
# how to add locker owner with id?
class Meta:
model = Locker
fields = ('url', 'locker_desc', 'locker_rating', 'locker_owner')
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'id', 'username', 'email', 'groups')
Views:
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
class LockerViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows lockers to be viewed or edited.
"""
# def create(self, request):
queryset = Locker.objects.all()
serializer_class = LockerSerializer
I use this to POST:
ttp -f POST localhost:8000/lockers/ locker_owner_id=2 locker_desc='desc of locker 3' locker_rating=150
But then the response is
"locker_owner": [
"This field is required."
]
Main Point:
I would like to create a new locker, and use User id as its locker_owner, yet still maintain the HyperlinkedModelSerializer. It won't let me, since they need url for locker_owner. Once I use hyperlink in the locker_owner of my POST request it works, but I don't know how to translate locker_owner=2 to it's url.
Any help would be appreciated. I have been looking for answers in days. Thank you!
This is not something you can do out-of-the-box. You will need to implement your own custom serializer Field, and override the to_representation method:
serializers.py
from rest_framework.reverse import reverse
class CustomRelatedField(serializers.PrimaryKeyRelatedField):
def to_representation(self, value):
return reverse('<your-user-detail-view-name>', args=(value.pk,), request=self.context['request'])
class LockerSerializer(serializers.ModelSerializer):
locker_owner = CustomRelatedField(queryset=User.objects.all())
class Meta:
model = Locker
fields = ('url', 'locker_desc', 'locker_rating', 'locker_owner')
You can then simply POST a simple JSON to create a new Locker object:
{
"locker_owner": 2,
"locker_desc": 'desc of locker 3',
"locker_rating": 150
}
I have found the solution myself looking at the tutorials available. The other answers have not really answered the question fully.
For those wanting to get url of a foreign key to be saved, here, locker_owner, just get them using hyperlinked related field
In my serializer:
class LockerSerializer(serializers.HyperlinkedModelSerializer):
locker_owner=serializers.HyperlinkedRelatedField(
read_only=True,
view_name='user-detail')
class Meta:
model = Locker
fields = ('locker_desc', 'locker_rating', 'locker_owner')
I was trying to get the id in the lookup field, but actually the lookup field is searching the id from my url. That's why it could not find it. Simple and done. Thank you for all the answers.

How do I specify database model in a serpy serializer?

I have two models,
class Publication(models.Model):
title = models.CharField(max_length=30)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
I am trying to learn serializing using serpy.
I wrote two serializers, but I am not sure how to mention the model. I wrote a django rest framework serializer, as follows,
class PublicationSerializer(serializers.ModelSerializer):
class Meta:
model = Publication
fields = 'title',
class ArticleSerializer(serializers.ModelSerializer):
publications = PublicationSerializer(many=True)
class Meta:
model = Article
fields = '__all__'
This is the serializers that I wrote for using with Serpy.
class PublicationSerializer(serpy.Serializer):
title = serpy.Field()
class ArticleSerializer(serpy.Serializer):
headline = serpy.Field()
publications = PublicationSerializer()
I dont know where should I mention the model,
I would like to be able to serialize a queryset, say
Article.objects.all()
what changes must be made to use it with Django Rest Framework?
You apparently don't need to specify an associated model for the serpy serializer. Passing your Django objects to the appropriate serpy serializer class should suffice. Or not?
articles = Article.objects.all()
articles_serialized = ArticleSerializer(articles, many=True).data

Django rest framework displaying specific field of a model

In my model:
from django.contrib.auth.models import User
class Restaurant(models.Model):
manager = models.ForeignKey(User, on_delete=models.PROTECT,
null=True, blank=False, related_name="manager")
in my serializers.py
class RestaurantSerializer(CoreHyperlinkedModelSerializer):
class Meta:
model = Restaurant
in my views.py
class RestaurantViewSet(viewsets.ModelViewSet):
queryset = Restaurant.objects.order_by('id').all()
serializer_class = RestaurantSerializer
on my list:
the manager is displaying as <rest_framework.relations.PKOnlyObject object at 0x9f7040xbc208>
How can I display it as normal data like its username?
You want to use a 'SlugRelatedField'.
There are a few ways you can go, but if you just want to show a username, all you need is this
from rest_framework import serializers
class RestaurantSerializer(serializers.ModelSerializer):
manager = serializers.CharField(source="manager.username")
class Meta:
model = Restaurant
if you inherit from ModelSerializer and skip the manager field, it will use user PK as the value of the manager field by default.
a slightly more involved way would be to define a separate serializer for User and then embed it in RestaurantSerializer.
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
class RestaurantSerializer(serializers.ModelSerializer):
manager = UserSerializer()
class Meta:
model = Restaurant
And if you really want to use hyperlinked serializer, you need to do quite a bit of work. You need to read this part carefully http://www.django-rest-framework.org/api-guide/serializers/#how-hyperlinked-views-are-determined

Django Admin: Ordering of ForeignKey and ManyToManyField relations referencing User

I have an application that makes use of Django's UserProfile to extend the built-in Django User model. Looks a bit like:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
# Local Stuff
image_url_s = models.CharField(max_length=128, blank=True)
image_url_m = models.CharField(max_length=128, blank=True)
# Admin
class Admin: pass
I have added a new class to my model:
class Team(models.Model):
name = models.CharField(max_length=128)
manager = models.ForeignKey(User, related_name='manager')
members = models.ManyToManyField(User, blank=True)
And it is registered into the Admin:
class TeamAdmin(admin.ModelAdmin):
list_display = ('name', 'manager')
admin.site.register(Team, TeamAdmin)
Alas, in the admin inteface, when I go to select a manager from the drop-down box, or set team members via the multi-select field, they are ordered by the User numeric ID. For the life of me, I can not figure out how to get these sorted.
I have a similar class with:
class Meta:
ordering = ['name']
That works great! But I don't "own" the User class, and when I try this trick in UserAdmin:
class Meta:
ordering = ['username']
I get:
django.core.management.base.CommandError: One or more models did not validate:
events.userprofile: "ordering" refers to "username", a field that doesn't exist.
user.username doesn't work either. I could specify, like image_url_s if I wanted to . . . how can I tell the admin to sort my lists of users by username? Thanks!
This
class Meta:
ordering = ['username']
should be
ordering = ['user__username']
if it's in your UserProfile admin class. That'll stop the exception, but I don't think it helps you.
Ordering the User model as you describe is quite tricky, but see http://code.djangoproject.com/ticket/6089#comment:8 for a solution.
One way would be to define a custom form to use for your Team model in the admin, and override the manager field to use a queryset with the correct ordering:
from django import forms
class TeamForm(forms.ModelForm):
manager = forms.ModelChoiceField(queryset=User.objects.order_by('username'))
class Meta:
model = Team
class TeamAdmin(admin.ModelAdmin):
list_display = ('name', 'manager')
form = TeamForm
This might be dangerous for some reason, but this can be done in one line in your project's models.py file:
User._meta.ordering=["username"]
For me, the only working solution was to use Proxy Model. As stated in the documentation, you can create own proxy models for even built-in models and customize anything like in regular models:
class OrderedUser(User):
class Meta:
proxy = True
ordering = ["username"]
def __str__(self):
return '%s %s' % (self.first_name, self.last_name)
After that, in your model just change Foreign Key to:
user = models.OneToOneField(OrderedUser, unique=True)
or even more suitable
user = models.OneToOneField(OrderedUser, unique = True, parent_link = True)

Categories