How to mapping field from json to model field in drf serialiazer? - django-rest-framework

I have a model:
class Book(models.Model):
genre_type_id = models.ForeignKey(GenreType)
author = models.ForeignKey(Author)
title = models.CharField(max_length=255)
year = models.DateField()
and serializer:
class BookSerializer(ModelSerializer):
model = Book
fields = ('id', 'genre_type_id', 'author', 'year')
read_only_fields = ('id', )
Server receives POST request with data:
{
"genre": 1,
"author": 3,
"title": "My title",
"year": "2016.01.01"
}
How to map genre field from json to genre_type_id model field in drf serializer?

You'll need to explicitly define the field as genre and set the source argument to genre_type_id so it maps genre_type_id to / from genre.

Related

IntegrityError at /api/order/create NOT NULL constraint failed: store_order.product_id

I'm trying to create Order via Api request with related keys, product and user id's. But getting an error that they cannot be null, although they're not null.
model.py
class Order(models.Model):
id = models.AutoField(primary_key=True)
product = models.ForeignKey(Product, related_name='product', on_delete=models.CASCADE, default = None)
user = models.ForeignKey(User, related_name='user', on_delete=models.CASCADE, default = None)
orderPrice = models.IntegerField(default=0)
status = models.CharField(max_length=255, default='Принят на обработку')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
serializer.py
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ['id', 'product', 'user', 'status', 'orderPrice']
depth = 1
view.py
#api_view(['POST'])
def OrderCreateView(request):
order_serializer = OrderSerializer(data=request.data)
if order_serializer.is_valid(raise_exception=True):
order_serializer.save()
return JsonResponse(order_serializer.data, safe=False)
and finally my api request
{
"product": 1,
"user": 3,
"status": "Принят на обработку",
"orderPrice": 30000
}
This code worked sometime but i did couple changes and now it getting an error. I tried to remigrate all my models, and requested like that "user_id", "product_id". But keeps getting the error
Hi
the problem of your code is your serializer because you mention a depth of 1 and you send a JSON in your POST functiona JSON in coherent.
Your json should contain all the attributes of your foreign keys like this:
{
"product": {
...attributes of product
},
"user": {
...attributes of user
},
"status": "Принят на обработку",
"orderPrice": 30000
}
You just have to declare the product field as nullable, because null is False by delfault:
class Order(models.Model):
[...]
product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True, blank=True)
[...]
I also removed related_name, as to retrieve the order of a product, you had to call product.product. Now you retrieve it using product.order.

DjangoREST: Extracting value from POST request to use in overridden create method

I am trying to accomplish a simple task of extracting the value passed from the POST request and using it as a parameter inside the overridden create method.
This is the example POST request body JSON, and "documented" is the field I wish to extract.
### POST
{
"url": "aaaa",
"title": "bbbb",
"publisher": "bbbb",
"desc": "bbbb",
"summary": "bbbb",
"documentId": "onDKe6K"
}
Using validated_data.pop("documentId") was the most obvious choice. However, I need DocumentListingField set to read_only=False in order to use this method. And that option raised larger issues as my Document model has an Hashfield that is not easily serializable.
Is there any other way to accomplish what I want in this situation? I've tried all of these but they all failed with "KeyError: documented"
validated_data.get("documentId")
validated_data["documentId"]
serializers.py
from django.forms.models import model_to_dict
class DocumentListingField(serializers.RelatedField):
def to_representation(self, instance):
return model_to_dict(instance.document)
class MySourceSerializer(serializers.ModelSerializer):
Document = DocumentListingField(many=False, read_only=True)
class Meta:
model = MySource
fields = (
"id",
"url",
"title",
"publisher",
"desc",
"summary",
"Document",
)
def create(self, validated_data):
documentId = validated_data.get("documentId"). <========= LINE OF INTEREST
print(documentId)
source = MySource.objects.create(
document=Document.objects.get(id=documentId), **validated_data
)
print(validated_data)
source.save()
return source
I think you can set the documentId field in the serializer.
class MySourceSerializer(serializers.ModelSerializer):
Document = DocumentListingField(many=False, read_only=True)
documentId = serializers.CharField(write_only = True)
lass Meta:
model = MySource
fields = (
...
"documentId",
)

