Serialize custom data in DRF - python

I have two types of static data and don't need any models because I don't want to save those data in the database. The data are generated from a class RandomObjectGenerator.
Data 1 is a string whose length could be up to 2,097,152
Date 2 is a JSON data whose size could be up to 4 like {'a':1, 'b':2, 'c':3, 'd':4}
views.py
from django.shortcuts import render
from rest_framework import views
from rest_framework.response import Response
from .generate import RandomObjectGenerator
from .serializers import ObjectSerializers
import json
# Create your views here.
class ApiView(views.APIView):
def get(self, request):
randomObject=RandomObjectGenerator().get_random_objects()
randomObjectCount=RandomObjectGenerator().get_random_object_count()
objectSerialize=ObjectSerializers(data={'randData':randomObject,'randCount': json.dumps(randomObjectCount)})
if objectSerialize.is_valid():
objectSerialize.save()
return Response(objectSerialize.data)
else:
return Response(objectSerialize.errors)
serializers.py
from rest_framework import serializers
class ObjectSerializers(serializers.Serializer):
randData=serializers.CharField()
randCount=serializers.IntegerField()
I am getting two types of errors here. If I use serializers.IntegerField() then I am getting the "A valid integer is required." error then if I try to use serializers.CharField() then it's showing create() must be implemented.
I got stuck for while and couldn't get any idea how i fixed it.

create() need to be implemented on any serializer you call .save() because calling create is what save does.
So the question really is, why are you calling .save() if you have no intention of saving this to a database? what are you conceptually saving here?
Removing the row with save() from the view should solve your issue.

Related

Why using validators=[URLValidator] when defining a model CharField doesn't make it check for URL-validity upon saving the corresponding ModelForm?

app.models.py:
from django.core.validators import URLValidator
from django.db import models
class Snapshot(models.Model):
url = models.CharField(max_length=1999, validators=[URLValidator])
app.forms.py:
from django import forms
from .models import Snapshot
class SnapshotForm(forms.ModelForm):
class Meta:
model = Snapshot
fields = ('url',)
app.views.py:
from django.http import HttpResponse
from .forms import SnapshotForm
def index(request):
snapshot_form = SnapshotForm(data={'url': 'not an URL!'})
if snapshot_form.is_valid():
snapshot_form.save()
return HttpResponse("")
Why does it save 'not an URL!' into DB, despite the fact that it ain't a valid URL?!
What is the right way to incorporate URLValidator?
You've specified the validators for your field like so: validators=[URLValidator]. This is incorrect because the validators argument takes in a list of callables that perform the validation. In this case even though URLValidator is callable but that's actually the __init__ method of the class being called. You need to pass an instance of the class for it to work properly:
# Note the brackets after URLValidator to instantiate the class
url = models.CharField(max_length=1999, validators=[URLValidator()])
Better yet since you want to take input a URL you should simply use the URLField class for your field:
url = models.URLField(max_length=1999)

Django client.get response has no 'data' attribute

TL;DR I can't figure out why a response object has a content attribute but not a data attribute. I'm learning about Django REST Framework using this tutorial and referencing the testing approaches here.
I'm trying to unit test a REST API response using Django REST Framework. The test is simply trying to assert that the response data length matches the number of objects created in the test, like so:
from django.urls import reverse
from rest_framework.test import APITestCase
from project.models import SomeObject
class ObjectAPITest(APITestCase):
def test_get_list_returns_list_of_objects(self):
SomeObject.objects.create()
SomeObject.objects.create()
response = self.client.get(reverse('object-list'))
self.assertEqual(len(response.data), 2)
Right now, for the sake of testing, I have a model that only has a uuid:
import uuid
from django.db import models
class SomeObject(models.Model):
some_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
And then a simple serializer class:
from rest_framework import serializers
from project.models import SomeObject
class SomeSerializer(serializers.ModelSerializer):
class Meta:
model = SomeObject
fields = ['some_id']
And of course all of this is wired up in a very simple view:
from django.http import JsonResponse
from project.models import SomeObject
from project.serializers import SomeSerializer
def some_list(request):
some_objects = SomeObject.objects.all()
serializer = SomeSerializer(some_objects, many=True)
return JsonResponse(serializer.data, safe=False)
When I run the test, I get this error:
AttributeError: 'JsonResponse' object has no attribute 'data'
But I know that the response has something, because if I add print(response.content) I get output I'd expect:
b'[{"some_id": "241b4a0e-99d4-4239-8034-3afdd77ccb0d"}, {"some_id": "b21d787c-82ec-4e0e-9784-522d84079016"}]'
I expect response.data to return something, because of this specific tip from the Djanto REST Framework testing documentation:
it's easier to inspect response.data
Granted I'm sure I can fix the test by parsing response.content, but, since I'm learning, I'm really baffled by why I can't use response.data despite it being the recommended approach.
It turns out that the testing documentation assumes you're using the Response class from rest_framework.response rather than the JsonResponse class from django.http.response.
Updating the view to this solved the problem:
from rest_framework.response import Response
from project.models import SomeObject
from project.serializers import SomeSerializer
#api_view(['GET'])
def some_list(request):
some_objects = SomeObject.objects.all()
serializer = SomeSerializer(some_objects, many=True)
return Response(serializer.data)

