Input for field is missing even after i type the correct format on the API - django

I have coded the following:
models.py
class Job(models.Model):
datetime = models.DateTimeField(default=timezone.now)
combinedparameters = models.CharField(max_length = 1000)
serializers.py
class JobSerializers(serializers.ModelSerializer):
class Meta:
model = Job
fields = ['combinedparameters']
views.py
#api_view(['POST'])
def create_job(request):
job = Job()
jobserializer = JobSerializers(job, data = request.data)
if jobserializer.is_valid():
jobserializer.save()
return Response(jobserializer.data, status=status.HTTP_201_CREATED)
return Response(jobserializer.errors, status=status.HTTP_400_BAD_REQUEST)
Page looks like this:
But if i copy
{'device': 177, 'configuration': {'port_range': 'TenGigabitEthernet1/0/1,TenGigabitEthernet1/0/2,TenGigabitEthernet1/0/3,TenGigabitEthernet1/0/4,TenGigabitEthernet1/0/5', 'port_mode': 'Access', 'port_status': 'Disabled', 'port_param1': 'Test\\n1\\n2\\n3', 'port_param2': 'Test\\n1\\n2\\n3'}}
And click post, got error saying the single quotes have to be double quotes. So i changed it to :
{"device": 177, "configuration": {"port_range": "TenGigabitEthernet1/0/1,TenGigabitEthernet1/0/5", "port_mode": "Access", "port_status": "Disabled", "port_param1": "1\\n2\\n3", "port_param2": "1\\n2\\n3"}}
I clicked post again and this time the following error comes out:
I dont understand why is it happening. The reason why I key in the long format because this is the format i want to save in my database and this format is created upon saving from my html that creates the job
Postman:
Updated Postman:

Your post data object does not contain the required key "combinedparameters". I'm guessing that big object you copy into content is the string you want saved into the CharField combinedparameters? If that's the case you should structure your post data like this:
{
"combinedparameters": "{'device': 177, 'configuration': {'port_range': 'TenGigabitEthernet1/0/1,TenGigabitEthernet1/0/2,TenGigabitEthernet1/0/3,TenGigabitEthernet1/0/4,TenGigabitEthernet1/0/5', 'port_mode': 'Access', 'port_status': 'Disabled', 'port_param1': 'Test\\n1\\n2\\n3', 'port_param2': 'Test\\n1\\n2\\n3'}}"
}

Related

Django Rest Framework and Channels, You cannot call this from an async context