Problems with POST on DRF

Django Rest Framework, PostgreSQL.
Models:
class Manufacturer(models.Model):
name = models.CharField(max_length=128)
class Product(models.Model):
name = models.CharField(max_length=64)
manufacturer = models.ForeignKey(
Manufacturer, on_delete=models.CASCADE, related_name="products"
)
Serializers:
class ManufacturerSerializer(serializers.ModelSerializer):
class Meta:
model = Manufacturer
fields = "__all__"
class ProductSerializer(serializers.ModelSerializer):
manufacturer = ManufacturerSerializer(read_only=True)
class Meta:
model = Product
fields = "__all__"
Then I send a GET-request, I get the following response:
{
"id": 1,
"manufacturer": {
"id": 1,
"name": "Manufacturer 1"
},
"name": "Product 1"
}
But then I send a POST-request,
{
"name": "Product 2",
"manufacturer_id": 1
}
I get the error:
null value in column "manufacturer_id" of relation "api_product" violates not-null constraint
DETAIL: Failing row contains (2, Product 2, null).
How to properly compose post-request?
For your POST send this:
{
"name": "Product 2",
"manufacturer": 1
}
You might also need to delete the whole row manufacturer = ManufacturerSerializer(read_only=True).

How can I add a field to a model serializer that has a reverse relationship with another model

