I can't get Django to serialize the AL_NODE as a modelserializer. Is it possible to serialize AL_NODEs?
Here is my code:
class UserSecuritySelectionModelSerializers(serializers.ModelSerializer):
class Meta:
model = UserSecuritySelectionModel()
fields = ('hasChildNode', 'classificationNames', 'tgtWeight','currWeight','SSM','ext_model_id')
Here is a sample of the data and how it is structured in the database:
Code in my views.py
if request.is_ajax() and id is not None:
rootNode = UserSecuritySelectionModel.objects.get(SSM_id=id, classificationNameNode__isnull=True)
if not rootNode.is_root():
node = rootNode.get_root()
data = serializers.serialize('json', node, use_natural_foreign_keys=True)
return JsonResponse(data, safe=False)
userSelectionModelSerializer = UserSecuritySelectionModelSerializers(rootNode)
#data = serializers.serialize('json', [rootNode], use_natural_foreign_keys=True)
return JsonResponse (userSelectionModelSerializer.data, status=201, safe=False)
You say it's not working but you haven't included any details about the error you are getting. This makes everything here a guess.
You should just be setting the model reference to a class, not actually creating an instance of the model
model = UserSecuritySelectionModel()
# should be
class Meta:
model = UserSecuritySelectionModel
Next, I think you should explicitly pass in the instance just to be safe. It's not required, but it makes your intention clear:
UserSecuritySelectionModelSerializers(instance=rootNode)
Third, just return a standard Response, not a JsonResponse, DRF will do the content negotiation for you. That is why you are using it.
return Response(MyLongSerializer(instance=root).data)
Finally, is there any reason for sending a 201? You do not appear to be creating anything in the view. If you are, then send it like this:
return Response(..., status=HTTP_201_CREATED)
Related
I have a django.db.models.Model A whose instances are created in a rest_framework.serializers.ModelSerializer from POST requests.
Depending on the data being sent in the POST, I would like to create one of several other "addon" models, let's say B or C, which I link to the original through a django.db.models.OneToOneField:
from django.db import models
class A(models.Model):
some_field = models.CharField()
class B(models.Model):
a = models.OneToOneField(A, related_name='addon', on_delete=models.CASCADE)
class C(models.Model):
a = models.OneToOneField(A, related_name='addon', on_delete=models.CASCADE)
What I would like to is to have a serializer which validates the incoming data, including some string indicating which addon to use. The serializer then creates the model instance of A and based on this creates the addon model.
I do not want to create a utility field in model A used to determine which addon to use, I would like to create the model directly using the instance of model A and information from the POST itself.
At the same time when accessing the data through a get, I would like to return the original string used to determine which addon to use.
What I have come up with so far:
from rest_framework import serializers
str2model = {'b': B, 'c': C}
class AddonField(serializers.Field):
def to_representation(self, value):
# I completely ignore "value" as no "internal value" is set in "to_internal_value"
myvalue = self.parent.instance.addon
for addon_name, addon_class in str2model.items():
if isinstance(myvalue, addon_class):
return addon_name
def to_internal_value(self, data):
# I create the "internal value" after "A" instance is created, thus here I do nothing?
return data
class ASerializer(serializers.ModelSerializer):
some_field = serializers.CharField()
the_addon = AddonField()
def validate_the_addon(self, value): # here addon is a string
if value in str2model.keys():
return value
def create(self, validated_data):
addon_name = validated_data.pop('the_addon')
addon_class = str2model[addon]
a = super(ASerializer, self).create(validated_data)
addon_class.objects.create(a=a)
return a
class Meta:
model = A
fields = ["some_field", "the_addon"]
When testing this I get:
AttributeError: Got AttributeError when attempting to get a value for field `the_addon` on serializer `ASerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `A` instance.
Original exception text was: 'A' object has no attribute 'the_addon'.
How can I temporarily store the_addon in the serializer until the A instance has been created?
This is how I would typically approach it
# Serializer
class ASerializer(serializers.Serializer):
some_field = serializers.CharField()
addon_b = serializers.CharField(required=False, allow_null=True)
addon_c = serializers.CharField(required=False, allow_null=True)
def create(self, validated_data):
addon_b = validated_data.pop('addon_b')
addon_c = validated_data.pop('addon_c')
a = A.objects.create(some_field=validated_data['some_field'])
if addon_b:
B.objects.create(a=a)
if addon_c:
C.objects.create(a=a)
return a
You can do other validations if necessary.
class TestAPIView01(generics.CreateAPIView):
permission_classes = {}
serializer_class = serializers.ASerializer
queryset = A.objects.all()
Also, look at the related_name on B and C you may want to consider making them different, as that might throw an error in the future. Cheers
I am using Django Rest Framework for a project and I have a nested serializer like this:
class TopSerializer(serializers.ModelSerializer):
contact = (something goes here)
email = (something goes here)
For POST, PATCH, PUT, DELETE I want to specify these values with a slug. Suppose each class (Contact, Email) has a member called resource_id and that is my slug. For these methods I could use:
class TopSerializer(serializers.ModelSerializer):
contact = serializers.SlugRelatedField(read_only=False, slug_field='resource_id')
email = serializers.SlugRelatedField(read_only=False, slug_field='resource_id')
However, for GET I want to return the embedded objects too, so I could use:
class TopSerializer(serializers.ModelSerializer):
contact = ContactSerializer(read_only=True)
email = EmailSerializers(read_only=True)
So how do I specify in my serializer that contact can be either a slug or a serialized object? Is there a way to do this with just one serializer or must I have two different serializers and use the request.method in the view to select which serializer I use?
Or, should I use something like this:
class TopSerializer(serializers.ModelSerializer):
contact = ContactSerializer(read_only=True)
email = EmailSerializers(read_only=True)
contact_rid = serializers.SlugRelatedField(read_only=False,slug_field=resource_id,queryset=models.Contact.objects.all())
email_rid = serializers.SlugRelatedField(read_only=False,slug_field=resource_id,queryset=models.Email.objects.all())
This way I can use contact_rid and email_rid for POST/PATCH/PUT/DELETE and get contact and email fields back in GET.
Am I on the right track? Other suggestions?
Check out custom fields https://www.django-rest-framework.org/api-guide/fields/#custom-fields
You could define a custom serializer fields that overrides serializers.Field and overrride to_representation to return the fully serialized object and to_internal_value to mimic the behavior of a slugRelatedField.
You are on the right track!
Use one related field for write and another to read the full object is a good approach if you need more details for related objects.
You can also add to the slug field the flag write_only=True if you want the field is used only for write. However, checking this option will not hint selected objects when you are under an update route in Browseable API
Check this anwser
I have an API endpoint that return a list of objects of one of my models.
I have a very trivial serializer that currently returns all the model fields.
What I need now is to add to this serializer another field which is calculated by applying a logic on one of the model field and another data structure that is also calculated dynamicly - but should be calculated only once for each call to the API.
What is the way to supply this structure to the serializer for every API call but not for every object the serializer returns?
My ModelViewSet (without the extra field) is quite trivial:
class MyModelViewset(ModelViewSet):
queryset = SomeModel.objects.all()
serializer_class = SomeModelSerializer
Some suggestions on how you can achieve this:
To calculate something only once and use it in the serializer you should add it to the serializer context.
You can create #property in your SomeModel class and it can be exposed via serializer fields as read only. Django has also #cached_property so that value is only calculated once and persist as long as the instance does
You can use SerializerMethodField field if you want to calculate something (you have access the serializer context in there).
Just a quick example on how to use this.
class MyModelViewset(ModelViewSet):
queryset = SomeModel.objects.all()
serializer_class = SomeModelSerializer
def get_serializer_context(self):
context = super().get_serializer_context()
context['my_value'] = [] # calculate something here, you have access to self.request
return context
class SomeModelSerializer(serializers.ModelSerializer):
my_data = serializers.SerializerMethodField()
class Meta:
model = SomeModel
def get_my_data(self, obj):
# you have access to self.context['my_value']
# you have access to obj.some_property
return my_data
Context
I have the following data structure:
class Birthday(ndb.Model):
day = ndb.IntegerProperty()
month = ndb.IntegerProperty()
year = ndb.IntegerProperty()
class User(ndb.Model):
name = ndb.StringProperty()
birthday = ndb.StructuredProperty(Birthday)
# ... other properties
Problem
When I try to use the populate() method on an instance of User, it gives an error: expecting a Birthday instance instead of a dictionary of params.
If I remove the birthday property, everything works fine: the User instance is populated with the dictionary of params.
Shouldn't the populate() method recognize structured properties and automatically populate them as well?
Any clues?
Thanks
PS: The populate method could also use a forgiving mode on which it ignores unknown properties for which there are references on the params dictionary.
>>Added comments
I'm using a generic REST Handler which is extended for accessing and changing several data types. The extension has to define a method getModel() that returns the model class to access/manipulate. The model class has to implement a few methods, namely create(cls, params).
The POST handler parses params (sent by AngularJS using $resouce -- link below) the following way:
# inside the generic REST Handler
params = json.loads(self.request.body, object_hook=self.datetime_decoder) # parse json params
...
self.getModel().create(params) # invokes the create method of the
The model class implements the create method the following way:
#classmethod
def create(cls, params = None):
obj = cls()
if params:
obj.update(**params)
obj.put()
return True, obj
return False, None
The contents of the JSON dict are:
{"name":"Ana Matos","email":"ana.matos#nvd.com","phone":"+35196983465671","birthday":{"day":1,"month":0,"year":1980},"gender":"FEMALE","groups":["2012/2013"],"serviceProviderId":206133}
JSON contens -- firefox screenshot
AngularJS $resource
Are you reporting a bug or requesting a feature? The populate() method requires its parameter types to match the declared type of the property, which in this case is a Birthday instance.
It would help if you showed the contents of the JSON dict that you are passing to populate() (and exactly how you are passing it).
Possibly the solution is as simple as getting the 'birthday' value from the JSON dict and using it to create a Birthday instance. But I would have to see your code to know for sure.
I am using classed based views with django 1.3 and am trying to figure out how to create an object without using the form. I do not need any user input to create the object but I am still getting an error message that the template is missing. Below is my current view where I have tried to subclass the form_valid method but its not working. Any help would be appreciated.
class ReviewerCreateView(CreateView):
model = Reviewer
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.role = 2
self.object.save()
return HttpResponseRedirect(self.get_success_url())
A CreateView is a specialized view whose purpose is to display a form on GET and validate the form data and create a new object based on the form data on POST.
Since you don't need to display a form and process the form data, a CreateView is not the tool for your job.
You either need a plain old function-based view, or, if you prefer to use a class-based view, derive from View and override get() or post(). For example, adapting your sample code:
class ReviewerCreator(View):
def get(self, request, *args, **kwargs):
Reviewer(user=request.user, role=2).save()
return HttpResponseRedirect('/your_success_url/')
I don't believe a view needs to do anything explicit with a form if it does not need one.
You can instantiate a Reviewer object. It's just a python object.
class ReviewerCreateView(CreateView):
model = Reviewer
self.object.user = self.request.user
self.object.role = 2
self.object.save()
return HttpResponseRedirect(self.get_success_url())