Context
I have 2 models: App & AppVersion.
I am trying to serialize fields from AppVersion along with a field from the App model. I am unable to display the related field in my serializer.
Goal
My goal is to have the API response include the related field like this:
[
{
"app_version_uuid": "61ee8efa-f79e-4fcd-a6ea-4a33544442e1",
"app_version_name": "Test app version",
"version_code": 2,
"version_name": "0.2",
"auto_start": True # related field
}
]
Models
# models.py
class App(models.Model):
app_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
app_name = models.CharField(max_length=100)
package_id = models.CharField(max_length=100, unique=True, null=True, blank=True, editable=False)
auto_start = models.BooleanField(default=False)
class AppVersion(models.Model):
app_version_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
app_uuid = models.ForeignKey(App, on_delete=models.CASCADE, related_name='app_versions')
app_version_name = models.CharField(max_length=100)
version_name = models.CharField(blank=True, null=True, max_length=100, editable=False)
version_code = models.IntegerField(blank=True, null=True, editable=False)
Serializers
# serializers.py
class AppVersionSerializer(serializers.ModelSerializer):
auto_start = serializers.SlugRelatedField(read_only=True, slug_field='auto_start')
class Meta:
model = AppVersion
fields = ('app_version_uuid', 'app_version_name', 'version_code', 'version_name', 'auto_start')
What I tried
I tried to use the SlugRelatedField() as shown in my serializers.py. I also tried to use a SerializerMethodField() like this:
# serializers.py
class AppSerializer(serializers.ModelSerializer):
class Meta:
model = App
fields = ('auto_start')
class AppVersionSerializer(serializers.ModelSerializer):
auto_start = serializers.SerializerMethodField(read_only=True)
def get_auto_start(self, model):
return AppSerializer(model).data
class Meta:
model = AppVersion
fields = ('app_version_uuid', 'app_version_name', 'version_code', 'version_name', 'auto_start')
The SlugRelatedField() does not show the auto_start in the response.
Using the SerializerMethodField() auto_start is shown as:
"auto_start": {}
What do I need to change in order to get the related field auto_start in my response?
Use source argument along with BooleanField,
class AppVersionSerializer(serializers.ModelSerializer):
auto_start = serializers.BooleanField(source='app_uuid.auto_start')
class Meta:
model = AppVersion
fields = ('app_version_uuid', 'app_version_name', 'version_code', 'version_name', 'auto_start')
Related
I am new to this tech, while working on django project i got some issues when i try to serialize Ticket's account.profile_pic
models.py
class Account(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE
profile_pic = models.ImageField(upload_to='images/profile_pics/', blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
class Ticket(models.Model):
author = models.ForeignKey(Account, on_delete=models.CASCADE)
descr = models.TextField(blank=False, null=False)
likes = models.ManyToManyField(User)
serializers.py
class DetailedTicketSerializer(serializers.ModelSerializer):
# Error occurs on below line: No file associated with ImageField
author_profile_pic = serializers.ReadOnlyField(source='author.profile_pic')
author_username = serializers.ReadOnlyField(source='author.user.username')
class Meta:
model = Ticket
fields = ['id', 'author_profile_pic', 'author_username', 'likes', 'descr']
Anyone knows how do i serialize Account.profile_pic's url???
serialize the account class. in your ticketserializer call the account serializer.
Here an example:
class
HobbySerializer(serializers.ModelSerializer):
class Meta:
model = Hobby
fields = '__all__'
class ProfileSerializer(serializers.ModelSerializer):
user_hobby = HobbySerializer(many=True)
class Meta:
model = Profile
fields = '__all__'
In DRF I am facing an issue, whenever I do a POST request on the endpoint, on the field "name" which is a text field I get an exception "Field 'id' expected a number but got 'TITLE'", but when I change the value of "name" to an integer the request is successful I don't understand it becauses name is TextField in model and why its mixing Id and Name field with each other. I have deleted the migration files from the Project and DB and re-run the Migrations, but still facing this issue.
Following is my code:
models.py
class Project(models.Model):
admin = models.ForeignKey(User, on_delete=models.CASCADE, related_name='project_crated_by')
name = models.TextField(max_length=225, blank=False, null=False)
project_members = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='members', null=True, blank=True)
created_on = models.DateField(default=timezone.now)
tags = ArrayField(models.CharField(max_length=225, default=''), blank=True)
def __str__(self):
return self.name
objects = models.Manager()
views.py
class ProjectView(viewsets.ViewSet):
def create(self, request):
project_name_exist = Project.verify_project_name(request.data['admin'], request.data['name'])
if project_name_exist:
return Response({'message': 'You already have a project with this name',
'status': status.HTTP_200_OK})
serialized_project = ProjectSerializer(data=request.data)
if serialized_project.is_valid():
serialized_project.save()
return Response({'message': 'Project Created Successfully', 'status': status.HTTP_201_CREATED})
else:
return Response({'error': serialized_project.errors, 'status': status.HTTP_400_BAD_REQUEST})
serializer.py
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = '__all__'
A more generic and non-DIY solution is to use UniqueTogetherValidator on your serializer and let Django sort it out.
from rest_framework.validators import UniqueTogetherValidator
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = '__all__'
validators = [
UniqueTogetherValidator(
queryset=Project.objects.all(),
fields=['admin', 'name'],
message='You already have a project with this name'
)
]
And/or add it to the model for enforcing it on the database.
class Project(models.Model):
admin = models.ForeignKey(User, on_delete=models.CASCADE, related_name='project_crated_by')
name = models.TextField(max_length=225, blank=False, null=False)
project_members = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='members', null=True, blank=True)
created_on = models.DateField(default=timezone.now)
tags = ArrayField(models.CharField(max_length=225, default=''), blank=True)
def __str__(self):
return self.name
objects = models.Manager()
class Meta:
unique_together = ("admin", "name")
at my models.py I have a "Movies" model with the following field setup:
video_stream_relation = GenericRelation(VideoStreamInfo, related_query_name='video_stream_relation')
This GenericRelation field points to the following model class:
class VideoStreamInfo(models.Model):
objects = RandomManager()
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
content_type = models.ForeignKey(ContentType, limit_choices_to=referential_stream_models, on_delete=models.CASCADE, verbose_name=_("Content Type"))
object_id = models.CharField(max_length=36, verbose_name=_("Object ID"))
content_object = GenericForeignKey('content_type', 'object_id')
index = models.IntegerField(verbose_name=_("Stream Index"), blank=False)
bit_rate = models.IntegerField(verbose_name=_("Bitrate (bps)"), blank=True, null=True, editable=False)
codec_name = models.CharField(verbose_name=_("Codec Name"), blank=True, null=True, editable=False, max_length=255)
width = models.IntegerField(verbose_name=_("Width"), blank=True, null=True, editable=False)
height = models.IntegerField(verbose_name=_("Height"), blank=True, null=True, editable=False)
date_added = models.DateTimeField(auto_now_add=True, verbose_name=_("Date Added"))
Now the Question is how can I get video_stream_relation.codec_name value in a ModelSerializer like this:
class MovieSerializer(serializers.ModelSerializer):
id = serializers.PrimaryKeyRelatedField(queryset=Movies.objects.all())
class Meta:
model = Movies
fields = ('id',
...)
I want to be able to display the codec_name as a API JsonResponse.
If needed, this is how my API view currently looks like:
#api_view(['GET',])
#authentication_classes([JSONWebTokenAuthentication])
#permission_classes([AllowAny])
def movies(request):
if request.method == 'GET':
obj = Movies.objects.all()
serializer = MovieSerializer(obj, many=True)
return JsonResponse(serializer.data, safe=False)
If I try to add the video_stream_relation field to my MovieSerializer I get back the following error:
TypeError: Object of type GenericRelatedObjectManager is not JSON
serializable
Thanks in advance.
You can create a model serializer for VideoStreamInfo and use it in MovieSerializer as a related manager like this:
from rest_framework import serializers
class VideoStreamInfoSerializer(serializers.ModelSerializer):
class Meta:
model = VideoStreamInfo
fields = ('codec_name', )
class MovieSerializer(serializers.ModelSerializer):
video_stream_relation = VideoStreamInfoSerializer(many=True, read_only=True)
id = serializers.PrimaryKeyRelatedField(queryset=Movies.objects.all())
class Meta:
model = Movies
fields = ('id',
'video_stream_relation',
...
)
I have 4 related models and I need to implement the functionality to consistently create instances of these models in a database in one post query. For this I use override of the APIView class post method.
models
class VendorContacts(models.Model):
contact_id = models.AutoField(primary_key=True)
vendor = models.OneToOneField('Vendors', on_delete=models.CASCADE)
contact_name = models.CharField(max_length=45, blank=True)
phone = models.CharField(max_length=45, blank=True)
email = models.CharField(max_length=80, blank=True, unique=True)
class Meta:
db_table = 'vendor_contacts'
class VendorModuleNames(models.Model):
vendor = models.OneToOneField('Vendors', on_delete=models.CASCADE, primary_key=True)
module = models.ForeignKey(Modules, models.DO_NOTHING)
timestamp = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'vendor_module_names'
unique_together = (('vendor', 'module'),)
class Vendors(models.Model):
COUNTRY_CHOICES = tuple(COUNTRIES)
vendorid = models.AutoField(primary_key=True)
vendor_name = models.CharField(max_length=45, unique=True)
country = models.CharField(max_length=45, choices=COUNTRY_CHOICES)
nda = models.DateField(blank=True, null=True)
user_id = models.ForeignKey('c_users.CustomUser', on_delete=models.PROTECT)
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'vendors'
unique_together = (('vendorid', 'timestamp'),)
class Modules(models.Model):
MODULES_NAME =tuple(MODULES)
mid = models.AutoField(primary_key=True)
module_name = models.CharField(max_length=50, choices=MODULES_NAME)
active = models.BooleanField(default=True)
timestamp = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'modules'
unique_together = (('mid', 'timestamp'),)
serializer.py
class VendorsSerializer(serializers.ModelSerializer):
class Meta:
model = Vendors
fields = ('vendor_name',
'country',
'nda',)
class VendorContactSerializer(serializers.ModelSerializer):
class Meta:
model = VendorContacts
fields = (
'contact_name',
'phone',
'email',)
class VendorModulSerializer(serializers.ModelSerializer):
class Meta:
model = VendorModuleNames
fields = ('module',)
class ModulesSerializer(serializers.ModelSerializer):
class Meta:
model = Modules
fields = ('module_name', )
views.py
class VendorsCreateView(APIView):
"""Create new vendor instances from form"""
def post(self, request, *args, **kwargs):
vendor_serializer = VendorsSerializer(data=request.data)
vendor_contact_serializer = VendorContactSerializer(data=request.data)
vendor_modules_serializer = VendorModulSerializer(data=request.data)
module_serializer = ModulesSerializer(data=request.data)
try:
vendor_serializer.is_valid(raise_exception=True) \
and vendor_contact_serializer.is_valid(raise_exception=True) \
and vendor_modules_serializer.is_valid(raise_exception=True) \
and module_serializer.is_valid(raise_exception=True)
vendor_serializer.save(user_id=request.user)
# ....
# Some new logic here ????
# ...
except ValidationError:
return Response({"errors": (vendor_serializer.errors,
vendor_contact_serializer.errors,
vendor_modules_serializer.errors
)},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(request.data, status=status.HTTP_200_OK)
There is no problem saving one Vendor model, but I can't imagine how to save cascading all related models in a single request.
save returns the newly saved object, which you can then pass into the subsequent save() methods:
vendor = vendor_serializer.save(user_id=request.user)
module = module_serializer.save()
vendor_module = vendor_modules_serializer.save(module=module, vendor=vendor)
vendor_contact = vendor_contact_serializer.save(vendor=vendor)
I have two models:
class Album(models.Model):
code = models.CharField(max_length=10, primary_key=True, default=_create_access_code, verbose_name=_("Id"))
name = models.CharField(max_length=200, verbose_name=_("Name"))
description = models.TextField(null=True, blank=True, verbose_name=_("Description"))
company = models.ForeignKey(Company, on_delete=models.PROTECT, related_name='albums', verbose_name=_("Company"))
access_code = models.CharField(max_length=10, default=_create_access_code, verbose_name=_("Internal Use"))
class Meta:
verbose_name = _("Album")
verbose_name_plural = _("Albums")
def __str__(self):
return "[{}] {} ({})".format(self.pk, self.name, self.company.id)
class Photo(models.Model):
name = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Name"))
album = models.ForeignKey(Album, on_delete=models.CASCADE, related_name='photos', verbose_name=_("Album"))
photo = models.ImageField(verbose_name=_("Photo"))
class Meta:
verbose_name = _("Photo")
verbose_name_plural =_("Photos")
def __str__(self):
return "[{}] {}".format(self.pk, self.name)
I am trying to make a post to the ModelViewSet for model Albums, but I get an error indicating that field photos is required. Even the OPTIONS method indicates it es required.
How can I instruct DRF for not considering look up fields as required? Is it some serializer setting?
You can add required=False to fields in the serializer.
photos = PhotoSerializer(many=True, required=False)
Something like this. Can you post you serializers?