Django form wont submit because of autocomplete widget field - django

I want to fill a text input in my form using an autocomplete widget that I have created using jquery ui. Everything works exactly how I want to, except when the form is submitted.
The problem is that when I submit the form, the text input is automatically reseted (I don't know why) and after that, the page reloads saying that the field is required (just validation working how it's supposed to). Of course, if it didn't reset the field everything would go fine.
I dont know if my select event of the autocomplete is working fine, here is the code:
select : function (e, ui) {
// I create a new attribute to store de database primary key of this option. This is
// usefull later on.
$('#%(input_id)s').attr('itemid', ui.item.real_value);
// I set the input text value's.
$('#%(input_id)s').val(ui.item.label);
}
Here is the full code of the autocomplete:
class AutocompleteTextInputWidget (forms.TextInput):
def media(self):
js = ("/js/autocomplete.js", "pepe.js")
def __init__(self, source, options={}, attrs={}):
self.options = None
self.attrs = {'autocomplete': 'off'}
self.source = source
self.minLength = 1
self.delay = 0
if len(options) > 0:
self.options = JSONEncoder().encode(options)
self.attrs.update(attrs)
def render(self, name, value=None, attrs=None):
final_attrs = self.build_attrs(attrs)
options = ''
if value:
final_attrs['value'] = escape(value)
if isinstance(self.source, list) or isinstance(self.source, tuple):
# Crea un Json con las opciones.
source = '['
for i in range(0, len(self.source)):
if i > 0:
source += ', '
source += '"' + self.source[i] + '"'
source += ']'
options = u'''
delay : %(delay)d,
minLength : %(minlength)s,
source : %(source)s
''' % {
'delay' : self.delay,
'minlength' : self.minLength,
'source' : source
}
elif isinstance(self.source, str):
options = u'''
delay : %(delay)d,
minLength : %(minlength)s,
source : function (request, response) {
if ($(this).data('xhr')) {
$(this).data('xhr').abort();
}
$(this).data('xhr', $.ajax({
url : "%(source_url)s",
dataType : "json",
data : {term : request.term},
beforeSend : function(xhr, settings) {
$('#%(input_id)s').removeAttr('itemid');
},
success : function(data) {
if (data != 'CACHE_MISS') {
response($.map(data, function(item) {
return {
label : item[1],
value: item[1],
real_value : item[0]
};
}));
}
},
}))
},
select : function (e, ui) {
$('#%(input_id)s').attr('itemid', ui.item.real_value);
$('#%(input_id)s').val(ui.item.label);
}
''' % {
'delay' : self.delay,
'minlength' : self.delay,
'source_url' : self.source,
'input_id' : final_attrs['id'],
}
if not self.attrs.has_key('id'):
final_attrs['id'] = 'id_%s' % name
return mark_safe(u'''
<input type="text" %(attrs)s/>
<script type="text/javascript">
$("#%(input_id)s").autocomplete({
%(options)s
});
</script>
''' % {
'attrs' : flatatt(final_attrs),
'options' : options,
'input_id' : final_attrs['id']
})
Tip: If I write some text without selecting it from the autocomplete, it still fails.
Another tip: If I set the field as optional it arrives to the view empty.
What should I do to make this work when I submit the form??? I have spent hours trying to
make this work. How can I make the form to recognise that I have allready filled that field?
Here is the code of the form:
test = forms.CharField(label = "autotest", widget = AutocompleteTextInputWidget('/myjsonservice'))
This is the rendered html:
<input type="text" autocomplete="off" id="id_test"/>
<script type="text/javascript">
$("#id_test").autocomplete({
delay : 0,
minLength : 0,
source : function (request, response) {
if ($(this).data('xhr')) {
$(this).data('xhr').abort();
}
$(this).data('xhr', $.ajax({
url : "/myjsonservice",
dataType : "json",
data : {term : request.term},
beforeSend : function(xhr, settings) {
$('#id_test').removeAttr('itemid');
},
success : function(data) {
if (data != 'CACHE_MISS') {
response($.map(data, function(item) {
return {
label : item[1],
value: item[1],
real_value : item[0]
};
}));
}
},
}))
},
select : function (e, ui) {
$('#id_test').attr('itemid', ui.item.real_value);
$('#id_test').val(ui.item.label);
}
});
</script>

