EmbeddedDocument in a Document doesn't initialize - python

I'm using drf_mongoengine for the first time and I'm having problems setting up the models. I want the documents to be initialized like this:
{
"name" : "new_name",
"metadata": {
"total_efficiency": 0.0,
"eff_vs_layer_thickness":{
"x":[],
"y":[]
}
}
}
The documents are created without the "metadata" field. What Am I missing?
Models:
class Detector(Document):
name = fields.StringField(null=True)
metadata = fields.EmbeddedDocumentField(Metadata, null=False)
class Metadata(EmbeddedDocument):
eff_vs_layer = fields.EmbeddedDocumentField(Plot)
total_efficiency = fields.DecimalField(null=True, default=0)
class Plot(EmbeddedDocument):
x = fields.ListField(fields.FloatField(null=True), default=[])
y = fields.ListField(fields.FloatField(null=True), default=[])
Serializer:
class DetectorSerializer(mongoserializers.DocumentSerializer):
class Meta:
model = Detector
fields = '__all__'
class MetadataSerializer(mongoserializers.EmbeddedDocumentSerializer):
class Meta:
model = Metadata
fields = '__all__'
View:
class DetectorViewSet(viewsets.ModelViewSet, mixins.UpdateModelMixin, mixins.DestroyModelMixin):
'''
Contains information about inputs/outputs of a single program
that may be used in Universe workflows.
'''
lookup_field = 'id'
serializer_class = DetectorSerializer

#alvcarmona, welcome to DRF-ME. You're generally doing everything right.
Just a couple of things: you don't need MetadataSerializer, as it will be created automatically inside DetectorSerializer.
You also shouldn't mix mixins.UpdateModelMixin and mixins.DestroyModelMixin into a full viewset (viewsets.ModelViewSet), instead, mix them into rest_framework_mongoengine.generics.GenericApiView (like here: https://github.com/umutbozkurt/django-rest-framework-mongoengine/blob/master/rest_framework_mongoengine/generics.py#L16).
Other than that, I think, you're doing everything right. If you can show me your project on github, I could help more.
UPDATE: to mix mixins into generic view, do it as we do here e.g.:
class PostPutViewSet(mixins.CreateModelMixin,
mixins.UpdateModelMixin,
GenericViewSet):
""" Adaptation of DRF ModelViewSet """
pass

Related

How can I restrict the list of objects in API view once an object has been added to a relationship?

I am working in django-rest-framework and I have three models: Event, Performer, and Link. I have many-to-many relationships established on the Event and Performer models as 'links' pointing to the Link model. In the API view, when I am creating or updating an event or performer, I am given a list of all links. I would like them to be removed as options once they've been associated with another object, but I can't seem to figure out how to. Below is my code:
class Link(models.Model):
created = models.DateTimeField(auto_now_add=True)
address = models.URLField()
def __str__(self):
return f"{self.address}"
class Meta:
ordering = ['created']
class Performer(models.Model):
created = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
links = models.ManyToManyField(Link)
def __str__(self):
return f"{self.first_name} {self.last_name}"
class Meta:
ordering = ['created']
class Event(models.Model):
created = models.DateTimeField(auto_now_add=True)
sale_date = models.DateTimeField()
event_date = models.DateTimeField()
performer = models.ForeignKey(Performer, on_delete=models.CASCADE)
links = models.ManyToManyField(Link)
class Meta:
ordering = ['event_date']
and I'm using this for serializers:
class LinkSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Link
fields = ['url', 'address']
class PerformerSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Performer
fields = ['url', 'first_name', 'last_name', 'links']
class EventSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Event
fields = ['url', 'performer', 'sale_date', 'event_date', 'links']
I thought about using
ManyToManyField.limit_choices_to
but I don't know what my selector would look like. I also thought I could use
Link.objects.exclude(...)
or
Link.objects.filter(...)
call somewhere but I just don't know where. Thanks to anyone who can help!
Edit: thought I’d add that what I thought would work is to use ‘limit_choices_to’ to filter out any links that are included in a relationship, but I couldn’t figure out how to test if an object was in a relationship (and since there’s multiple relationships only testing for one isn’t perfect either)
You should make use of the Serializer class' get_queryset method:
class LinkSerializer(serializers.HyperlinkedModelSerializer):
def get_queryset(self):
return super().get_queryset().filter(performer=None, event=None)
class Meta:
model = Link
fields = ['url', 'address']
I figured out what I was trying to accomplish with this: I needed to restrict the choices for the field at the model level, which I was able to do by passing a predetermined restriction to the 'limit_choices_to=' parameter. See code below and thank you to #anthony2261 for the suggestion, your filter section helped me to understand how to filter even though it wasn't the type of filtering I needed!
# create a dict of filter conditions(?)
restrict_choices = {'performer': None, 'event': None}
class Performer(...):
...
# refer to the restriction defined previously
# when defining the links relationship.
links = models.ManyToManyField(Link, limit_choices_to=restrict_choices)