What i am trying to do is to nest a DRF model serializer into another model serialiser's field like so
class username_serial(ModelSerializer):
class Meta:
model = User
fields = ['username','email']
class game_serial(ModelSerializer):
user_01 = username_serial()
class Meta:
model = game
fields = ['id','user_01','user_02','is_private','is_accepted']
Error :
Exception inside application: You cannot call this from an async
context - use a thread or sync_to_async. Traceback (most recent call
last): File
"C:\Users\baza\Desktop\production\venv\lib\site-packages\django\db\models\fields\related_descriptors.py",
line 173, in get
rel_obj = self.field.get_cached_value(instance) File "C:\Users\baza\Desktop\production\venv\lib\site-packages\django\db\models\fields\mixins.py",
line 15, in get_cached_value
return instance._state.fields_cache[cache_name] KeyError: 'user_01'
This works normally without Django Chennels because channels is async and i can't use sync code with, works fine by using:
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
In the settings file but it's not a safe approach when it comes to production.
i tried using channel's database_sync_to_async as a decorator and as well as a function with a SerializerMethodField like so:
class game_serial(ModelSerializer):
user_01 = SerializerMethodField(read_only=True)
#database_sync_to_async
def get_user_01(self, obj):
username = obj.user_01.username
return str(username)
class Meta:
model = game
fields = ['id','user_01','user_02','is_private','is_accepted']
but i get back:
[OrderedDict([('id', 23), ('user_01', <coroutine object SyncToAsync.__call__ at 0x0000000005DF6678>), ('user_02', None), ('is_private', False), ('is_accepted', False)]), OrderedDict([('id', 24), ('user_01', <coroutine object SyncToAsync.__call__ at 0
x0000000005DF6D58>), ('user_02', None), ('is_private', False), ('is_accepted', False)])]
with an
Exception inside application: Object of type 'coroutine' is not JSON
serializable
so i guess that JSON couldn't serialize those "coroutine" objects and normally i should or "want" to get the actual values of them instead.
How can i do the task? any workaround or other methods are welcomed, thank you in advance.
in consumers.py ..
games = await database_sync_to_async(self.get_games)()
serialized_games = game_serial(games, many=True)
await self.send({
"type": "websocket.send",
'text': json.dumps(serialized_games.data)
})
def get_games(self):
return list(game.objects.all())
I never used Django Channels but I know Django and async.
I'll be honest, I don't like these hacky decorators. And I don't think running such a simple task in a thread is a good idea.
You have an obj, so you were able to query the DB earlier. If so, there's a place without async context or async context where accessing the DB works. In the error message user_01 is not found in the "cached" object from the DB. So just prefetch what you need before.
def get_queryset(self):
return game_serial.objects.select_related('user_01')
class game_serial(ModelSerializer):
user_01 = serializers.CharField(source='user_01.username')
This way you don't have problems with this sync-to-async magic, it's more efficient and easier to reason about.
EDIT:
I repeat, you should select related where you fetch the data. After you added another example, I can suggest something like that
def get_games(self):
return list(game.objects.select_related('user_01').all())
and it will work just fine.
You can also try
#database_sync_to_async
def get_games(self):
return list(game.objects.select_related('user_01').all())
and
serialized_games = await game_serial(games, many=True)
In both cases this serializer will work just fine.
class game_serial(ModelSerializer):
user_01 = serializers.CharField(source='user_01.username')
class Meta:
model = game
fields = ['id','user_01','user_02','is_private','is_accepted']

Unit testing Django model with an image - Not quite understanding SimpleUploadedFile

I'm a testing noob and I'm trying to figure out how to write a test to confirm that a model form is valid and will generate a new instance of Post, which is a model that has an image field.
I looked some other SO posts, and it looks like I should be using SimpleUploadedFile to mock the image field. I'm having a hard time understanding how SimpleUploadedFile works (haven't found any straightforward documentation for this application), and different SO posts use some different looking syntax.
Am I supposed to point to a real file path to an actual image that is held somewhere in my Django app, or does this create a fake file to be used?
tests.py
class CreatePost(TestCase):
def test_create_post(self):
data = {
"content": "This is a post, I'm testing it out"
}
files_data = {
"image": SimpleUploadedFile(name='test_image.jpg', content=open(image_path, 'rb').read(), content_type='image/jpeg')
}
response = self.client.post("/new", data=data, files=files_data)
self.assertEqual(Post.objects.count(),1)
self.assertRedirects(response, 'index')
models.py
class Post(models.Model):
content = models.CharField(max_length=260)
timestamp = models.DateTimeField(auto_now_add=True)
posted_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
liked_by = models.ManyToManyField(User, blank=True, related_name="liked_posts")
image = models.ImageField(upload_to='uploads/', verbose_name='image')
def __str__(self):
return f"{self.posted_by} posted {self.content} at {self.timestamp}"
def is_valid_post(self):
return len(self.content) <= 260 and len(self.content) >= 0
class Post_form(ModelForm):
class Meta:
model = Post
fields = ['content', 'image']
I'm having a hard time understanding how SimpleUploadedFile works (haven't found any straightforward documentation for this application)
Take a look on source code of SimpleUploadedFile — it's a simple representation of a file, which just has content, size, and a name.
You don't need to point to a real file of real image (unless you want to). So you can replace real image (in your example — open(image_path, 'rb').read()) with fake data or even empty binary data b''
Also put all request fields under single data object.
And I don't find initialisation of client in your example.
Summarise, your test will finally look like on this one:
from django.test import Client, TestCase
class CreatePost(TestCase):
def setUp(self):
self.client = Client()
def test_create_post(self):
data = {
"content": "This is a post, I'm testing it out",
"image": SimpleUploadedFile(name='test_image.jpg', content=b'', content_type='image/jpeg')
}
response = self.client.post("/new", data=data)
self.assertEqual(Post.objects.count(),1)
self.assertRedirects(response, 'index')