I have two models. Fiction and Review model. They are the following:
class Fiction(models.Model):
"""
Model that encopasses a Movie, TV Series, book or similar
"""
MOVIE = 1
TV_SERIES = 2
BOOK = 3
PODCAST = 4
TYPE = (
(MOVIE, 'Movie'),
(TV_SERIES, 'TV-Series'),
(BOOK, 'Book'),
(PODCAST, 'Podcast')
)
title = models.CharField(max_length=50)
description = models.CharField(max_length=200)
active = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
platform = models.ForeignKey(
StreamPlatform,
on_delete=models.SET_NULL,
related_name='fictions',
null = True
)
type = models.PositiveSmallIntegerField(
choices = TYPE,
default = MOVIE
)
def __str__(self):
return self.title
and
class Review(models.Model):
"""
model for fiction reviews from users
"""
rating = models.PositiveSmallIntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
fiction = models.ForeignKey(Fiction, on_delete=models.CASCADE, related_name="reviews")
description = models.CharField(max_length=200, null = True, blank =True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.rating) + " | " + str(self.fiction)
class Meta:
ordering = ['-created']
and also two serializers
for fiction
class FictionSerializer(serializers.ModelSerializer):
"""
serializer for Movie model
"""
class Meta:
model = Fiction
fields = "__all__"
and for review
class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ['rating', 'fiction', 'description']
I want to be able to display the rating of the review inside the fiction serializers. I tried something like:
rating = serializers.ReadOnlyField(source='reviews.rating')
but it didnt work. Anyone has an idea?
Since you added reviews as a related name, you can use that.
Here is a working example for you. (I've created a small project for this, so this definitely works)
class ReviewRatingSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ('rating', )
class FictionSerializer(serializers.ModelSerializer):
"""
serializer for Movie model
"""
reviews = ReviewRatingSerializer(many=True)
class Meta:
model = Fiction
fields = "__all__"
This might cause lots of database queries if you want to return lots of Fiction items at once.
To fix that, you should use prefetch_related in your views.py
Here is a simple example for a list view.
class GetFictionMovies(ListAPIView):
pagination_class = None
serializer_class = FictionSerializer
def get_queryset(self):
queryset = Fiction.objects.all().prefetch_related('reviews')
return queryset
Output will be similar to this.
[
{
"id": 1,
"reviews": [
{
"rating": 3
},
{
"rating": 4
}
],
"title": "Starwars",
"description": "asdasd",
"active": true,
"created": "2021-06-27T16:28:55.521748Z",
"type": 1
},
{
"id": 2,
"reviews": [
{
"rating": 5
},
{
"rating": 2
}
],
"title": "LOTR",
"description": "asdasd",
"active": true,
"created": "2021-06-27T16:29:03.227639Z",
"type": 1
},
{
"id": 3,
"reviews": [
{
"rating": 4
},
{
"rating": 3
}
],
"title": "GODFATHER",
"description": "asdasd",
"active": true,
"created": "2021-06-27T16:34:45.171444Z",
"type": 1
}
]
My advice for you is to always check for number of queries made to the db and try to avoid duplicate calls to the db.

How do you access nested object information with Django Rest Framework?

I've got two models connected through a ManyToManyField that links projects together with users, as such:
class Project(Model):
STATUS_CHOICES = (
('active', 'Active'),
('archived','Archived'),
)
name = CharField(max_length=50)
members = ManyToManyField("accounts.User", through='ProjectUser')
organization = ForeignKey(Organization, related_name="organizations", on_delete=CASCADE, verbose_name="Team")
status = CharField(max_length=10, choices=STATUS_CHOICES, default='active')
def __str__(self):
return self.name
class Meta:
db_table = 'project'
ordering = ('organization', 'name')
unique_together = ('name', 'organization',)
class ProjectUser(Model):
ROLE_CHOICES = (
('member', 'Member'),
('admin','Admin'),
)
user = ForeignKey("accounts.User", on_delete=CASCADE)
project = ForeignKey(Project, on_delete=CASCADE)
user_hour_cost = DecimalField(max_digits=6, decimal_places=2, default=0)
role = CharField(max_length=10, choices=ROLE_CHOICES, default='member')
class Meta:
db_table = 'projectuser'
ordering = ('user',)
unique_together = ('project', 'user',)
and a ProjectSerializer that looks like this:
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ["name", "organization", "members"]
I wish to extract data about the users when using the ProjectSerializer (e.g get the username, email, first name, last name from the User model). All I get back with this serializer is
{
"name": "Project X/Y",
"organization": 1,
"members": [
2,
1
]
}
Is there a way for me to traverse the information on the members so my template can use it? E.g members[0].username?
I can't just use depth = 1 because that returns data directly from User model, but ignores the fields on the ProjectUser model
I'm looking for something along the lines of
{
"name": "Project X/Y AB",
"organization": 1,
"projectusers": [
{
"user": ["id": 1, "username": "foo", "first_name": "joey"],
"project": 1,
"user_hour_cost": "550.00",
"role": "admin"
},
{
"user": ["id": 2, "username": "hellboy", "first_name": "erik"],
"project": 1,
"user_hour_cost": "190.00",
"role": "member"
}
]
}
Doesn't necessarily have to look just like this - but I need for my frontend to receive information about the user that sits on the User table in my db
Maybe you could try to specify your own serializer for the (project)users. This is covered more in depth in the official DRF docs.
class ProjectSerializer(serializers.ModelSerializer):
members = MemberSerializer(many=True)
class Meta:
model = Project
fields = ["name", "organization", "members"]
and define your Member and User Serializer:
class MemberSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = ProjectMember
fields = ["user ", "...", "role "]
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["id", "username", "first_name", "..."]
But beware that making such a construct writeable is tricky. You would probably have to overwrite your Serializers create() methods to implement this. See here for more details.
I actually solved it by just nesting another serialized object inside ProjectUser
User Serializer
from accounts.models import User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("username", "first_name", "email")
and then
from .models import Project
from .models import ProjectUser
from accounts.serializers import UserSerializer
from rest_framework import serializers
class ProjectUserSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = ProjectUser
fields = ("user", "user_hour_cost", "role")
class ProjectSerializer(serializers.ModelSerializer):
projectusers = ProjectUserSerializer(many=True, read_only=True)
class Meta:
model = Project
fields = ["name", "organization", "projectusers"]
Which returned
{
"name": "Project XXAA",
"organization": 1,
"projectusers": [
{
"user": {
"username": "Google",
"first_name": "Chrome",
"email": "google#chrome.com"
},
"user_hour_cost": "550.00",
"role": "admin"
},
{
"user": {
"username": "Mozilla",
"first_name": "Joey",
"email": "mozilla#firefox.com"
},
"user_hour_cost": "190.00",
"role": "member"
}
]
}
Good enough to work with!

Resources