Finally found the answer, the problem was that the "name" attribute wasn't rendered. Hence, the field could't get to the view as part of the request.
The final code of the autocomplete widget ended up like this:
class AutocompleteTextInputWidget (forms.TextInput):
def media(self):
js = ("/js/autocomplete.js", "pepe.js")
def __init__(self, source, options={}, attrs={}):
self.options = None
self.attrs = {'autocomplete': 'off'}
self.source = source
self.minLength = 1
self.delay = 0
if len(options) > 0:
self.options = JSONEncoder().encode(options)
self.attrs.update(attrs)
def render(self, name, value=None, attrs=None):
final_attrs = self.build_attrs(attrs)
options = ''
if value:
final_attrs['value'] = escape(value)
if isinstance(self.source, list) or isinstance(self.source, tuple):
# Crea un Json con las opciones.
source = '['
for i in range(0, len(self.source)):
if i > 0:
source += ', '
source += '"' + self.source[i] + '"'
source += ']'
options = u'''
delay : %(delay)d,
minLength : %(minlength)s,
source : %(source)s
''' % {
'delay' : self.delay,
'minlength' : self.minLength,
'source' : source
}
elif isinstance(self.source, str):
options = u'''
delay : %(delay)d,
minLength : %(minlength)s,
source : function (request, response) {
if ($(this).data('xhr')) {
$(this).data('xhr').abort();
}
$(this).data('xhr', $.ajax({
url : "%(source_url)s",
dataType : "json",
data : {term : request.term},
beforeSend : function(xhr, settings) {
$('#%(input_id)s').removeAttr('itemid');
},
success : function(data) {
if (data != 'CACHE_MISS') {
response($.map(data, function(item) {
return {
label : item[1],
value: item[1],
real_value : item[0]
};
}));
}
},
}))
},
select : function (e, ui) {
$('#%(input_id)s').attr('itemid', ui.item.real_value);
$('#%(input_id)s').val(ui.item.label);
}
''' % {
'delay' : self.delay,
'minlength' : self.delay,
'source_url' : self.source,
'input_id' : final_attrs['id'],
}
if not self.attrs.has_key('id'):
final_attrs['id'] = 'id_%s' % name
return mark_safe(u'''
<input type="text" name="%(name)s" %(attrs)s/>
<script type="text/javascript">
$("#%(input_id)s").autocomplete({
%(options)s
});
</script>
''' % {
'attrs' : flatatt(final_attrs),
'options' : options,
'input_id' : final_attrs['id'],
'name' : name
})
If someone knows how to improve this messy code it would be nice.
If someone knows about a nice widget documentation for django 1.4 (Other than the oficial, which sucks by the way) it would be nice too.
Bye, good coding everyone!!!

Related

#Django - 'list' object has no attribute 'id'

