Django giving error when save JSON data in models - python

I tried this code to save my JSON data to my model that is Mvouchar. But getting this error. I easily get data through cmd but I tried to save this in my model then I get the error, why this happens, I think am doing some minor mistake but can't catch please help if u get my problem
#views.py
#csrf_exempt
def jsdata(request):
table_data = json.loads(request.POST.get('MyData'))
print(table_data)
for data in table_data:
b_no = request.POST['billno']
b_details = request.POST['billdetails']
at = request.POST['amount2']
record = Mvouchar(bill_no = data.b_no, bill_details = data.b_details,am=data.at)
record.save()
return render(request, 'cheque/mvouchar.html', {'msg': 'Data Saved.'})
#models.py
class Mvouchar(models.Model):
related = models.ForeignKey(Signs, on_delete=models.CASCADE, null=True, blank=True)
bill_no = models.CharField(max_length=80, null=True, blank=True)
bill_details = models.CharField(max_length=1000, null=True, blank=True)
am = models.CharField(max_length=30, null=True, blank=True)
vouchar_no = models.CharField(max_length=1000, null=True, blank=True)
#urls.py
url(r'jsondata/$', views.jsdata, name='jsondata'),
#script
<script>
$("#btnjson").click(function () {
var array1 = [];
$("tbody tr").each(function () {
var firstTableData = {};
firstTableData.BillNo = $(this).find('td').eq(0).text();
firstTableData.BillDetails = $(this).find('td').eq(1).text();
firstTableData.Amount = $(this).find('td').eq(2).text();
array1.push(firstTableData);
//}
});
alert(JSON.stringify(array1));
$.ajax({
type: "POST",
url: "/jsondata/",
dataType: 'json',
data: {MyData: JSON.stringify(array1)},
success: function(msg){
alert(msg);
}
});
return false;
} );
});
</script>

from django.http import JsonResponse
def jsdata(request):
table_data = json.loads(request.POST.get('MyData'))
# print(table_data)
r_data = {
'success': True,
}
for data in table_data:
# Since you are just creating objects you don't need to save created object in a variable.
try:
Mvouchar.objects.create(bill_no = data['BillNo'], bill_details=data['BillDetails'],at=data['Amount'])
except:
r_data['success'] = False
# IMO Views responding to ajax requests should send JsonResponse
if r_data['success']:
r_data['msg'] = 'Data Saved'
else:
r_data['msg'] = 'Not all Data Saved'
return JsonResponse(r_data)

This error probably occurs when the key not present in request data. By changing
b_no = request.POST['billno']
to
b_no = request.POST.get('BillNo').
solve throwing this exception. Even if data not present it returns None. Or can handle it by add try.except block.
try:
b_no = request.POST['billno']
except KeyError:
pass
And in your code, you call every dict key in lower case form and amount key as amount2. Change that to Amount

If your are storing data in table_data (table_data = request.POST.get('MyData')) then use table_data to store it in models.
Your data is in dictionary form so need to call values by its keys.
like -
#csrf_exempt
def jsdata(request):
table_data = json.loads(request.POST.get('MyData'))
print(table_data)
for data in table_data:
b_no = data.get('BillNo')
b_details = data.get('BillDetails')
at = data.get('Amount')
record = Mvouchar(bill_no = b_no , bill_details = b_details ,am=at )
record.save()
return render(request, 'cheque/mvouchar.html', {'msg': 'Data Saved.'})
Try this and tell me if any error occurs.

A KeyError is usually thrown when you look up a value for a key in a dictionary that does not exist. You could either provide a default value or check the existence of the key before you do the lookup.
request.POST.get('billno', 'default_value')
For more information about querying a dictionary with default values, see this helpful StackOverflow answer
The line above as such will not work, since there are other issues in this code besides the key not existing with only lowercase letters (see below).
Looking at the code I expect the key to not exist either because it has not been sent or because it may contain uppercase letters.
As it has been pointed out in the comments to your question, not only are you querying for billno (only lowercase letters) while the key you send has uppercase letters (BillNo), but you also embed it into another dictionary MyData - you need to change your query from request.POST['billno'] to data['BillNo'] (the same goes for all the other values you are trying to extract).
The correct query would thus look like this:
for data in table_data:
b_no = data['BillNo']
b_details = data['BillDetails']
at = data['Amount']
record = Mvouchar(bill_no = b_no, bill_details = b_details,am=at)
record.save()

