Foreign Key with Django REST Serializers - python

I'm having trouble figuring out how to get the Foreign key relationship to work with my Django REST api.
I have a model
from django.db import models
from django.core.validators import RegexValidator
# Create your models here.
class Hero(models.Model):
alphanumeric = RegexValidator(r'^[0-9a-zA-Z]*$', 'Only alphanumeric characters are allowed.')
name = models.CharField(max_length=60, blank=True, validators=[alphanumeric])
alias = models.CharField(max_length=60)
def __str__(self):
return self.name
class SideKick(models.Model):
alphanumeric = RegexValidator(r'^[0-9a-zA-Z]*$', 'Only alphanumeric characters are allowed.')
hero = models.ForeignKey(Hero, related_name='sidekicks', on_delete=models.CASCADE)
sideKick_name = models.CharField(max_length=60, validators=[alphanumeric])
def __str__(self):
return self.sideKick_name
Serializers
from rest_framework import serializers
from .models import Hero, SideKick
class HeroSerializer(serializers.HyperlinkedModelSerializer):
sidekicks = serializers.SlugRelatedField(many=True, read_only=True, slug_field='title')
class Meta:
model = Hero
fields = ('name', 'alias', 'sidekicks')
class SideKickSerializer(serializers.HyperlinkedModelSerializer):
sideKick_name = HeroSerializer()
class Meta:
model = SideKick
fields = ('sideKick_name')
Here is what the api webpage looks like
I'm fairly new to this and was wondering how I can get the option to select a Hero to create a sidekick through the API webpage.
every time i create a hero the sidekick field is blank.
Any help would be apperciated.

Try somthing like this :
class HeroSerializer(serializers.HyperlinkedModelSerializer):
sidekicks = serializers.SerializerMethodField()
class Meta:
model = Hero
fields = ('name', 'alias', 'sidekicks')
def get_sidekicks(self, obj):
sidekicks = []
SideKick_objects = SideKick.objects.all()
for f in SideKick_objects:
features.append({"alphanumeric": f.alphanumeric, "hero": f.hero, "sideKick_name": f.sideKick_name})
return sidekicks
If you get any error for "sidekicks = serializers.SerializerMethodField()"
try using : "serializers.ModelSerializer" instead "serializers.HyperlinkedModelSerializer"

Related

How to create nested serialization from mutliple models uisng django rest framewrok

I am trying to create nested relationship from more than two models in Django Rest Framework.
Thank you in advance for helping me.
I succeed with two models but when I'm trying with three models unable to create nested serialization.
from django.db import models
class Project(models.Model):
project_id = models.AutoField(primary_key=True)
project_name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Site(models.Model):
site_id = models.AutoField(primary_key=True)
site_name = models.CharField(max_length=255)
project_id= models.ForeignKey(Project, related_name="projectid", on_delete=models.CASCADE)
def __str__(self):
return self.site_name
class Aggin(models.Model):
assign_id = models.AutoField(primary_key=True)
site_id = Models.ForeginKey(Site, relate_name="siteid", on_delete=models.CASCADE)
from rest_framework import serializers
from .models import Song, Artist
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ('__all__')
class SiteSerializer(serializers.ModelSerializer):
class Meta:
model = Site
fields = ('__all__')
class AggignSerializer(serializers.ModelSerializer)
class Meta:
model = Aggin
fields = ('__all__')
I think you don't need to primary id field if you wanna use the Django's default foreign key setting. And related_name should be defined from the view of the counterpart model.
from django.db import models
class Project(models.Model):
project_name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Site(models.Model):
site_name = models.CharField(max_length=255)
project = models.ForeignKey(Project, related_name="sites", on_delete=models.CASCADE)
def __str__(self):
return self.site_name
class Aggin(models.Model):
site = Models.ForeginKey(Site, relate_name="assigns", on_delete=models.CASCADE)
And then, in serializer, you can set like the following.
from rest_framework import serializers
from .models import Song, Artist
class ProjectSerializer(serializers.ModelSerializer):
sites = SiteSerializer(read_only = True, many = True)
class Meta:
model = Project
fields = ('id', 'project_name', 'sites')
class SiteSerializer(serializers.ModelSerializer):
assigns = AggignSerializer(read_only = True, many = True)
class Meta:
model = Site
fields = ('id', 'site_name', 'assigns')
class AggignSerializer(serializers.ModelSerializer):
class Meta:
model = Aggin
fields = ('id')

Django - UniqueConstraint not created

I am trying to enforce a constraint for mysql where user is prohibited from inserting twice the same name and model. E.g This should not be allowed to be inserted twice: name:Name1 model:Model1
#Model
class Car(models.Model):
name = models.CharField(max_length=100)
model = models.CharField(max_length=100)
#View
class CarListCreateAPIView(generics.ListCreateAPIView):
serializer_class = CarSerializer
def get_queryset(self):
trip_code = self.kwargs.get("pk")
return Car.objects.filter(trip = trip_code) #Return cars for given trip
#Seializer
class CarSerializer(serializers.ModelSerializer):
class Meta:
model = Car
fields = ('__all__')
constraints = [
models.UniqueConstraint(fields=['name', 'model'], name='car_name_model_constraint')
]
The problem is that the constraint is never created and thus not enforced. What might be the issue with the code?
Use unique_together in class Meta in Model like that:
class Car(models.Model):
name = models.CharField(max_length=100)
model = models.CharField(max_length=100)
class Meta:
unique_together = ['name','model']
More on that here: https://docs.djangoproject.com/en/4.0/ref/models/options/#unique-together

Name returning a number after serializing data