How to serialize abstract base models and sub-models in Django REST framework

I have a model Complaint which can have many Evidences.
An Evidence can be an Attachment or a Link and is an abstract base model.
from django.db import models
class Complaint(models.Model):
text = models.TextField()
class Evidence(models.Model):
complaint = models.ForeignKey(
Complaint,
related_name='%(class)s_evidences',
on_delete=models.CASCADE
)
is_attached = models.BooleanField()
class Meta:
abstract = True
class Attachment(Evidence):
file = models.FileField(upload_to='attachments')
class Link(Evidence):
url = models.URLField()
I want to be able take input of a Complaint and and all its Evidences from a single view like so:
{
"text": "Lorem ipsum",
"evidences": [
{
"description": "dolor sit amet",
"is_attached": false,
"url": "https://example.com"
},
{
"description": "consectetur adipisicing elit",
"is_attached": true,
"file": "http://localhost:8000/media/attachments/filename"
}
]
}
This is what I have in my serializers.py thus far:
from rest_framework import serializers
from .models import Complaint, Evidence, Attachment, Link
class EvidenceSerializer(serializers.Serializer): # since ModelSerializer can not be used with abstract models
id = serializers.IntegerField(read_only=True)
is_attached = serializers.BooleanField()
class Meta:
abstract = True
model = Evidence
fields = ['id', 'is_attached']
class AttachmentSerializer(EvidenceSerializer, serializers.ModelSerializer):
class Meta:
model = Attachment
fields = EvidenceSerializer.Meta.fields + ['file']
class LinkSerializer(EvidenceSerializer, serializers.ModelSerializer):
class Meta:
model = Link
fields = EvidenceSerializer.Meta.fields + ['url']
class ComplaintSerializer(serializers.ModelSerializer):
evidences = EvidenceSerializer(many=True)
class Meta:
model = Complaint
fields = ['id', 'text', 'evidences']
This gives the following error when requesting to the complaint POST view (/lodge):
TypeError at /lodge
Got a `TypeError` when calling `Complaint.objects.create()`. This may be because you have a writable field on the serializer class that is not a valid argument to `Complaint.objects.create()`. You may need to make the field read-only, or override the ComplaintSerializer.create() method to handle this correctly.
Bottom of the traceback:
TypeError: Complaint() got an unexpected keyword argument 'evidences'
How should I write the serializers for these models?
The error message says to override Complaint.objects.create, but I don’t know how to go about it.
I tried following the example given for writable nested serializers in the official docs, but it does not use abstract models.

Using Swagger with Django Rest Framework, can I see POST parameters in different fields instead of one body

I'm actually create APIs on a Django Website using Django Rest Framework.
I'm trying to document them using Swagger.
I'm using Django 2.1, django-rest-swagger 2.2 and djangorestframework 3.11
Everything is nearly working as expected except something :
Let me explain you :
I have this model (models.py)
class Technology(models.Model):
"""
This model defines the different technologies
"""
name = models.CharField(max_length=CHAR_SHORT)
path = models.CharField(max_length=CHAR_SHORT, validators=[validate_tech_path], help_text='this is only used to construct the url')
image = models.ImageField()
mailer = models.EmailField(blank=True)
external = models.BooleanField(default=False)
internal = models.BooleanField(default=False)
class Meta:
verbose_name_plural = "technologies"
ordering = ['name']
def __str__(self):
return self.name
Then I have the corresponding serializer class (serializer.py):
class TechnologySerializer(serializers.ModelSerializer):
"""
This model defines the different technologies
"""
class Meta:
model = Technology
fields = ('id', 'name', 'path', 'image', 'mailer', 'external', 'internal')
Finally I have my view with generated APIs (views.py):
class TechnologyViewSet(viewsets.ModelViewSet):
queryset = Technology.objects.all()
serializer_class = TechnologySerializer
http_method_names = ['get','post','delete','put']
Here's the result :
Api description 1
Api description 2
As you see on the picture above, the parameters are une the json body.
Is it possible to have something like this for all the parameters :
API parameter wanted
Thanks a lot.
Try using ListCreateAPIView instead of modelviewset you will be able to see your post https://www.django-rest-framework.org/api-guide/generic-views/#listcreateapiview.
from rest_framework.generics import ListCreateAPIView
class TechnologyViewSet(ListCreateAPIView):
queryset = Technology.objects.all()
serializer_class = TechnologySerializer
Hope it help.