Related

How to integrate python script in a django project

So I am extremely new to programming, and am stuck at this issue, I am using python with Django and Mongodb for database. I need to write a service that assigns an ID (not the one assigned by mongodb) upon each user form submission. for example entry 1's ID will be [Prefix entered by user] 2101, entry 2's ID will be [Prefix entered by user] 2102, so its basically adding in the number 2100.
I have no idea how and where to integrate this logic in my code. I have tried a few solutions on the internet but nothing seems to work.
my code:
Model.py
class Writeups(Document):
blog_id = 2100
title = fields.StringField(max_length=120)
date_created = fields.DateField(blank=True, null=True)
date_modified = fields.DateField(blank=True, null=True)
version_number = fields.DecimalField(null= True , max_digits=1000, decimal_places=2)
storage_path = fields.StringField(max_length=120)
STRIKE_READY_BRIEF = 'SRB'
STRIKE_READY_THREAT_REPORT = 'SRTR'
PREFIX_CHOICES = [
(STRIKE_READY_BRIEF, 'SRB'),
(STRIKE_READY_THREAT_REPORT, 'SRTR'),
]
prefix = fields.StringField(
max_length=4,
choices=PREFIX_CHOICES,
null=False,
blank=False,
)
views.py:
#csrf_exempt
def writeups_request(request):
"""
Writeup Request
"""
if request.method == 'GET':
try:
data = {
'form-TOTAL_FORMS': '1',
'form-INITIAL_FORMS': '0',
}
writeups = WriteupsFormset(data)
# print(writeup)
return render(request, "writeups/writeups.html", {'writeups_forms': writeups})
except Exception as e:
print(e)
response = {"error": "Error occurred"}
return JsonResponse(response, safe=False)
if request.method == 'POST':
writeup_data = WriteupsFormset(request.POST)
if writeup_data.is_valid():
flag = False
logs = []
for writeups_data in writeup_data:
print(writeups_data)
if writeups_data.cleaned_data.get('DELETE'): # and malware_data._should_delete_form(form):
continue
title = writeups_data.cleaned_data.get('title')
date_created = writeups_data.cleaned_data.get('date_created')
date_modified = writeups_data.cleaned_data.get('date_modified')
version_number = writeups_data.cleaned_data.get('version_number')
storage_path = writeups_data.cleaned_data.get('storage_path')
prefix = writeups_data.cleaned_data.get('prefix')
try:
writeups = Writeups(),
title=title,
date_created=date_created,
date_modified=date_modified,
version_number=version_number,
storage_path=storage_path,
prefix=prefix)
writeups.save()
In order to implement your custom script at all submissions, Django's Signals are the solution for your use case. Look into post_save and pre-save signals and use them as per your problem.
https://docs.djangoproject.com/en/3.2/ref/signals/
If you have an existing database and you want a script to iterate through it and update the dataset, you can take a look at Management Commands
https://docs.djangoproject.com/en/4.0/howto/custom-management-commands/

I can't add Many-To-Many relation in Django