How to change serializer field name when validation error is triggered

I need to change the view of the error displayed when I validate the field.
serializer.py
class ElementCommonInfoSerializer(serializers.ModelSerializer):
self_description = serializers.CharField(required=False, allow_null=True,
validators=[RegexValidator(regex=r'^[a-zA-Z0-9,.!? -/*()]*$',
message='The system detected that the data is not in English. '
'Please correct the error and try again.')]
)
....
class Meta:
model = Elements
fields = ('self_description',......)
This error is displayed
{
"self_description": [
"The system detected that the data is not in English. Please correct the error and try again."
]
}
The key of error dict is field name - self_description. For FE I need to send another format like:
{
"general_errors": [
"The system detected that the data is not in English. Please correct the error and try again."
]
}
How to change this?
One way this could be achieved is via custom exception handler
from copy import deepcopy
from rest_framework.views import exception_handler
def genelalizing_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# Now add the HTTP status code to the response.
if 'self_description' in response.data:
data = deepcopy(response.data)
general_errors = data.pop('self_description')
data['general_errors'] = general_errors
response.data = data
return response
in settings
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils. genelalizing_exception_handler'
}
Another solution is to rewrite the validate method.
def validate(self, data):
self_description = str((data['self_description']))
analyst_notes = str((data['analyst_notes']))
if re.match(r'^[a-zA-Z0-9,.!? -/*()]*$', self_description) or re.match(r'^[a-zA-Z0-9,.!? -/*()]*$', analyst_notes):
raise serializers.ValidationError({
"general_errors": [
"The system detected that the data is not in English. Please correct the error and try again."
]
})
return data
The solution is very simple.
you can rename the key field by using serializer method (source attribute)
below you can find an example code.
class QuestionSerializer(serializers.ModelSerializer):
question_importance = serializers.IntegerField(source='importance')
question_importance = serializers.IntegerField(required=False)
class Meta:
model = create_question
fields = ('id','question_importance','complexity','active')
Above you can see I have an importance field which is present in django model But here I renamed this field to question_importance by using source attribute .
In your case it will be like below,
class ElementCommonInfoSerializer(serializers.ModelSerializer):
general_errors = serializer.CharField(source="self_description")
general_error = serializers.CharField(required=False, allow_null=True,
validators=[])
class Meta:
model = Elements
fields = ('general_error',......)

Convert POST to PUT with Tastypie