How can I add an extra field to a proxied model in Django?

I'm specifically talking about the Tag model, which I have no much experience with. The code goes like this:
#register_snippet
class ArticleTag(index.Indexed,Tag):
class Meta:
proxy=True
search_fields = [
index.SearchField('name', partial_match=True),
index.SearchField('slug', partial_match=True),
]
The Tag model has two fields, 'name' and 'slug'. But now I want to add a third custom field named 'type' that will be simply a CharField.
I tried modifying it like this:
#register_snippet
class ArticleTag(index.Indexed,Tag):
class Meta:
proxy=True
search_fields = [
index.SearchField('name', partial_match=True),
index.SearchField('slug', partial_match=True),
]
merge_to = models.CharField(max_length=500, blank=True, null=True)
panels = [
FieldPanel('name'),
FieldPanel('slug'),
FieldPanel('type'),
]
However the server yields:
ERRORS:
?: (models.E017) Proxy model 'ArticleTag' contains model fields.
How can I achieve what I am trying to do?
Your tag gas to inherit from TagBase and you'll have to create a custom through model. The django-taggit documentation has an example on how to create custom tags.
We can do this below trick :P
class Model(object):
'''
Skip extra field validation "models.E017"
'''
#classmethod
def _check_model(cls):
errors = []
return errors
#register_snippet
class ArticleTag(Model, index.Indexed,Tag):
class Meta:
proxy=True

How to serialize hierarchical relationship in Django REST

I have a Django model that is hierarchical using django-mptt, which looks like:
class UOMCategory(MPTTModel, BaseModel):
"""
This represents categories of different unit of measurements.
"""
name = models.CharField(max_length=50, unique=True)
description = models.CharField(max_length=50, unique=True)
parent = TreeForeignKey('self', null=True, blank=True, related_name='%(app_label)s_%(class)s_sub_uom_categories')
The problem now is I created a REST API using Django REST Framework; how do I make sure that parent field returns serialized data?
Here is the Model Serializer:
class UOMCategorySerializer(BaseModelSerializer):
"""
REST API Serializer for UOMCategory model
"""
class Meta:
model = UOMCategory
In DRF you can use a serializer as a field in another serializer. However, recursion is not possible.
Tom Christie posted a solution on another question (Django rest framework nested self-referential objects). His solution will also work with your problem.
In your UOMCategorySerializer.Meta class you specify the fields you want to use, also list the parent and/or children field(s) there. Then you use Tom Christies solution.
In your case this would give:
class UOMCategorySerializer(ModelSerializer):
class Meta:
model = UOMCategory
fields = ('name', 'description', 'parent', 'children')
Tom Christies solution: By specifying what field to use for parent and/or children, you avoid using too much (and possibily endless) recursion:
UOMCategorySerializer.base_fields['parent'] = UOMCategorySerializer()
UOMCategorySerializer.base_fields['children'] = UOMCategorySerializer(many=True)
The above works for me in a similar situation.
Simple DRF API view using MPTT cache and DRF serializer.
from rest_framework import serializers
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from events.models import Category
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = (
"name",
"slug",
)
class CategoryTreeView(GenericAPIView):
serializer_class = CategorySerializer
def get(self, request, *args, **kwargs):
root_nodes = Category.objects.all().get_cached_trees()
data = []
for n in root_nodes:
data.append(self.recursive_node_to_dict(n))
return Response(data)
def recursive_node_to_dict(self, node):
result = self.get_serializer(instance=node).data
children = [self.recursive_node_to_dict(c) for c in node.get_children()]
if children:
result["children"] = children
return result

Categories