Django Rest Framework serializer nested objects internal server error 500 - python

I have three models like those:
class Ambulance(models.Model):
ambulance_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
ambulance_name = models.CharField(max_length=255)
forms = models.ManyToManyField(Form)
class Form(models.Model):
form_id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
form_title = models.CharField(max_length=255)
class Page(models.Model):
page_id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
page_title = models.CharField(max_length=100, default='Untitled Page')
form = models.ForeignKey(
Form,
related_name='pages',
on_delete=models.CASCADE,
null=True
)
And the serializers as follow:
class AmbulanceSerializer(serializers.ModelSerializer):
forms = FormSerializer(read_only=True, many=True)
class Meta:
model = Ambulance
fields = ['ambulance_id', 'ambulance_name', 'forms']
class FormSerializer(ModelSerializer):
pages = PageSerializer(many=True, read_only=True)
class Meta:
model = Form
fields = ['form_id', 'form_title', 'pages']
class PageSerializer(ModelSerializer):
class Meta:
model = Page
fields = '__all__'
They were working and give me the nested object that I wanted but after adding many-to-many relationships, it gives me an internal server error 500 and it didn't tell me what the problem was, after debugging I found the problem occurs when calling AmbulanceSerializer(ambulance) or FormSerializer(form)
Could someone give me some hint? and what the problem actually!

Your code is wrong:
Can you please check this line?
fields = fields = ['ambulance_id', 'ambulance_name', 'forms']
which is wrong syntax.
You can try with this:
fields = ['ambulance_id', 'ambulance_name', 'forms']

Following your comment, where you state that you are using these serializers for "all types of requests", the answer is simple: remove read_only=True whenever you are using a nested serializer.
The read_only parameter will stop writes i.e. POST, PUT, etc... requests.

Related

How can I include in the JSON's GET request the list of foreign keys of a model in Django?

My models have users that can have multiple devices. When I do a GET request on users it returns only the fields specified in the user model, as it should. But I want the option to include in the JSON returned by the GET request the list of devices the user has. How can I do that? Secondly, is there a way I can sometimes get a user with the list of devices in the same JSON, and other times without it? Also, I am really new to Django, and I would appreciate a lot code examples to understand better, if possible.
These are my models:
class User(models.Model):
name = models.CharField(max_length=100)
birth_date = models.DateField()
address = models.CharField(max_length=200)
class Device(models.Model):
description = models.CharField(max_length=200)
location = models.CharField(max_length=200)
max_energy_consumption = models.FloatField()
avg_energy_consuumption = models.FloatField()
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
My serializers:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = '__all__'
And the following default ModelViewSets for CRUD api calls:
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class DeviceViewSet(ModelViewSet):
queryset = Device.objects.all()
serializer_class = DeviceSerializer
There are some different ways easiest one would be add a property in your User model and add that to your serializer
class User(models.Model):
name = models.CharField(max_length=100)
birth_date = models.DateField()
address = models.CharField(max_length=200)
#property
def devices(self):
return Device.objects.filter(user_id=self.id).values("location", "description").distinct()
class UserSerializer(serializers.ModelSerializer):
devices = serializers.ReadOnlyField()
class Meta:
model = User
fields = '__all__'
EDIT - for second part of your question:
I have experienced that writing '__all__' in our serializers not the best thing to do when we do not need all the information all the time(performance issues). To address this obsticle making a seperate serializer would be again an easy solution. Whenever I am facing this kind of thing i query same endpoint but send a different qs that i dont use in other endpoint in your case lets say your user viewsets route is something like /api/user/ you can add a qs when you send your get request to your backend and add ?with_devices=true.
Then you can use your second user serializer like this:
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
def get_serializer_class(self):
if self.request.GET.get("with_devices", False):
return UserWithDeviceSerializer
return UserSerializer
where your serializers would be something like:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["name", "birth_date", ..so on]
class UserWithDeviceSerializer(serializers.ModelSerializer):
devices = serializers.ReadOnlyField()
class Meta:
model = User
fields = '__all__'
This would give you what you asked in the comment.

Adding a comments feature for a Project in djangorestframework