I have 2 models:
models.py:
class Item(models.Model):
name = models.CharField(max_length=100)
price = models.FloatField(max_length=20)
shelfLife = models.BooleanField()
def __str__(self):
return self.name
#property
def shL(self):
temp = "Doesnt' have shelf life"
if(self.shelfLife):
temp = "Does have sehlf life"
return temp
class Order(models.Model):
num = models.CharField(max_length=20)
date = models.DateField()
items = models.ManyToManyField(Item)
def __str__(self):
return self.num
according to this doc I can do:
views.py:
elif request.method == "POST":
list_items = request.POST.getlist('arr[]') # get list of items
order_num = request.POST.getlist('o_n') # get order num
order_date = request.POST.getlist('o_d') # get order date
order = Order(num=order_num[0], date=order_date[0])
order.save()
for i in range(len(list_items)):
item_name = str(list_items[i])
item = Item.objects.filter(name=item_name)
order.items.add(item)
To fetch each item that I need, I loop through the list_items list of strings and filter each object request by this string and then just add the item to the many-to-many field of the order model.
In addition, when I fetch
item = Item.objects.filter(name="Salad")
the returned QuerySet is not empty, however, if I pass a string variable to the name filter it returns an empty QuerySet.
I would appreciate any help!
Thanks
EDIT:
As #Willem Van Onsem sudjested I chaned my views.py to:
elif request.method == "POST":
list_items = request.POST.getlist('arr[]') # get list of items
order_num = request.POST.getlist('o_n') # get order num
order_date = request.POST.getlist('o_d') # get order date
order = Order(num=order_num[0], date=order_date[0])
order.save()
items = Item.objects.filter(name__in=list_items)
order.items.add(*items)
However, I still get a QuerySet items as an empty QuerySet.
I have checked the request variable list_tems and it is a list of strings (it's not empty).
I tested it by creating an array of Strings
arr=["Salad", "Chocolate"]
and paste it as a filter
items = Item.objects.filter(name__in=arr)
order.items.add(*items)
to the QuerySet and it works - QuerySet object items is not empty and it writes the right data to the DB.
index.html:
$(document).on('click','.btn-create-order',function(){
$.ajax({
method: "GET",
contentType: "application/json",
url: "{% url 'order-items api' %}",
success: function(data){
var index = 1;
data.forEach(element => {
$('#multyItemSelect').append("<option value='" + index + "' id='" +index+ "'> "+ element['fields'].name+"</option>")
index++;
});
},
error: function(jqXHR, textStatus, errorThrown){}
})
})
$('.order-new-submit').click(function(){
var order_num = $('#InputNumber').val()
var order_date = $('#InputDate').val()
var item_selected = $('#multyItemSelect').val() // number
var arr = [] // arr for the item names
var index = 0;
item_selected.forEach(e => {
arr[index] = $('#' + e).html()
index++
});
console.log(order_date)
// DEBUGIN
// arr.forEach(e => {
// console.log(e)
// });
// END DEBUGIN
$.ajax({
method: "POST",
url: "{% url 'order-items api' %}",
data: {'arr[]': arr,
'o_n' : order_num,
'o_d' : order_date},
success: function(data){
},
error: function(jqXHR, textStatus, errorThrown){}
})
})
Thanks to #Willem Van Onsem the issue has been found:
print(list_items)
prints values with space at the beginning:
[' Salad', ' Beef Stake ', ' Chocolate']
item in order.items.add(…) needs to be a model object, or the primary key of that model, but not a queryset. You can however make use of iterable unpacking to pass the items wrapped in the QuerySet as individual parameters:
for i in range(len(list_items)):
item_name = str(list_items[i])
items = Item.objects.filter(name=item_name)
order.items.add(*items) # &leftarrow; with an asterisk (*)
You can boost efficiency by fetching all the Items in one query with:
items = Item.objects.filter(name__in=[str(x) for x in list_items])
order.items.add(*items)

The submitted data was not a file. Check the encoding type on the form in DRF 3

OutputError
{
"item_image": [
"The submitted data was not a file. Check the encoding type on the form."
],
"item_thumb": [
"The submitted data was not a file. Check the encoding type on the form."
]
}
Data which I'm posting is
input
{
"item_name": "Lural",
"item_image": "/home/prashant/Desktop/suede.png",
"item_thumb": "/home/prashant/Desktop/suede.png",
"item_description": "sd",
"item_mass": 1,
"item_category": "Make Up",
"item_sub_category": "Sub-Feminine",
"item_est_price": "123.12",
"item_wst_price": "120.34"
}
for media type application/json
views.py
#api_view(['GET', 'POST'])
def product_list(request):
if request.method == 'POST':
serializer = ProductSerializer( data=request.data)
# data.encode("base64")
if serializer.is_valid():
serializer.save()
res_msg = {'Success_Message' : 'Created','Success_Code' : 201}
return Response(res_msg)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
models.py
class Product(models.Model):
item_category_choices = (
('Make Up','Make Up'),
('Skin Care','Skin Care'),
('Fragrance','Fragrance'),
('Personal Care','Personal Care'),
('Hair Care','Hair Care'),
)
item_name = models.CharField(max_length=50)
item_image = models.ImageField()
item_thumb = models.ImageField()
item_description = models.TextField(max_length=200)
item_mass = models.IntegerField()
item_category = models.CharField(max_length=20,choices = item_category_choices)
item_sub_category = models.CharField(max_length=20)
item_est_price = models.DecimalField(max_digits=15,decimal_places=2)
item_wst_price = models.DecimalField(max_digits=15,decimal_places=2)
def __unicode__(self):
return self.item_name or _('Sprint ending %s')% self.item_avg_price
serializers.py
class ProductSerializer(ModelSerializer):
class Meta:
model = Product
fields = ('id','item_name' ,'item_image','item_thumb','item_description','item_mass','item_category',
'item_sub_category','item_est_price','item_wst_price',)
tried many forums & third party packages too but their isn't any way out from this problem.
Also GET is working totally fine.
Thanks for your time
From Django Docs -
If you intend to allow users to upload files, you must ensure that the
environment used to run Django is configured to work with non-ASCII
file names. If your environment isn’t configured correctly, you’ll
encounter UnicodeEncodeError exceptions when saving files with file
names that contain non-ASCII characters.
Thus adding this method to the model solved my problem :
class Product(models.Model):
item_category_choices = (
('Make Up','Make Up'),
('Skin Care','Skin Care'),
('Fragrance','Fragrance'),
('Personal Care','Personal Care'),
('Hair Care','Hair Care'),
)
item_name = models.CharField(max_length=50,verbose_name='Product Name')
item_image = models.ImageField(verbose_name='Product Image')
item_thumb = models.ImageField(verbose_name='Product Thumb')
item_description = models.TextField(verbose_name='Product Descriptions')
item_mass = models.CharField(max_length=10,verbose_name='Product Weight')
item_category = models.CharField(max_length=20, choices = item_category_choices,verbose_name='Product Category')
item_sub_category = models.CharField(max_length=20,verbose_name='Product Sub Category')
item_est_price = models.DecimalField(max_digits=12,decimal_places=2,verbose_name='East Product Price')
item_wst_price = models.DecimalField(max_digits=12,decimal_places=2,verbose_name='West Product Price')
def __unicode__(self):
return (self.item_name)
def image_img(self):
if self.item_image:
return u'<img src="%s" width="50" height="50" />' % self.item_image.url
else:
return '(Sin imagen)'
image_img.short_description = 'Thumb'
image_img.allow_tags = True
Instead of submitting a link to a file "/home/prashant/Desktop/suede.png", you need to actually open the file and submit that instead.
For example, here is a test I have to test image submission:
# generate image and open
tmp_file = Image.new('RGB', (3, 3,), 'white')
tmp_file.putpixel((1, 1,), 0)
tmp_file.save(f.name, format='PNG')
_file = open(f.name, 'rb')
data = {'file': _file}
response = api.client.put(url=url, data=data)
You should open the image and send the request as follows:
with open("/home/prashant/Desktop/suede.png", 'rb') as image:
data = {'item_name': 'Lural',
'item_image': image,
'item_thumb': image,
'item_description': 'sd',
'item_mass': 1,
'item_category': 'Make Up',
'item_sub_category': 'Sub-Feminine',
'item_est_price': '123.12',
'item_wst_price': '120.34'
}
response = api.client.put(url, data, format='multipart')
This should work!

In Django 1.6.x how to get data of associated models in views?

After so much googling I did'nt found any relevant solution for getting the associated models data in views.
Things is I am requesting to the server via Ajax and want to fetch the folders and folder creator name(from User model). But it did'nt return me the first_name of the creator. Here's the code
models.py
class UserFolder(models.Model):
creator = models.ForeignKey(User)
name = models.CharField(blank=False, max_length=150)
is_shared = models.BooleanField(default=False)
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
views.py
def pagination(obj, request):
max = request.GET.get('max') if 'max' in request.GET else 1
paginator = Paginator(obj, max) # Show 25 contacts per page
page = request.GET.get('page')
try:
return paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
return paginator.page(1)
except EmptyPage:
return ""
def folder_list(request): #called via ajax
folders = UserFolder.objects.filter(creator=request.user.id).order_by('-modified_date')
folders = pagination(folders, request) #for pagination
folders = json.dumps({}) if not folders else serializers.serialize("json", folders)
return HttpResponse(folders)
I have tried .select_related('creator') too but it did'nt work.
In js I am fetching the data like this :
$.each(data, function(i, v) {
var t = fldrTpl, id = v.pk;
v = v.fields
t = t.replace(/\{(ID)\}/g, id);
t = t.replace(/\{(NAME)\}/g, v.name);
t = t.replace(/\{(C_SIZE)\}/g, (v.current_size == null?0:v.current_size));
t = t.replace(/\{(C_ID)\}/g, v.creator.first_name);
t = t.replace(/\{(C_DATE)\}/g, v.created_date);
$(".my-folder-table").append(t);
});
v.creator.first_name always return undefined.
Any help would be really appreciated.
Thanks
You can always create a dictionary of the data you want to use on your client side:
def folder_list(request): #called via ajax
folders_obj = UserFolder.objects.filter(creator=request.user).select_related('creator').order_by('-modified_date').all()
folders_dict = []
for folder in folders_obj:
d = {}
for k, v in folder.__dict__.items():
d[k] = str(v)
d["creator__first_name"] = folder.creator.first_name
folders_dict.append(d)
folders = pagination(folders_dict, request) #for pagination
folders = json.dumps({}) if not folders else serializers.serialize("json", folders)
return HttpResponse(folders)
And in your js:
t = t.replace(/\{(C_ID)\}/g, v.creator__first_name);

Using tastypie resource in view

my first question here :
So I'm using tastypie to have api's for my app.
I want to be able to use tastypie to render json and then include that in a django view so that I can bootstrap my app's data.
There is an example of this in django tastypie cookbook here : http://django-tastypie.readthedocs.org/en/latest/cookbook.html#using-your-resource-in-regular-views
The problem is that I CANNOT get this to work, I've tried variants from simpler to more complex and I just cant get it, here some code for my models :
class ChatMessage(models.Model):
content = models.TextField()
added = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(ChatUser, related_name="messages")
chat_session = models.ForeignKey(ChatSession, related_name="messages")
answer_to = models.ForeignKey('self', blank=True, null=True)
flagged = models.BooleanField(blank=True,default=False)
mododeleted = models.BooleanField(blank=True,default=False)
mododeleted_by = models.ForeignKey(ChatUser,blank=True,null=True,default=None)
mododeleted_at = models.DateTimeField(blank=True,null=True,default=None)
[...]
class ChatSession (models.Model):
title = models.CharField(max_length=200)
link_title = models.CharField(max_length=200)
description = tinymce_models.HTMLField()
date = models.DateTimeField()
online = models.BooleanField(default=False)
next_session = models.BooleanField(default=False)
meps = models.ManyToManyField(ChatMep)
uid_newsupdate = models.CharField(max_length=200,blank=True,null=True,default="")
[...]
and my resources :
class ChatMessageResource(MyModelResource):
chat_session = fields.ForeignKey(ChatSessionResource, 'chat_session')
def renderOne(self,request,pkval):
data = self.obj_get(None,pk=pkval)
dbundle = self.build_bundle(obj=data,request=request)
return self.serialize(None,self.full_dehydrate(dbundle),'application/json')
def dehydrate(self, bundle):
bundle.data['likes'] = bundle.obj.get_likes()
bundle.data['likes_count'] = len(bundle.data['likes'])
return bundle
class Meta:
authentication = Authentication()
authorization = Authorization()
queryset = ChatMessage.objects.all()
resource_name = 'message'
fields = ('content', 'added', 'flagged', 'mododeleted','author','answer_to','chat_session')
filtering = {
'chat_session': ALL_WITH_RELATIONS,
}
and my view index :
def index(request):
cur_sess = get_current_chat_session()
data1= ChatMessageResource().renderOne(request,723)
return render_to_response('test.html',
{
'all_data' : data1
},
context_instance=RequestContext(request))
What I want is my renderOne() function to give me the json of ONE ChatMessageResource
And also I'd like a renderAll() function to gice me ALL (or filtered) ChatMessageResources in json.
And I want to use tastypie internals, I KNOW i could serialize it by myself but that's not the point..
Right now the error is :
NoReverseMatch at /live/
Reverse for 'api_dispatch_detail' with arguments '()' and keyword arguments '{'pk': 14L, 'resource_name': 'session'}' not found.
I'm just getting crazy, I've been trying for hours.
So please, how to get ONE/ALL resource as JSON by code using tastypie in a django view !
If It's not clear or I need to clarify, please just ask, thanks
Really what I want to do is to be able to get the JSON returned by an API url I created, but from code, not by visiting the url .. So If I have /api/v1/messages/?chat_session=14 which return a list of messages, I want to be able to do the same by code (and not by fetching the url with curl or something please).
Note :
definition of ModelResource.obj_get from https://github.com/toastdriven/django-tastypie/blob/master/tastypie/resources.py
def obj_get(self, request=None, **kwargs):
"""
A ORM-specific implementation of ``obj_get``.
Takes optional ``kwargs``, which are used to narrow the query to find
the instance.
"""
try:
base_object_list = self.get_object_list(request).filter(**kwargs)
object_list = self.apply_authorization_limits(request, base_object_list)
stringified_kwargs = ', '.join(["%s=%s" % (k, v) for k, v in kwargs.items()])
if len(object_list) <= 0:
raise self._meta.object_class.DoesNotExist("Couldn't find an instance of '%s' which matched '%s'." % (self._meta.object_class.__name__, stringified_kwargs))
elif len(object_list) > 1:
raise MultipleObjectsReturned("More than '%s' matched '%s'." % (self._meta.object_class.__name__, stringified_kwargs))
return object_list[0]
except ValueError:
raise NotFound("Invalid resource lookup data provided (mismatched type).")
So here I found the solution, the problem was with url resolving ... I needed to add
def get_resource_uri(self, bundle_or_obj):
return '/api/v1/%s/%s/' % (self._meta.resource_name,bundle_or_obj.obj.id)
to the related object (session here) in order for it to work (don't ask why!)
So here is my working solution for renderDetail and renderList :
def renderDetail(self,pkval):
request = HttpRequest()
request.GET = {'format': 'json'}
resp = self.get_detail(request, pk=pkval)
return resp.content
def renderList(self,options={}):
request = HttpRequest()
request.GET = {'format': 'json'}
if len(options) > 0:
request.GET.update(options)
resp = self.get_list(request)
return resp.content
And here is an example usage :
cmr = ChatMessageResource()
dataOne= cmr.renderDetail("723")
dataAll = cmr.renderList({'limit':'0','chat_session':cur_sess.pk})
https://github.com/toastdriven/django-tastypie/issues/962
I've found that obj_get method needs a bundled request object. See the link.
def user_detail(request, username):
ur = UserResource()
# Add this request bundle to the obj_get() method as shown.
req_bundle = ur.build_bundle(request=request)
user = ur.obj_get(req_bundle, username=username)
....
Your problem seems to be here:
data = self.obj_get(None,pk=pkval)
The parameters to obj_get should be kwargs that can be passed directly to a standard get. None should not be in there.

Categories

Resources