I'm trying to implement a search bar to query my database and show only the matches. When I hit submit it just gives me back 'SEARCH', which is what I set as the default instead of printing an error.
ajax.py
...
def chunkSearcher(request):
test = request.GET.get('search_box', "SEARCH")
print(test)
....
Searcher.html
<form type="get" action="." style="margin: 0">
<input id="search_box" type="text" name="search_box" value="Search..." >
<button id="search_submit" type="submit" >Submit</button>
urls.py
url(r'^ajax/chunk/Searcher/$',
ajax.chunkSearcher, name='chunkSearcher')
views.py (It actually works here for some reason but it won't recognize the same two lines of code in my ajax code
def searcher(request):
# test = request.GET.get('search_box', "SEARCH")
# print(test)
this_main = Searcher(
request = request,
num_elements = Candidate.objects.all().count(),
size = 'col-xs-12',
title = 'Search',
modelname = 'Searcher',
listing_fields = [
{'readable_name': 'Name', 'model_attribute': 'full_name()', 'subtext_model': 'email', 'color': 'False'},
{'readable_name': 'Status', 'model_attribute': 'get_status_display()', 'color': 'True'},
{'readable_name': 'Automated Status', 'model_attribute': 'get_auto_status()', 'color': 'True'},
{'readable_name': 'Submitter', 'model_attribute': 'submitter', 'color': 'True'},
],
listing_actions = [
{'tooltip': 'Search', 'color': 'success', 'icon': 'plus', 'permission': 'prog_port.add_candidate', 'modal': 'candidateform', 'controller': 'addCandidate'},
],
)
context = {
'nav' : Nav(request),
'main' : this_main,
'fb' : TestFeedback()
}
return render(request, 'prog_port/base.html', context)
widgets.py
class Searcher:
def __init__(self, request,
num_elements,
size = 'col-xs-12',
modelname = None,
title = None,
listing_fields = None,
listing_actions = None):#!!
self.template = 'prog_port/widgets/Searcher.html'
self.size = size
self.modelname = modelname
self.num_elements = num_elements
self.num_pages = int(math.ceil( num_elements / 25.0))
self.title = title
self.listing_fields = [x['readable_name'] for x in listing_fields]
self.listing_actions = listing_actions
for action in self.listing_actions:
action['restricted'] = False
if 'permission' in action:
if not request.user.has_perm(action['permission']):
action['restricted'] = True
Getting this working without Ajax would be a bit quicker to start. When the action attribute of your form is pointed towards the URL of the current page (rather than towards the URL of your ajax view), the GET request is sent to the view that corresponds to that page's URL - your searcher view in your case. That's why you were able to get the expected values to print when you had those two lines in that view.
Importantly, since the searcher view is the one rendering your page, having access to your search_box value in that view lets you filter or otherwise manipulate the queryset being passed into the view's context and ultimately display only the restricted/filtered items you want shown.
A separate Ajax view doesn't have access to all of that stuff right off of the bat. To dynamically update your search results with a separate Ajax view, that view will need to respond to your request with all of the information necessary to re-render the page appropriately. Practically speaking, that usually means one of two things:
Your search results are displayed within a div or other defined content area, and your Ajax view returns the HTML necessary to populate that content area with the appropriate stuff, or
Your initial view renders its template based on some serialized JSON, and your Ajax view provides updated information in that format which is then used to re-render the template.
This is a good starting point for getting the hang of ajax with django. Notice in the example code given how the view responds to the ajax call with some data (a HTTPResponse or a rendered template), and how that data is then used in the success/failure functions.
If your ajax view returned the HTML necessary to render search results, you could use your success function to update the search results div (or table or whatever) on your page with that new HTML. For example:
views.py
def index(request):
return render(request, "index.html")
def ajax_update(request):
return HttpResponse("<h1>Updated Header</h1>")
index.html
...
<div id="update_this_header">
<h1>Old header</h1>
</div>
<button id='updater'>
...
<script>
$("#updater").click(function() {
$.ajax({
url: #url to ajax_update view
success : function(data) {
$("#update_this_header").html(data)
},
failure : function(data) {
...
}
});
});
</script>
Now clicking the updater button should update the contents of the update_this_header div with the HTML returned in the HttpResponse from our ajax_update view (I admit I didn't test this, forgive me if there's a typo). Updating your search results works the same way; you just need to do more processing in your ajax view to respond with the correct HTML.
I hope this helps make things somewhat clearer; please let me know if I can (try to) explain anything more fully. The important takeaway here is that an ajax view will provide you with Some Data. It's up to you to make sure your template can take that data and properly display it.
Related
I have a mini project with Django.
I have just a model with a counter total :
class Reponse(models.Model):
total = models.IntegerField()
I have this view:
def questions(request):
_reponse = get_object_or_404(Reponse, id= 1)
return render(request, 'vautmieux/test.html', {'reponse': _reponse})
And my HTML test display the variable total:
<p id="block1">
{{reponse.total}}
</p>
My question is :
I would like when I click on a button in the HTML, the variable total increases by 1.
How can I do that ? With a form POST or GET ? May be there is a better method ?
A button like this :
<p id="block1">
{{reponse.total}}
</p>
<button type="button">
Click for increase total by 1
</button>
You can do that way
from django.db.models import F
def questions(request)
_reponse = Reponse.objects.update(total=F('total') + 1)
return render(request, 'vautmieux/test.html', {'reponse': _reponse})
If you want to add a button to increase the counter so you need to create two separate views one to render the html page and the other to increase the integerfield
So you views.py will look like this
from django.db.models import F
from django.views.decorators.csrf import csrf_exempt
def questions(request)
return render(request, 'vautmieux/test.html', {'reponse': _reponse})
#csrf_exempt
def IncreaseCounter(request):
_reponse = Reponse.objects.update(total=F('total') + 1)
return HttpResponse('the counter has been increased')
and in your url you will have :
path('question_page/', views.questions, name='question-html'),
path('increase_counter/', views.IncreaseCounter, name='Increase-Counter')
And last you need just to add a button to target the second view :
<button onclick="window.location.href='/increase_counter/';"> + 1 </button>
And the ideal way is to use ajax so your page will not refresh every time you click the button, to do so you have to change the onclick function of the button and to add the following script :
<button onclick="increase_counter()"> + 1 </button>
<script type="text/javascript">
$.ajax({
url: '/increase_counter/',
method : 'POST',
success: function(response) {
alert('counter increased')
}
});
</script>
But if you want to use ajax you have to add a csrf_exempt decorator on your view.
In order to update a specific object in your model you need to pass the pk as a variable in your url like so :
path('increase_counter/<int:pk>/', views.IncreaseCounter, name='Increase-Counter')
in your button you will loop change the button to be like this :
<button onclick="window.location.href='/increase_counter/{{ response.pk }}/';"> + 1 </button>
for aajax is the same method you add the pk into the url.
And in your view you will add this :
def IncreaseCounter(request, pk):
_reponse = Reponse.objects.filter(pk=pk).update(total=F('total') + 1)
return HttpResponse('the counter has been increased')
In my opinion, this is a much easier straightforward method. This isn't exact code for your problem, but more of a general flow on what the heck all this stuff is and how to use it. So apologies on the wordiness of this post. I am posting for my case because I think will be useful to you as you expand your app to needing to control totals for a particular object. So, in my case, I was needing a button to easily increase or decrease a part quantity for an inventory system, like nuts and bolts. This is what I needed to control inventory while on a part-specific page:
A button with a reverse pattern name that links to increase_inventory view.
Add 1</button>
Notice the "parts.pk", I'm going to talk about it in sec. Also, now and later notice the differences between _ and -. When you see -, it's a pattern name.
In your urls.py, place a similar code like this in your file.
urlpatterns = [
path('increase_inventory_level/<pk>', views.increase_inventory, name='increase-inventory'), ]
Notice a few things here too,
"inventory-level" is the pattern name which is the linking variable between your html page and urls.py.
"increase_inventory_level/<pk" is the URL link for a specific object. Say in your parts list you have: a nut, a bolt, and an o-ring. Each one of these objects is going to have a different PK. Each one of these items has it's own detail page which is going to have its own url, which will look something like this partdetail/<pk. (Close those brackets for pk, I can't in this post or it will disappear)
The last thing to notice is the "views.increase_inventory". This links your app to the specific view that you need.
def increase_inventory(request, *args, **kwargs):
pk = kwargs.get('pk')
part = get_object_or_404(Part, pk=pk)
part.inventory_level = part.inventory_level + 1
part.save()
context = {'part': part}
return render(
request,
'webapp/part_detail.html',
context=context
)
Now we're cooking. The view grabs the object associated with the PK value then edits the inventory attribute of the part object, saves it, and then renders the html page with the new part information.
Basically code works as I expected, but my ListView is not refreshing. Everything works fine, but template does not load itself, I must push reload button (all data are loaded correctly then).
I done simple form/input for testing and there is no problem with views. My project requires calendar widget for picking months, and simplest way to do this I found on the internet was Ajax approach.
Ajax function:
$(document).ready(function () {
$(function () {
$("#datetimepicker1").datetimepicker({
viewMode: 'months',
format: 'MM/YYYY',
}).on('dp.change', function (e) {
var url = "/booking/update_months/{{hotel_id}}";
$.ajax({
type: 'GET',
url: url,
dataType: 'json',
data: {
month: e.date.month(),
},
success: function (data) {
},
error: function (data) {
}
});
})
});
});
Url "/booking/update_months/{{hotel_id}}" refers to first View function I'm using for this functionality:
#csrf_exempt
def update_months(request, hotel_id):
if request.GET.get('month'):
month = request.GET.get('month')
request.session['month'] = int(month) + 1
return HttpResponseRedirect(reverse('booking:hotel_statistics', args=(hotel_id,)))
else:
return render_to_response(request, 'booking/hotel_statistics.html')
Then in HotelStatistics ListView I'm doing some stuff in get_context_data function, nothing special here. Just by some "prints" I've tested that the code is being executed until the end of the class.
class HotelStatistics(ListView):
model = Reservation
context_object_name = 'reservations'
template_name = 'booking/hotel_statistics.html'
def get_context_data(self, **kwargs):
.
.
.
return context
I'm pretty sure that I'm missing something with Ajax functionality, It's my first approach to this language. Thanks in advance for your help.
Why are you using AJAX here? Unless I am missing something, you should just have the date picker inside an HTTP GET form that is submitted.
template.html
<!-- The action attribute hardcoded URL value should be replaced with a Django url templatetag. This will allow the url to be resolved based on the router configuration. i.e. {% url "booking:update_months" hotel_id %} -->
<form action="/booking/update_months/{{ hotel_id }}" method="GET">
<input id="date" name="date">
<button type="submit">Update</button>
</form>
<script>
$(document).ready(function() {
$("#date").datetimepicker({
viewMode: 'months',
format: 'yyyy-mm-dd',
});
});
</script>
view.py
#csrf_exempt
def update_months(request, hotel_id):
date = request.GET.get("date")
if date:
request.session["month"] = datetime.strptime(date, "%Y-%m-%d").month
return redirect("booking:hotel_statistics", False, hotel_id)
return render(request, "booking/hotel_statistics.html")
The code above submits an HTTP GET form, resulting in a server side 301 and therefore a "refresh" of your statistics page with the update session data.
Notice, we are sending up the full date information. I am not sure if you are doing this separately, but if you are allowing users to update each date part independent, you could now bring that together into a single update_date function.
I'm looking at building a site where you have a detail view of an object, and more data about that object can be shown to the user by clicking to open a modal pop up box. I was wondering how, in Django, this data can only be loaded if the user chooses to open the modal box - otherwise there's no point in it being there.
def detail_view(request):
...
extra_data = Object.objects.all().values_list('rating', flat=True) # Only required if a user open the modal box
return render(request, 'detail.html', {'extra_data':extra_data})
Any ideas on how this might be achieved whilst using as little JavaScript as possible?
This is a very broad question, although the following is a generic structure of how you can achieve this. The code below is just for reference. It is just to demonstrate the structure. You need two views, first view will fetch the basic info for all items. The second view fill fetch the additional details for the selected item.
The assumption is that you will have a button for opening the modal, to show the more details.
The javascript is listening to the click event on that button, and it is fetching more details that is to be displayed from the server, and then displaying it in the modal container.
Disclaimer: This is not the most optimal way to do this, this is just a quick and dirty solution.
//Assuming there is a button with id myButton using which user will toggle modal
$("#myButton").on("click", function(e) {
e.preventDefault();
var modal = null;//whatver the modal is
var model_id = 1;//store the id of the model in a accessible location and load it here
var modalContainer = $("#modalContent") // the element which is the container of the modal which will hold its contents
$.ajax({
url: "/get_item_detail", //this url should call viewB
type: "GET",
data: {
"id": model_id
},
success: function(response) {
var html = "<div></div>" //generate your html content for the modal container
modalContainer.html(html); //put the generated html content inside the modal Container
modal.open(); //open the modal here
},
error: function(response) {
}
})
});
from django.shortcuts import render
from django.http import JsonResponse
import json
def viewA(request):
#Fetch the basic info for all objects
items = Sample.objects.all()
return render(reqeust, "template.html", {
"items": items
})
def viewB(request):
#Fetch the additional detail of the concerned object
id = request.GET.get("id", None) #get id or if no id, set None
detailed_item = Sample.objects.get(id=id)
return JsonResponse(json.loads(detailed_item), safe=False)
I am trying to query a database through my website and dynamically add columns with the results using Jinja templates. I am using flask and on my views function. I am rendering the values like this
return render_template('query.html',my_list=my_list )
The thing is that when I load the page the user hasn't queried the database yet, so my_list is empty. The user queries the database by pressing a button on the html page and making an ajax request through jquery. My question now is if its possible to return my_list using jinja templates even after the page is already loaded, meaning I have to return render_template a second time (after submit button) to get the values for my_list.
p.s. I prefer not to use json parsing.
Here is some sample code. I query the database with the values of intranetID of column SubmitterID and platform of column Platforms to return the whole entry to the database with the values of the rest of the columns as well.
#app.route('/querydbvalues',methods=['POST', 'GET'])
def querydbvalues():
if request.method == 'POST' or request.method == 'GET':
results = models.mydatabase.query.filter_by(SubmitterID=qIntranetID,Platforms=qPlatform).all()
my_list = [i.user for i in results]
return render_template('query.html',my_list=my_list )
and on my html page
{% for n in my_list %}
<li>{{n}}</li>
{% endfor %}
on the Javascript file when you press the button i make the ajax request like this
$("#SearchDatabase").click(function(){
var tmp = document.getElementById("qIntranetID").value;
var tmp2 = document.getElementById("qPlatform").value;
jQuery.ajax({
dataType: "json",
url:"/querydbvalues", //tell the script where to send requests
data:{text:tmp,text2:tmp2},
type:'GET',
contentType: 'application/json',
success: function(results){
//do something
}
});
});
The Ajax call is a completely separate request. You can do what you want in that request, including render templates.
I have view in django that add product to the cart( i use django-carton 1.2). That my code:
def add(request,product_id):
cart = Cart(request.session)
product = Product.objects.get(pk=product_id)
if product.quantity >=1:
cart.add(product, price=product.price)
product.quantity-=1
product.save()
return render (request,'shopping/show-cart.html')
else:
return HttpResponse("No product ")
After that view has worked a certain product add to the cart, cart with all products is showing. The problem: when in browser I make function "reload current page" it increase quantity of products in my cart. At the same my remove product view is working, but it only try delete the product when page reload from the function of browser
You should only do actions that modify data - like add and delete - on a POST request, not a GET. You need to create a form in your page, even if it just contains a single button, and check if request.method == 'POST' in the view before doing anything.
Either include a form tag within your html that POSTS information or you can use an Ajax request call.
<script type="text/javascript">
$(document).ready(function(){
$('#add_cart_button').click(function(e)
{
$.ajax({
url: 'xxxxxxx',
type: 'POST',
dataType: 'html',
data: {
'somevariable' : 'somevalue'
},
async: false,
success: function(data){
$('#target').html(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert("Status: " + textStatus); alert("Error: " + errorThrown);
}
});
When you make the Ajax call, it sends whatever you have in your data dictionary to the specified url. From there, Django implements whatever function you want to process that data on the backend and returns the information back. The success function basically gets that information back and does whatever you want with it. In your case, you probably just want to re-render that chunk of HTML that displays the items in your cart.The target tag within the success function is where that chunk of HTML will be rendered, so include a target div tag in your html where you want it to be displayed.
You can access the data from the ajax request in your view by doing request.POST.get('key',somedefaultvalue) (if you want to have a default value if it can't find the dictionary or if it's empty) or just request.POST[key].