I have created a project API in djangorestframework but I want to add a comments feature in it also. I was scratching my head thinking how can I do it but couldn't end up with a solution since I'm a beginner in djangorestframework.
here is my models.py:
class Project(models.Model):
title = models.CharField(max_length=300)
body = models.TextField(max_length=5000)
header_image = models.ImageField(default="default.jpg")
demo = models.URLField(null=True, blank=True)
code = models.URLField(null=True, blank=True)
and here is my serializers.py:
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = "__all__"
and here is my views.py:
#api_view(["GET"])
def getProjects(request):
project = models.Project.objects.all()
serializer = ProjectSerializer(project, many=True)
return Response(serializer.data)
Please help me in showing how can I add a comments section to the project API
note: I don't want the comments API to be a different object instead it should be a nested object in the object of the project
As each project can have several comments, you should implement Comment model and make a FK between these two
models.py
...
class Comment(models.Model):
project = models.ForeignKey('Project', related_name='comments', on_delete=models.CASCADE)
text = models.TextField(null=False, blank=False)
... # Some other field if you want. like created_time, author, ...
Then you can have a nested serializer to get comments with project:
serializers.py
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ('text', ...)
class ProjectSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True)
class Meta:
model = Project
fields = (..., 'comments')

How can I create form using this django model?

I am trying to create form using this model. I want to add data in this database model using form to perform CRUD operation. I am using MySQL database.
models.py
from django.db import models
from .managers import CategoryManager, SubCategoryManager
# this is my parent model
class Node(models.Model):
name = models.CharField(max_length=150)
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
related_name='children',
null=True,
blank=True
)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
verbose_name_plural = 'Nodes'
class Category(Node):
object = CategoryManager()
class Meta:
proxy = True
verbose_name_plural = 'Categories'
class SubCategory(Node):
object = SubCategoryManager()
class Meta:
proxy = True
verbose_name_plural = 'SubCategories'
class Product(models.Model):
sub_category = models.ForeignKey(
SubCategory, on_delete=models.CASCADE
)
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
def __str__(self):
return self.name
Try the Imagine smart compiler which allows automatic generating of code + tests for your CRUD APIs and Django models from a very simple config. Amongst other things, it generates code in the correct way to handle foreign key relationships in Django Views. You can also try a demo here imagine.ai/demo
PS: Something like this simple config will generate all the code for the CRUD API along with the tests!
Model Node {
id integer [primary-key]
name string [max-length 150]
}
Model Product {
id integer [primary-key]
name string [max-length 100]
description string [nullable]
}

storing a List using Django REST framework

I have a model that contains the newly added ListField class in DRF.
I am trying to store a List of strings so the output would look like so:
{
"hashtags":["something", "new"],
}
I'm pretty new to working with DRF but I seem to be having some trouble serializing the result. When I run the request I received a
HashField() is not JSON serializable error. Again I'm new to working with the the framework and Python in general but any suggestions, point in the right direction would be a help.
models.py
class HashField(serializers.ListField):
child = serializers.CharField(max_length=100)
class Mention(models.Model):
author = models.ForeignKey(User)
hashtags = HashField()
placename = models.CharField(max_length=140, default='SOME STRING')
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.placename
serializers.py
class MentionSerializer(serializers.ModelSerializer):
class Meta:
model = Mention
Django REST Framework has different kind of serializers, what you need here is a field serializer that validates a list type.
in serializers.py do as follow:
class MentionSerializer(serializers.ModelSerializer):
hashtags = serializers.ListField()
class Meta:
model = Mention

Overriding formset in TabularInline Django admin form

I'm having trouble overriding the formset on a TabularInline inline of a ModelAdmin object in my admin site. I know you're supposed to have a model associated with a TabularInline object, but I'm not sure how to specify this on the form object used to generate the formset. With the code below, I'm getting "'AppAssetInline.formset' does not inherit from BaseModelFormSet."
class AppAssetForm(forms.ModelForm):
model = App.assets.through
primary = forms.BooleanField()
uuid = forms.CharField()
class AppAssetInline(admin.TabularInline):
model = App.assets.through
AssetFormset = formset_factory(AppAssetForm)
formset = AssetFormset
class AppAdmin(admin.ModelAdmin):
inlines = [AppAssetInline,]
The answer to my question didn't have to do with how I was structuring my forms, but rather how I was joining fields on my models. I had the following structure in my models:
class App(models.Model):
package = models.FileField(upload_to=settings.APP_PACKAGE_ROOT)
assets = models.ManyToManyField('AppAsset', blank=True, null=True)
download_count = models.IntegerField(default=0)
class AppAsset(models.Model):
def __unicode__(self):
return self.asset_file.name
notes = models.CharField(max_length=255, null=True, blank=True)
type = models.CharField(max_length=255, null=True, blank=True)
asset_file = models.FileField(upload_to=settings.APP_PACKAGE_ROOT)
What I did was change the structure such that AppAsset now has a foreign key on App for its assets. After that, I could use the TabularInline on the AppAsset model with no problems. Here are the latest source files:
https://github.com/ridecharge/spout/blob/master/Spout/AppDistribution/models.py
https://github.com/ridecharge/spout/blob/master/Spout/AppDistribution/admin.py
You should use django.forms.models.inlineformset_factory instead of formset_factory

Categories

Resources