I have this code,
def delivery_date(request):
today = datetime.today().date()
results = [get(today)]
stages = Stage.objects.prefetch_related('Stage').all()
for i in range(3):
results.append(get(results[i]))
results = [{'date': i} for i in results]
stages = [{'menu': s} for s in stages]
for i in results:
for stage in stages:
stage['id'] = stage['menu'].id
stage['name'] = stage['menu'].name
stage['desc'] = stage['menu'].desc
stage['menu'] = stage['menu'].Stage.filter(
delivery_date__exact=i['date'])
stage['menu'] = serializers.serialize('python', stage['menu'])
i['menu'] = stages
i['date'] = i['date'].strftime('%b %-d')
return JsonResponse(results, safe=False)
But the results says,
this image
But if the results has only one date, it works.
Like this,
def delivery_date(request):
today = datetime.today().date()
results = [get(today)]
stages = Stage.objects.prefetch_related('Stage').all()
# for i in range(3):
# results.append(get(results[i]))
results = [{'date': i} for i in results]
stages = [{'menu': s} for s in stages]
for i in results:
for stage in stages:
stage['id'] = stage['menu'].id
stage['name'] = stage['menu'].name
stage['desc'] = stage['menu'].desc
stage['menu'] = stage['menu'].Stage.filter(
delivery_date__exact=i['date'])
stage['menu'] = serializers.serialize('python', stage['menu'])
i['menu'] = stages
i['date'] = i['date'].strftime('%b %-d')
return JsonResponse(results, safe=False)
The results,
[
{
"date" : Oct 25,
"menu" : [
{
"menu" : [
{
"model" : backend.product,
"pk" : 13,
"fields" : {
"name" : Tasty Tempeh,
"desc" : Nasi, Ayam, Wortel, Buncis, Bawang Merah, Bawang Putih, Daun Salam, Serai, Minyak Wijen, Minyak Kelapa Sawit.,
"desc_detail" : ,
"delivery_date" : 2019-10-25,
"avatar" : ,
"stage" : 1
}
}
],
"id" : 1,
"name" : Porridge,
"desc" :
}
}
]
What's wrong with my logic? Can Anyone helps?
it is just you missed that menu is a Dict which has a list as value say temp
and that temp file has a dict at its 0 indexes:
use the following :
stage['menu'][0]['id']

Multiple Search Fields not working as expected

I have 3 search field(event_name, event_loc, event_date), while one of them filled it return all table data, while it has to be return one.
views.py
def searchEvent(request):
if request.is_ajax():
q_event_name = request.POST.get('event_name')
q_event_loc = request.POST.get('event_loc')
q_event_date = request.POST.get('event_date')
# return JsonResponse(dict(events=list(Event.objects.values('name', 'info'))))
return JsonResponse(
serializers.serialize('json', Event.objects.filter(
Q(name__icontains=q_event_name) | Q(info__icontains=q_event_loc) | Q(start_event_dt__icontains=q_event_date))),
safe=False)
custom.js
$.ajax({
url: '/searchevent/',
data: {
'event_name': event_name,
'event_loc': event_loc,
'event_date': event_date
},
dataType: 'json',
type: 'POST',
success: function (data) {
data = JSON.parse(data);
event_html = '';
for(var i in data){
event_name = data[i]['fields']['name'];
event_html = event_html + '<div class="col-md-4">' +
' <h5 class="card-title text-center pt-3">' + event_name + '</h5>' +
'</div>';
}
document.getElementById("event-container").innerHTML = event_html;
}
});
for example, I have two event in my DB
name=aaa, loc=bbb, date=2019-01-01
name=ccc, loc=ddd, date=2018-01-01
in the search bar when I searched for only name field with aaa, it returns all of the two event, which i expected to return me only first event.
You are using Q-expressions to build up your query (or-conditions).
So your filter expression will translate to:
name__icontains=q_event_name OR info__icontains=q_event_loc OR start_event_dt__icontains=q_event_date
If q_event_loc is for example an empty string - icontains will match every info column which is not null.

Multipart Request Unit Test with Tastypie

I've been trying to make an unit test for a multipart request but without success. What I've been doing is:
json_data = json.dumps({
"group_type" : "1",
"foo" : {
"visibility" : "4",
"description" : "#natureza tranquilidade",
"bars" : [
{
"x_pos" : 28.16901408450704,
"y_pos" : 38.87323943661972,
"bar_name" : "morro"
},
{
"x_pos" : 65.07042253521126,
"y_pos" : 65.07042253521126,
"bar_name" : "cachoeira"
}
]
}
})
photo = Image.new('RGB', (100, 100))
tmp_file = tempfile.NamedTemporaryFile(suffix='.jpg')
photo.save(tmp_file)
post_data = {
"json_data": json_data,
"photo": photo
}
response = self.client.post(
'/api/endpoint/',
data=post_data,
format='multipart',
authentication=self.get_credentials(self.user2)
)
But I get the following error:
File /venv/lib/python2.7/site-packages/tastypie/serializers.py", line 200, in serialize raise UnsupportedFormat("The format indicated '%s' had no available serialization method. Please check your ``formats`` and ``content_types`` on your Serializer." % format)
UnsupportedFormat: The format indicated 'json' had no available serialization method. Please check your ``formats`` and ``content_types`` on your Serializer.
Do you have any Ideas?
Fixed by uploading a real file and using Django's test API instead of the Tastypie's:
def test_post(self):
json_data = json.dumps({
"group_type" : "1",
"look" : {
"visibility" : "4",
"description" : "#natureza tranquilidade",
"photo_tags" : [
{
"x_pos" : 28.16901408450704,
"y_pos" : 38.87323943661972,
"brand_name" : "morro"
},
{
"x_pos" : 65.07042253521126,
"y_pos" : 65.07042253521126,
"brand_name" : "cachoeira"
}
]
}
})
with open('cards/tests/look_test.jpg') as photo:
response = self.client.client.post(
'/api/endpoint/',
data={'json_data': json_data, 'photo': photo},
HTTP_AUTHORIZATION=self.get_credentials(self.user2)
)
print response
self.assertEquals(response.status_code, 201)
Have fun!

How to search and replace in mongoose?

My objective :I want to update multiple documents in a collection in a certain path with a condition that path matches a regex then search and replace with certain value in the path then finally save all those documents persistently in db.
example:
myCollection : [
{ doc1 : { summary : 'Summary 1 : one', value : 1 },
{ doc2 : { summary : 'Summaryyuist 2 : two', value : 1 },
{ doc3 : { summary : 'hello 3 : three', value : 3 },
];
now i want to replace all 'Summary' with 'hello' in path :'summary'
so the result after query should be :
myCollection : [
{ doc1 : { summary : 'hello 1 : one', value : 1 },
{ doc2 : { summary : 'helloyuist 2 : two', value : 1 },
{ doc3 : { summary : 'hello 3 : three', value : 3 },
];
I am just looking the query to be used above.
From here
http://mongoosejs.com/docs/api.html#query_Query-regex
I found no information how to implement. Specially what the 'Number' parameter does in regex method.?
Also from here :
http://mongoosejs.com/docs/api.html#query_Query-regex
Also i do not found the 'show code' link for monsoose regex. Can someone at least reply a link for mongoose regex code.
i find only how to find models, NOT how to update with replacement with a value that matches the regex in certain path of docs.
How to achieve my objective?
You could use mongoose Model.Update option i hope.
Model.update = function (query, doc, options, callback) { ... }
Example :
MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);
MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, function(err,numberAffected, raw) {
if (err) return handleError(err);
console.log('The number of updated documents was %d', numberAffected);
console.log('The raw response from Mongo was ', raw);
});
http://mongoosejs.com/docs/api.html#model_Model.update

Django Admin: populate the field based on previous field value

I have a model in django admin as follows
ChoiceA= (
("on-false","on-false"),
("on-true","on-true"),
)
ChoiceB = (
("always","always"),
("never","never"),
)
id = models.CharField(verbose_name="Field",max_length=32)
type = models.CharField(verbose_name="Expression",max_length=32)
action = models.CharField(max_length=32, choices=x)
Now based on the type entered by the user ie if user enters type = "a" then action's choices should be set to ChoiceA and if user enters type ="b" then action's choices should be set to ChoiceB. How can I achieve this in Django Admin?
Edit:
action_change.js
jQuery(document).ready(function(){
$("#id_type").change( function(event) {
$.ajax({
"type" : "POST",
"url" : "/action_choices/",
"dataType" : "json",
"cache" : false,
"error" : alert("hello"),
"success" : function(json) {
$('#id_action >option').remove();
for(var j = 0; j < json.length; j++){
$('#id_action').append($('<option></option>').val(json[j][0]).html(json[j][1]));
}
}
});
});
});
You can achieve it using Ajax and jQuery:
models.py:
type = models.CharField(verbose_name="Expression",max_length=32)
action = models.CharField(max_length=32, choices = (('', ''), ))
admin.py:
class MyModelAdmin(admin.ModelAdmin):
list_display = ('type', )
class Media:
js = ['/static/js/action_change.js']
admin.site.register(MyModel, MyModelAdmin)
urls.py:
url(r'^action_choices/', 'myproject.myapp.views.action_choices'),
views.py:
def action_choices(request):
action_list = []
ChoiceA = ("on-false", "on-true")
ChoiceB = ("always", "never")
action_type = request.GET.get('action_type')
if str(action_type).lower() == 'a':
choices = ChoiceA
elif str(action_type).lower() == 'b':
choices = ChoiceB
else:
choices = ()
[action_list.append((each,each)) for each in choices]
json = simplejson.dumps(action_list)
return HttpResponse(json, mimetype='application/javascript')
Create the file action_change.js with following content in your static folder and define correct path in class Media of ModelAdmin.
action_change.js
(function($){
$(function(){
$(document).ready(function() {
$('#id_type').bind('keyup', type_change);
$('#id_action >option').show();
});
});
})(django.jQuery);
// based on the type, action will be loaded
var $ = django.jQuery.noConflict();
function type_change()
{
var action_type = $('#id_type').val();
$.ajax({
"type" : "GET",
"url" : "/action_choices/?action_type="+action_type,
"dataType" : "json",
"cache" : false,
"success" : function(json) {
$('#id_action >option').remove();
for(var j = 0; j < json.length; j++){
$('#id_action').append($('<option></option>').val(json[j][0]).html(json[j][1]));
}
}
})(jQuery);
}
This should work fine for the scenario you asked. And I'm giving my suggestion below:
models.py
type = models.CharField(verbose_name="Expression",max_length=32, choices = (('a', 'a'), ('b', 'b'), ))
action = models.CharField(max_length=32, choices = (('', ''), ))
action_change.js (line 5)
$('#id_type').bind('change', type_change);
You would have to initialize the action field with all possible choices, or Django will complain that a choice that didn't previously exist isn't a valid choice.
My recommendation would be to initialize the field with all of the possible choices, and use JavaScript to toggle the visibility of the choices, depending on the value of type. There are a few plugins around that will handle dynamic fields in Django admin, but most that I've seen deal with ForeignKey or ManyToMany fields that need to do lookups.
You're probably best off just adding some JavaScript to your admin form via the Media meta class and handling it yourself.