I am trying to Create a Serializer Class that returns highlighted text using django_elasticsearch_dsl_drf DJANGO Rest framework - django-rest-framework

I am using this serializer but it does not returb the highlighted fields from elastic search
class ResourceFileSerializer(serializers.ModelSerializer):
"""Serializer for resource files."""
class Meta:
model = ResourceFile
fields = ["id", "title", "body", "summary"]
read_only_fields = ["id"]
class ResourceFileDocumentSerializer(ResourceFileSerializer):
"""Handle resource file document."""
class Meta(ResourceFileSerializer.Meta):
document = ResourceFileDocument
fields = [
"title",
"body",
"summary",
]

Related

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",
)

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!

Nested Image Field in custom Page representation wagtail api

We are building a headless CMS with the wagtail API.
Our main model became very long, to make the representation cleaner and more easily accessible for the Frontend,
I am trying to group the different fields of my PageModel into sections.
But I don't manage to serialize the nested ImageField.
This is my PageModel:
class LandingPage(Page):
…
introduction_headline= models.CharField()
introduction_text = RichTextField()
introduction_icon = models.ForeignKey(
'main.CaptionImage',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name = '+',
)
…
I would like to group those fields into one section in the api, like so:
{
"id": 3,
"meta": {…},
"introduction_section": {
"introduction_headline": "intro head",
"introduction_text": "<p>intro text</p>",
"introduction_image": {
"id": 1,
"meta": {
"type": "main.CaptionImage",
"detail_url": "http://localhost/api/v2/images/1/",
"download_url": "/media/original_images/1.png"
},
"title": "german_design_image.png",
"caption": "Pretty Image"
},
},…
I managed to accomplish this in parts by writing a custom IntroductionSection - serializer:
class LandingPage(Page):
…
api_fields = [
APIField('introduction_section', serializer=IntroductionSectionField(source='*')),
…
]
class IntroductionSectionField(Field):
read_only = True
write_only = False
def to_representation(self, value):
return {
"introduction_headline" : value.introduction_headline,
"introduction_text" : value.introduction_text,
"introduction_image" : ?
}
But I simply can't figure out how to serialize the nested Image Field?
I want the same representation as the standard nested-relation-representation of the page model.
I tried around with get_related_field() method of the PageModel, tried to call the ImageSerializer, and all sorts of other things.

In the Django Rest Framework, how do you add ManyToMany related objects?

Here's my code:
Models
class Recipe(models.Model):
name = models.CharField(max_length=50, unique=True)
ingredient = models.ManyToManyField(Ingredient)
class Ingredient(models.Model):
name = models.CharField(max_length=50, unique=True)
View
class RecipeDetailAPIView(RetrieveUpdateDestroyAPIView):
permission_classes = (IsAdminOrReadOnly,)
serializer_class = RecipeSerializer
queryset = Recipe.objects.all()
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def perform_update(self, serializer):
serializer.save(updated_by_user=self.request.user)
Serializers
class IngredientSerializer(serializers.ModelSerializer):
class Meta:
model = Ingredient
fields = [
'id',
'name',
]
class RecipeSerializer(serializers.ModelSerializer):
ingredient = IngredientSerializer(many=True, read_only=False)
class Meta:
model = Recipe
fields = [
'id',
'name',
'ingredient',
]
I'm starting with the following Recipe object:
{
"id": 91
"name": "Potato Salad"
"ingredient": [
{
"id": 5,
"name": "Potato"
}
]
}
Now, I am attempting to update that object by putting the following JSON object to the IngredientSerializer:
{
"id": 91
"name": "Potato Salad"
"ingredient": [
{
"id": 5,
"name": "Potato"
},
{
"id": 6,
"name": "Mayo"
}
]
}
What I want is it to recognize that the relationship to Potato already exists and skip over that, but add a relationship to the Mayo object. Note that the Mayo object already exists in Ingredients, but is not yet tied to the Potato Salad object.
What actually happens is the Serializer tries to create a new Ingredient object and fails because "ingredient with this name already exists."
How do I accomplish this?
DRF does not have any automatic "write" behavior for nested serializers, precisely because it does not know things like how to go about updates in the scenario you mentioned. Therefore, you need to write your own update method in your RecipeSerializer.
class IngredientSerializer(serializers.ModelSerializer):
def validate_name(self, value):
# manually validate
pass
class Meta:
model = Ingredient
fields = ['id', 'name']
extra_kwargs = {
'name': {'validators': []}, # remove uniqueness validation
}
class RecipeSerializer(serializers.ModelSerializer):
ingredient = IngredientSerializer(many=True, read_only=False)
def update(self, instance, validated_data):
ingredients = validated_data.pop('ingredient')
# ... logic to save ingredients for this recipe instance
return instance
class Meta:
model = Recipe
fields = ['id', 'name', 'ingredient']
Relevant DRF documentation:
Saving Instances
Writable Nested Serializer
Updating nested serializers
Update:
If DRF validation fails for the uniqueness constraint in the name field, you should try removing validators for that field.
Alternative solution: Only use full serializer as read only field
You can change you RecipeSerializer to the following:
class RecipeSerializer(serializers.ModelSerializer):
ingredient_details = IngredientSerializer(many=True, read_only=True, source='ingredient')
class Meta:
model = Recipe
fields = ['id', 'name', 'ingredient', 'ingredient_details']
And that's it. No need to override update or anything. You'll get the detailed representation when you get a recipe, and you can just PUT with the ingredient ids when updating. So your json when updating will look something like this:
{
"id": 91
"name": "Potato Salad"
"ingredient": [5, 6]
}

How to mapping field from json to model field in drf serialiazer?

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.

Resources