Django custom serializer how to input ID/PK - django

I am trying to make custom serializer work. Can't use model serializer since I will be saving data into multiple models. Here is my code:
class RushSerializer(serializers.Serializer):
keyword = serializers.IntegerField(read_only=True)
url = serializers.URLField()
def save(self):
keyword = self.validated_data['keyword']
url = self.validated_data['url']
Url1.objects.create(keyword=keyword, url=url)
And I input data like this:
nov = {'url': 'https://domain.com/'}
nov['keyword'] = id
serializer = RushSerializer(data=nov)
It returns me this error:
KeyError at /admin/keywords/kw/add/
'keyword'
Here is the model itself:
class Url1(models.Model):
keyword = models.ForeignKey(KW)
url = models.URLField()
What is wrong with my code? I tried pretty much anything from here but can't seem to make it work.
EDIT: To clarify, it works if I do it with model serializer and this:
class Meta:
model = Url1
fields = ('keyword', 'url')
So problem isn't in input 100% but in serializer itself.
If I do keyword without read_only then I get this error:
ValueError at /admin/keywords/kw/add/ Cannot assign "104":
"Url1.keyword" must be a "KW" instance.

Related

Django Converting Queryset with Foreign Keys to JSON

I'm trying to pass JSON objects to my template. I have some complicated relationships that I can't modify. Based on OS
posts I've tried different things. The ones that I think brought me close are:
I created a .values() queryset that looks like this
def my_queryset():
results=TodaysResults.objects.values('id','foreignModel1__name',
'foreignModel1__ForeignModel2__title')
print (myquery_set)
my_queryset gives me all the values I need. So I tried to convert it using the methods below.
1)
def make_querydict():
results=TodaysResults.objects.values('id','foreignModel1__name',
'foreignModel1__ForeignModel2__title')
json_results=json.dumps(list(results,cls=DjangoJSONEncoder))
print (json_results)
I get the following error:
"TypeError: list() takes no keyword arguments"
I tried this as well:
def serialize():
fields = ['id','foreignModel1__name','foreignModel1__ForeignModel2__title']
qs = TodaysResults.objects.all()
json_data = serializers.serialize('json',qs,fields=fields)
print(json_data)
But when I print the json_data in only shows id and not the foreign values.
Based on a few answers like this(from 2012) that were along the same lines And I tried:
def for_JSON_response():
response=JsonResponse(dict(results_info=list(TodaysResultslts.objects.values('id','foreignModel1__name',
'foreignModel1__ForeignModel2__title')
print(response)
I don't get any errors but it does not print anything.So, I'm assuming nothing happened.
Based on this I tried:
def my_queryset():
results=TodaysResults.objects.values('id','foreignModel1__name',
'foreignModel1__ForeignModel2__title')
print (JsonResponse(results, safe=False))
I get:
TypeError: Object of type QuerySet is not JSON serializable
And I tried:
def my_queryset():
results=TodaysResults.objects.values('id','foreignModel1__name',
'foreignModel1__ForeignModel2__title')
results_json = serializers.serialize('json',results)
And I got:
AttributeError: 'dict' object has no attribute '_meta'
I've been looking around a lot and some of the responses look outdated. My attempts above are the ones that I believe came the closest to converting the
valuesqueryset to json or getting the values I need into JSON. Is there a way of making a chained query like the one I have in my_queryset and convert it to JSON?
Models.Py
Simplified for this example
class TodaysResults(models.Model):
place = models.CharField(max_length=255)
ForeignModel1 = models.ForeignKey(ForeignModel,related_name='m1')
class ForeignModel(models.Model):
name = Models.CharField(max_length=255)
ForeignModel2 = models.ManyToManyField(M2M, related_name='m2')
class M2M(models.Model):
title = Models.CharField(max_length=255)
Here we have three model TodaysResults, ForeignModel and MModel and I am guessing MModel is a manyTomany relationship with ForeignModel. I am proposing two possible way how we can get all information from TodaysResults serializer.
Possible Solution One
from rest_framework import serializers
class MModelSerializer(serializers.ModelSerializer):
class Meta:
model = MModel
fields = ('title',)
class ForeignModelSerializer(serializers.ModelSerializer):
foreignModel2 = MModelSerializer(many=True) # as it is many to many field
class Meta:
model = ForeignModel
fields = ('name', 'foreignModel2',)
class TodaysResultsSerializer(serializers.ModelSerializer):
foreignModel1 = ForeignModelSerializer()
class Meta:
model = TodaysResults
fields = ('place', 'foreignModel1')
Now pass your TodaysResults queryset TodaysResultsSerializer and from serializer.data you will get your serialized data.
Possible Solution Two
Even we can do this with one serialize as there is not all fields is required.
class TodaysResultsSerializer(serializers.ModelSerializer):
class Meta:
model = TodaysResults
fields = ('place', 'foreignModel1__name', 'foreignModel1__foreignModel2__title')
Though i am not fully sure but this should also work.

Django Rest Framework - Override serializer create method for model with self-referential M2M field

I have a model that looks like this:
class Profile(models.Model):
following = models.ManyToManyField('Profile', related_name='followed')
admin_notes = models.TextField()
And a Django Rest Framework serializer that looks like this:
class ProfileSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Profile
fields = '__all__'
When creating a new Profile I want to interrupt the save/create to set admin_notes programmatically. I know how do to this inside a function-based view, but I am using DRF's viewsets this time and so I want to do this inside the serializer. The self-referential relationship is giving me trouble, though.
I tried this within the ProfileSerializer:
def create(self, **validated_data):
p = Profile(**validated_data)
p.admin_notes = 'Test'
p.save()
This, as well as trying Profile.objects.create instead of just making a new Profile instance in the first line, resulted in the following error:
Exception Value: "<Profile>" needs to have a value for field "id" before this many-to-many relationship can be used.
Trying this:
def create(self, validated_data):
validated_data['admin_notes'] = 'Test'
return super(ProfileSerializer, self).create(**validated_data)
Results in this error:
Exception Value: create() got an unexpected keyword argument 'following'
I understand that Django creates m2m relationships in a two-step-save process, so the error makes sense but still leaves me stumped as to how I can move forward. How can I set the value of admin_notes within the serializer given the way my model is set up?
Try this (assuming that you are passing the ids of the Profile instances to your m2m field):
def create(self, validated_data):
# First, remove following from the validated_data dict...
following_data = validated_data.pop('following', None)
# Set the admin_notes custom value...
validated_data['admin_notes'] = 'Test'
# Create the object instance...
profile = Profile.objects.create(**validated_data)
# Finally, add your many-to-many relationships...
if following_data:
for data in following_data:
profile.followed.add(Profile.objects.get(**data))
return profile

How to set nullable field to None using django rest frameworks serializers

I have a simple view with which I want to be able to set a nullable TimeField to None:
class Device(models.Model):
alarm_push = models.TimeField(null=True, blank=True)
class DeviceSettingsSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = ('alarm_push',)
class DeviceSettingsView(RetrieveUpdateAPIView):
serializer_class = DeviceSettingsSerializer
lookup_field = 'uuid'
def get_queryset(self):
return Device.objects.all()
But if I try to PATCH data like {'alarm_push': None} I get an error like {"alarm_push":["Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]]."]}
How can I set alarm_push to None?
As your Serializer is a ModelSerializer DRF will use a TimeField() for your alarm_push model attribute. When you checkout the sourcecode of the DRF Timefield https://github.com/encode/django-rest-framework/blob/master/rest_framework/fields.py#L1278 you can see that to_internal_value is raising your error when every attempt of parsing the value failes.
So to have your TimeField be empty you should patch {"alarm_push": ""} with an empty string to represent an empty state.

Django Rest Framework – Custom Hyperlink field in serializer

How can I add a custom hyperlink field in a serializer? I would like to have a hyperlink field in my serializer that has query params in it. Since there is no way to pass query params from HyperlinkedRelatedField or HyperlinkedIdentityField as far as I know, I've tried using a SerializerMethodField. However, this only serializes to a string, and is not a clickable URL when I visit the API through my browser. My code looks something like this:
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
class Meta:
model = MyModel
fields = ('url', 'custom_field')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view'),
urllib.urlencode({'param': 'foo'})
)
return result
Also, I am having trouble understanding the difference between a HyperlinkedRelatedField and a HyperlinkedIdentityField, so a brief explanation would be appreciated.
This should do the trick:
from rest_framework.reverse import reverse
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
class Meta:
model = MyModel
fields = ('url', 'custom_field')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view', args=[obj.id], request=self.context['request']),
'param=foo'
)
return result
The reverse function in rest_framework takes a view name (whatever view you'd like to link to), either an args list (the object id, in this case) or kwargs, and a request object (which can be accessed inside the serializer at self.context['request']). It can additionally take a format parameter and any extra parameters (as a dictionary) that you want to pass to it.
The reverse function then builds a nice, fully-formed URL for you. You can add query params to it by simply adding as many ?{}&{}&{} to your result variable and then filling in the series of query params beneath the 'param=foo' inside your format function with whatever other params you want.
The HyperlinkedIdentityField is used on the object itself that is being serialized. So a HyperlinkedIdentifyField is being used in place of your primary key field on MyModel because you are using a HyperlinkedModelSerializer which creates a HyperlinkedIdentityField for the pk of the object itself being serialized.
The HyperlinkedRelatedField is used to define hyperlinked relationships to RELATED objects. So if there were a MySecondModel with a foreign key relationship to MyModel and you wanted to have a hyperlink on your MyModel serializer to all the related MySecondModel objects you would use a HyperlinkedRelatedField like so (remember to add the new field to your fields attribute in Meta):
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
mysecondmodels = serializers.HyperlinkedRelatedField(
many=True
read_only=True,
view_name='mysecondmodel-detail'
)
class Meta:
model = MyModel
fields = ('url', 'custom_field', 'mysecondmodels')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view', args=[obj.id], request=self.context['request']),
'param=foo'
)
return result
If it were a OneToOneField rather than ForeignKey field on MySecondModel then you would set many=False.
Hope this helps!

Denormalizing models in tastypie

What i'm trying to do is to add a query result from a model to a modelresource, as you can see in this block of code:
def dehydrate(self, bundle):
bundle.data['image'] = place_image.image.get(place=1).get(cardinality=0)
I want to add a field to PlaceResource that will contain the image from place_site model where place=1 and cardinality=0. But im recieving an error:
The 'image' attribute can only be accessed from place_image instances
So, my question is: Is it impossible to use the query result from another model in a tastypie modelresource? Im sorry for my bad english, please correct me if something's wrong. Thanks for your time.
There's the complete code:
MODELS.py:
class place(models.Model):
idPlace = models.AutoField(primary_key=True)
Name = models.CharField(max_length=70)
class place_image(models.Model):
idImage = models.AutoField(primary_key=True)
place = models.ForeignKey(place,
to_field='idPlace')
image = ThumbnailerImageField(upload_to="place_images/", blank=True)
cardinality = models.IntegerField()
API.py
from models import place
from models import place_image
class PlaceResource(ModelResource):
class Meta:
queryset = place.objects.all()
resource_name = 'place'
filtering = {"name": ALL}
allowed_methods = ['get']
def dehydrate(self, bundle):
bundle.data['image'] = place_image.image.get(place=1).get(cardinality=0)
return bundle
class PlaceImageResource(ModelResource):
place = fields.ForeignKey(PlaceResource, 'place')
class Meta:
queryset = place_image.objects.all()
resource_name = 'placeimage'
filtering = {"place": ALL_WITH_RELATIONS}
allowed_methods = ['get']
The error you are getting is caused by the fact that you are accessing the image attribute of a model class, not instance.
The object that is being dehydrated in the dehydrate method is stored in obj attribute of the bundle parameter. Also, you are trying to filter place_image models to only those with place=1 and cardinality=0 by accessing the image attribute of place_image model class. Such filtering won't work as image is not a ModelManager instance. You should use objects attribute instead. Furthermore, get() method returns an actual model instance thus a subsequent call to get() will raise AtributeError as your place_image model instances have no attribute get.
So, all in all, your dehydrate should look like this:
def dehydrate(self, bundle):
bundle.data['image'] = place_image.objects.get(place_id=1, cardinality=0).image
return bundle
Notice that this code requires the place_image with desired values to exist, otherwise a place_image.DoesNotExist will be thrown.
There is also some redundancy in your models:
idPlace and idImage can be removed, as django by default creates an AutoField that is a primary key called id when no other primary key fields are defined
place_image.place field has a redundant to_field parameter, as by default ForeignKey points to a primary key field