I have a table service :
from django.db import models
from users.models import CustomUser
SERVICE_CHOICES = (
('Carpenter', 'Carpenter'),
('Driver', 'Driver'),
('Ambulanve', 'Ambulanve'),
('Spa', 'Spa'),
('Barber', 'Barber'),
('Cleaning', 'Cleaning'),
('Cook', 'Cook'),
)
class Service(models.Model):
name = models.ForeignKey(CustomUser, on_delete=models.CASCADE, limit_choices_to={'is_worker': True},)
service = models.CharField(choices=SERVICE_CHOICES,max_length=100)
def __str__(self):
return f'{self.service} - {self.name}'
and a table CustomUser :
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
is_worker = models.BooleanField(default=False)
is_customer = models.BooleanField(default=True)
I am serializing the Service table below :
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import *
class ServiceSerializer(serializers.ModelSerializer):
class Meta:
model = Service
fields = '__all__'
But when I add a service from the admin panel, the name shows a number in the browser and not the string. How do I change it to show a the name and not a number?
This is because name is a forignkey to CustomUser, and the default behavior is to return the PK related to the CustomUser instance.
Instead you could use SerializerMethodField.
class UserSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
class Meta:
model = Service
fields = '__all__'
def get_name(self, obj):
return obj.name.first_name
In your ServiceSerializer you can try:
class ServiceSerializer(serializers.ModelSerializer):
class Meta:
model = Service
fields = '__all__'
def to_representation(self, instance):
rep = super(ServiceSerializer, self).to_representation(instance)
rep['name'] = instance.name.name # instance.name.what_you_use_in_Custom_User_model_to_represent_name
return rep
However, consider whether this is what you want, because this change could lead to more problems than it solves.

Dynamic choices in Foreignkey Field in Django Rest Framework

I have just started learning Django Rest Framework and trying to make a simple API using Django rest Framework.
This is my models.py
from __future__ import unicode_literals
from django.db import models
class Student(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=150, blank=False)
student_id = models.CharField(max_length=20, primary_key=True)
father_name = models.CharField(max_length=150)
mother_name = models.CharField(max_length=150)
class Meta:
ordering = ('student_id',)
class Subject(models.Model):
created = models.DateTimeField(auto_now_add=True)
subject_id = models.CharField(max_length=20, primary_key=True)
name = models.CharField(max_length=150)
class Meta:
ordering = ('subject_id',)
class Result(models.Model):
created = models.DateTimeField(auto_now_add=True)
grade = models.DecimalField(max_digits=5, decimal_places=3, blank=False)
student_id = models.ForeignKey(Student, on_delete=models.CASCADE)
subject_id = models.ForeignKey(Subject, on_delete=models.CASCADE)
class Meta:
ordering = ('created',)
And this is my serializers.py
from rest_framework import serializers
from models import *
class StudentSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Student
fields = ('student_id', 'name', 'father_name', 'mother_name')
class SubjectSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Subject
fields = ('subject_id', 'name')
class ResultSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Result
fields = ('grade', 'student_id', 'subject_id')
In my "Result" model, I have two foreign keys; student_id and subject_id. This is how it looks like:
My questions is, how can I show the "name" field in the drop down menu in stead of showing "Student Object" and "Subject Object"?
I have tried with
STUDENT_CHOICES = [(each.student_id, each.name) for each in Student.objects.all()]
SUBJECT_CHOICES = [(each.subject_id, each.name) for each in Subject.objects.all()]
in the model's "choices=" field but it didn't work out.
Thanks in advance.
I think you're looking for this part of the DRF documentation.
Basically, your Django model's own representation is used. So for example, in your Student model you can add __str__ method:
# this is for Python 3, use __unicode__ on Python 2
def __str__(self):
return self.name
Meta options documentation for Django is here, look for model methods.

Key error in work with serializer

models.py:
class Car(models.Model):
# many fields
class CarOptions(models.Model):
car = models.OneToOneField(Car, primary_key=True, related_name='options')
color = models.CharField()
# many other fields
So, I want get all information from Car and its CarOptions.
serializer.py:
class CarOptionsSerializer(serializers.ModelSerializer):
color = serializers.CharField()
class Meta:
model = CarOptions
fields = ('color')
class CarSerializer(serializers.ModelSerializer):
color = CarOptionsSerializer(many=True, read_only=True, )
class Meta:
model = Car
fields = ('many fields', 'color', )
In views.py I have created class based on XMLRenderer, in _to_xml() method (link) I use:
self._to_xml(xml, item["color"])
But it does not work. I have an error:
KeyError: 'color'
I print item-dict and there is no key color in it.
How to fix that?
Thanks!
You should reference the options in the CarSerializer using the options field name rather than the color field name (as you set the related field to be options in the OneToOneField). You do not define a color field directly on the Car.
The following code works for me with the latest django and rest framework:
models.py
class CarModel(models.Model):
name = models.CharField(max_length=250)
class CarOptionsModel(models.Model):
car = models.OneToOneField(CarModel, related_name='options')
color = models.CharField(max_length=250)
serializers.py
class CarOptionsSerializer(serializers.ModelSerializer):
color = serializers.CharField()
class Meta:
model = CarOptionsModel
fields = ('color',)
class CarSerializer(serializers.ModelSerializer):
options = CarOptionsSerializer(read_only=True, )
class Meta:
model = CarModel
fields = ('options', 'name')
views.py
class CarViewSet(viewsets.ModelViewSet):
serializer_class = CarSerializer
queryset = CarModel.objects.all()
urls.py
router = routers.DefaultRouter()
router.register(r'api', CarViewSet)
urlpatterns = router.urls
I would note that since you define a one to one mapping, you cannot have many=True.

Categories