Django Rest Framework ListCreateAPIView not checking has_object_permissions?

I've been trying to figure this out for almost a full day now, and I can't seem to figure out why has_object_permission method isn't called when the ListCreateAPIView is called in DRF. I've tried all the solutions I could find, but according to the docs check_object_permissions is called in this class already.
I know it has to be something stupid I'm missing. Code snippets are below, please help!
views.py:
from accountability.models import AccountabilityItem
from accountability.serializers import AccountabilityItemSerializer
from rest_framework import generics
from .permissions import InGroup
class AccountabilityItemListCreate(generics.ListCreateAPIView):
queryset = AccountabilityItem.objects.all()
serializer_class = AccountabilityItemSerializer
permission_classes = (InGroup,)
permissions.py:
from rest_framework import permissions
class InGroup(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
print('Checking for object')
return False
Another note, I've added the has_permission method to the permissions.py file, and this method runs all the time no matter what.
Thanks!
Calling has_object_permission doesn't make sense for lists. It is intended for single instances.
What you want is to filter your list of objects so it only leaves those for which the user has some permissions. DjangoObjectPermissionsFilter does it but requires django-guardian. You might get a similar result but creating your own filtering class (sources for DjangoObjectPermissionsFilter)

Django: save() vs update() to update the database?

I'm writing a Django app, and I need a function to update a field in the database. Is there any reason to do one of these methods rather than the other?
def save_db_field(name,field,value):
obj = MyModel.objects.get(name=name)
obj.field = value
obj.save()
def update_db_field(name,field,value):
MyModel.objects.get(name=name).update(field=value)
It seems like the second is better because it does it in one DB call instead of two. Is there a reason why fetching, then updating is any better?
There are several key differences.
update is used on a queryset, so it is possible to update multiple objects at once.
As #FallenAngel pointed out, there are differences in how custom save() method triggers, but it is also important to keep in mind signals and ModelManagers. I have build a small testing app to show some valuable differencies. I am using Python 2.7.5, Django==1.7.7 and SQLite, note that the final SQLs may vary on different versions of Django and different database engines.
Ok, here's the example code.
models.py:
from __future__ import print_function
from django.db import models
from django.db.models import signals
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
__author__ = 'sobolevn'
class CustomManager(models.Manager):
def get_queryset(self):
super_query = super(models.Manager, self).get_queryset()
print('Manager is called', super_query)
return super_query
class ExtraObject(models.Model):
name = models.CharField(max_length=30)
def __unicode__(self):
return self.name
class TestModel(models.Model):
name = models.CharField(max_length=30)
key = models.ForeignKey('ExtraObject')
many = models.ManyToManyField('ExtraObject', related_name='extras')
objects = CustomManager()
def save(self, *args, **kwargs):
print('save() is called.')
super(TestModel, self).save(*args, **kwargs)
def __unicode__(self):
# Never do such things (access by foreing key) in real life,
# because it hits the database.
return u'{} {} {}'.format(self.name, self.key.name, self.many.count())
#receiver(pre_save, sender=TestModel)
#receiver(post_save, sender=TestModel)
def reicever(*args, **kwargs):
print('signal dispatched')
views.py:
def index(request):
if request and request.method == 'GET':
from models import ExtraObject, TestModel
# Create exmple data if table is empty:
if TestModel.objects.count() == 0:
for i in range(15):
extra = ExtraObject.objects.create(name=str(i))
test = TestModel.objects.create(key=extra, name='test_%d' % i)
test.many.add(test)
print test
to_edit = TestModel.objects.get(id=1)
to_edit.name = 'edited_test'
to_edit.key = ExtraObject.objects.create(name='new_for')
to_edit.save()
new_key = ExtraObject.objects.create(name='new_for_update')
to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key)
# return any kind of HttpResponse
That resuled in these SQL queries:
# to_edit = TestModel.objects.get(id=1):
QUERY = u'SELECT "main_testmodel"."id", "main_testmodel"."name", "main_testmodel"."key_id"
FROM "main_testmodel"
WHERE "main_testmodel"."id" = %s LIMIT 21'
- PARAMS = (u'1',)
# to_edit.save():
QUERY = u'UPDATE "main_testmodel" SET "name" = %s, "key_id" = %s
WHERE "main_testmodel"."id" = %s'
- PARAMS = (u"'edited_test'", u'2', u'1')
# to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key):
QUERY = u'UPDATE "main_testmodel" SET "name" = %s, "key_id" = %s
WHERE "main_testmodel"."id" = %s'
- PARAMS = (u"'updated_name'", u'3', u'2')
We have just one query for update() and two for save().
Next, lets talk about overriding save() method. It is called only once for save() method obviously. It is worth mentioning, that .objects.create() also calls save() method.
But update() does not call save() on models. And if no save() method is called for update(), so the signals are not triggered either. Output:
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
# TestModel.objects.get(id=1):
Manager is called [<TestModel: edited_test new_for 0>]
Manager is called [<TestModel: edited_test new_for 0>]
save() is called.
signal dispatched
signal dispatched
# to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key):
Manager is called [<TestModel: edited_test new_for 0>]
As you can see save() triggers Manager's get_queryset() twice. When update() only once.
Resolution. If you need to "silently" update your values, without save() been called - use update. Usecases: last_seen user's field. When you need to update your model properly use save().
Both looks similar, but there are some key points:
save() will trigger any overridden Model.save() method, but update() will not trigger this and make a direct update on the database level. So if you have some models with overridden save methods, you must either avoid using update or find another way to do whatever you are doing on that overridden save() methods.
obj.save() may have some side effects if you are not careful. You retrieve the object with get(...) and all model field values are passed to your obj. When you call obj.save(), django will save the current object state to record. So if some changes happens between get() and save() by some other process, then those changes will be lost. use save(update_fields=[.....]) for avoiding such problems.
Before Django version 1.5, Django was executing a SELECT before INSERT/UPDATE, so it costs 2 query execution. With version 1.5, that method is deprecated.
In here, there is a good guide or save() and update() methods and how they are executed.
save() method can be used to insert new record and update existing record and generally used for saving instance of single record(row in mysql) in database.
update() is not used to insert records and can be used to update multiple records(rows in mysql) in database.
Using update directly is more efficient and could also prevent integrity problems.
From the official documentation https://docs.djangoproject.com/en/3.0/ref/models/querysets/#django.db.models.query.QuerySet.update
If you’re just updating a record and don’t need to do anything with
the model object, the most efficient approach is to call update(),
rather than loading the model object into memory. For example, instead
of doing this:
e = Entry.objects.get(id=10)
e.comments_on = False
e.save()
…do this:
Entry.objects.filter(id=10).update(comments_on=False)
Using update() also prevents a race condition wherein something might
change in your database in the short period of time between loading
the object and calling save().
Update only works on updating querysets. If you want to update multiple fields at the same time, say from a dict for a single object instance you can do something like:
obj.__dict__.update(your_dict)
obj.save()
Bear in mind that your dictionary will have to contain the correct mapping where the keys need to be your field names and the values the values you want to insert.
Update will give you better performance with a queryset of more than one object, as it will make one database call per queryset.
However save is useful, as it is easy to override the save method in your model and add extra logic there. In my own application for example, I update a dates when other fields are changed.
Class myModel(models.Model):
name = models.CharField()
date_created = models.DateField()
def save(self):
if not self.pk :
### we have a newly created object, as the db id is not set
self.date_created = datetime.datetime.now()
super(myModel , self).save()
Use _state.adding to differentiate update from create https://docs.djangoproject.com/en/3.2/ref/models/instances/
def save(self, *args, **kwargs):
# Check how the current values differ from ._loaded_values. For example,
# prevent changing the creator_id of the model. (This example doesn't
# support cases where 'creator_id' is deferred).
if not self._state.adding and (
self.creator_id != self._loaded_values['creator_id']):
raise ValueError("Updating the value of creator isn't allowed")
super().save(*args, **kwargs)
one of the differences that might cause a lot of headaches is that save updates, but update does NOT update columns of type DateTimeField(auto_now=True)
or ModificationDateTimeField
These fields automatically (should) set their date when an object is saved to the database.
save():
can be used with model object but not with QuerySet or Manager objects.
with select_for_update() can run SELECT FOR UPDATE query.
update()
can be used with QuerySet or Manager objects but not with model object.
with select_for_update() cannot run SELECT FOR UPDATE query.
For example, I have Person model as shown below:
# "store/models.py"
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
Then, you can use save() with Person model object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.get(id=1)
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.filter(pk=1).first()
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
But, you cannot use save() with QuerySet object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.filter(pk=1)
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
Then, the error below occurs:
AttributeError: 'QuerySet' object has no attribute 'save'
And, you cannot use save() with Manager object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
Then, the error below occurs:
AttributeError: 'Manager' object has no attribute 'save'
Then, you can use update() with QuerySet object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.filter(pk=1)
person.update(name="Tom") # Here
return HttpResponse("Test")
And, you can use update() with Manager object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects
person.update(name="Tom") # Here
return HttpResponse("Test")
But, you cannot use update() with Person model object as shown below:
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.get(id=1)
person.update(name="Tom") # Here
return HttpResponse("Test")
# "store/views.py"
from .models import Person
from django.http import HttpResponse
def test(request):
person = Person.objects.filter(pk=1).first()
person.update(name="Tom") # Here
return HttpResponse("Test")
Then, the error below occurs:
AttributeError: 'Person' object has no attribute 'update'
Next for example, select_for_update() is used to prevent race condition(lost update or write skew) when updating data in Django.
And, I have test view with save() and select_for_update().filter(pk=1).first() as shown below:
# "store/views.py"
from django.db import transaction
from .models import Person
from django.http import HttpResponse
#transaction.atomic
def test(request):
person = Person.objects.select_for_update().filter(pk=1).first() # Here
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
Then, when I run test view, SELECT FOR UPDATE and UPDATE queries are run as shown below. *I used PostgreSQL and these logs below are the query logs of PostgreSQL and you can check On PostgreSQL, how to log SQL queries with transaction queries such as "BEGIN" and "COMMIT":
Now, I remove first() to use update() as shown below:
# "store/views.py"
from django.db import transaction
from .models import Person
from django.http import HttpResponse
#transaction.atomic
def test(request):
person = Person.objects.select_for_update().filter(pk=1) # Here
person.update(name="Tom") # Here
return HttpResponse("Test")
Then, when I run test view, SELECT FOR UPDATE query is not run and only UPDATE query is run as shown below:
And, I have test view with save() and select_for_update().get(pk=1) as shown below:
# "store/views.py"
from django.db import transaction
from .models import Person
from django.http import HttpResponse
#transaction.atomic
def test(request):
person = Person.objects.select_for_update().get(pk=1) # Here
person.name = 'Tom'
person.save() # Here
return HttpResponse("Test")
Then, when I run test view, SELECT FOR UPDATE and UPDATE queries are run as shown below:
Now, I remove get() to use update() as shown below:
# "store/views.py"
from django.db import transaction
from .models import Person
from django.http import HttpResponse
#transaction.atomic
def test(request):
person = Person.objects.select_for_update() # Here
person.update(name="Tom") # Here
return HttpResponse("Test")
Then, when I run test view, SELECT FOR UPDATE query is not run and only UPDATE query is run as shown below:
So, save() with select_for_update() can run SELECT FOR UPDATE query while update() with select_for_update() cannot.