Full Disclosure: Cross posted to Tastypie Google Group
I have a situation where I have limited control over what is being sent to my api. Essentially there are two webservices that I need to be able to accept POST data from. Both use plain POST actions with urlencoded data (basic form submission essentially).
Thinking about it in "curl" terms it's like:
curl --data "id=1&foo=2" http://path/to/api
My problem is that I can't update records using POST. So I need to adjust the model resource (I believe) such that if an ID is specified, the POST acts as a PUT instead of a POST.
api.py
class urlencodeSerializer(Serializer):
formats = ['json', 'jsonp', 'xml', 'yaml', 'html', 'plist', 'urlencoded']
content_types = {
'json': 'application/json',
'jsonp': 'text/javascript',
'xml': 'application/xml',
'yaml': 'text/yaml',
'html': 'text/html',
'plist': 'application/x-plist',
'urlencoded': 'application/x-www-form-urlencoded',
}
# cheating
def to_urlencoded(self,content):
pass
# this comes from an old patch on github, it was never implemented
def from_urlencoded(self, data,options=None):
""" handles basic formencoded url posts """
qs = dict((k, v if len(v)>1 else v[0] )
for k, v in urlparse.parse_qs(data).iteritems())
return qs
class FooResource(ModelResource):
class Meta:
queryset = Foo.objects.all() # "id" = models.AutoField(primary_key=True)
resource_name = 'foo'
authorization = Authorization() # only temporary, I know.
serializer = urlencodeSerializer()
urls.py
foo_resource = FooResource
...
url(r'^api/',include(foo_resource.urls)),
)
In #tastypie on Freenode, Ghost[], suggested that I overwrite post_list() by creating a function in the model resource like so, however, I have not been successful in using this as yet.
def post_list(self, request, **kwargs):
if request.POST.get('id'):
return self.put_detail(request,**kwargs)
else:
return super(YourResource, self).post_list(request,**kwargs)
Unfortunately this method isn't working for me. I'm hoping the larger community could provide some guidance or a solution for this problem.
Note: I cannot overwrite the headers that come from the client (as per: http://django-tastypie.readthedocs.org/en/latest/resources.html#using-put-delete-patch-in-unsupported-places)
I had a similar problem on user creation where I wasn't able to check if the record already existed. I ended up creating a custom validation method which validated if the user didn't exist in which case post would work fine. If the user did exist I updated the record from the validation method. The api still returns a 400 response but the record is updated. It feels a bit hacky but...
from tastypie.validation import Validation
class MyValidation(Validation):
def is_valid(self, bundle, request=None):
errors = {}
#if this dict is empty validation passes.
my_foo = foo.objects.filter(id=1)
if not len(my_foo) == 0: #if object exists
foo[0].foo = 'bar' #so existing object updated
errors['status'] = 'object updated' #this will be returned in the api response
return errors
#so errors is empty if object does not exist and validation passes. Otherwise object
#updated and response notifies you of this
class FooResource(ModelResource):
class Meta:
queryset = Foo.objects.all() # "id" = models.AutoField(primary_key=True)
validation = MyValidation()
With Cathal's recommendation I was able to utilize a validation function to update the records I needed. While this does not return a valid code... it works.
from tastypie.validation import Validation
import string # wrapping in int() doesn't work
class Validator(Validation):
def __init__(self,**kwargs):
pass
def is_valid(self,bundle,request=None):
if string.atoi(bundle.data['id']) in Foo.objects.values_list('id',flat=True):
# ... update code here
else:
return {}
Make sure you specify the validation = Validator() in the ModelResource meta.

django output pdf

Hi all as i in learning stage of django so support me.
I have to generate pdf reports in django.I want that the details should be picked from the database and displayed in the pdf document.i am using report lab.
Now have a look at the code
def pdf_view(request):
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=hello.pdf'
p = canvas.Canvas(response)
details = Data.objects.all()
print details
p.drawString(20, 800, details)
p.drawString(30, 700, "I am a Python Django Professional.")
p.showPage()
p.save()
return response
now as a learning example i have made two fields in models
class Data(models.Model):
first_name = models.CharField(max_length =100,blank=True,null=True)
last_name = models.CharField(max_length =100,blank=True,null=True)
def __unicode__(self):
return self.first_name
and i want that in the pdf document it should display the name s whatever i fill through admin but it is giving me error
'Data' object has no attribute 'decode'
Request Method: GET
Request URL: http://localhost:8000/view_pdf/
Django Version: 1.3
Exception Type: AttributeError
Exception Value:
i want to pik the details from the database and display in the pdf document
'Data' object has no attribute 'decode'
It would have helped if you'd posted the actual traceback.
However I expect the issue is this line:
p.drawString(20, 800, details)
Details is a queryset, that is a list-like container of model instances. It's not a string, and neither does it contain a string. Maybe you want something like:
detail_string = u", ".join(unicode(obj) for obj in details)
which calls the __unicode__ method on every object in your queryset, and joins the resulting list with commas.