Django serializer for one object

I'm trying to figure out a way to serialize some Django model object to JSON format, something like:
j = Job.objects.get(pk=1)
##############################################
#a way to get the JSON for that j variable???
##############################################
I don't want:
from django.core import serializers
serializers.serialize('json', Job.objects.get(pk=1),ensure_ascii=False)
Because it returns JSON array, not a single object representation.
Any ideas?
One way I'm thinking of: is to find a way to get a hash(attribute,value) of the object and then use simplejson to get the JSON representation of it, however I don't know how to get that hash.
How about just massaging what you get back from serializers.serialize? It is not that hard to trim off the square brackets from the front and back of the result.
job = Job.objects.get(pk=1)
array_result = serializers.serialize('json', [job], ensure_ascii=False)
just_object_result = array_result[1:-1]
Not a fancy answer but it will give you just the object in json notation.
Method-1
Use Django Serializer with python format
from django.core import serializers
j = Job.objects.get(pk=1)
response = serializers.serialize('python', [j], ensure_ascii=False)
Method-2
use json format while serializing and loads the string response
import json
from django.core import serializers
j = Job.objects.get(pk=1)
json_str_response = serializers.serialize('json', [j], ensure_ascii=False)
response = json.loads(json_str_response)[0]
Method-3
Use Django REST Framework's Serializer class
define a serializer class and serialize the instance as
from rest_framework import serializers
class JobSerializer(serializers.ModelSerializer):
class Meta:
model = Job
fields = '__all__'
j = Job.objects.get(pk=1)
response = JobSerializer(instance=j).data
Reference
1. Serializer Django model object
I would suggest using Django's model_to_dict. If I'm not mistaken, serializers.serialize() relies on it, too, but it only works for list, not single model instance. That's how you get a dict instance with your model fields out of a single model:
from django.forms.models import model_to_dict
# assuming obj is your model instance
dict_obj = model_to_dict( obj )
You now just need one straight json.dumps call:
import json
json.dumps(dict_obj)

Categories