Django - retrieve information if only it's needed - python

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)

Related

Python Flask WTForms Dependent Dropdown

I was wondering if someone could help me.
I want to be able to click on customer and locations be based off of the certain customer, being a dependent dropdown. This information is coming from a database, hence the queries in the following code.
This is my form function for both customer and location
class CustomerPick(SubForm):
customer = QuerySelectField(u'Customer',
get_label=u'sCustomer',
query_factory=lambda :
(TCustomer.query.order_by(TCustomer.sCustomer)),
validators=[DataRequired(),])
location = QuerySelectField(u'Location',
get_label=u'sLocation',
query_factory=lambda :
(TLocation.query.order_by(TLocation.sLocation)),
validators=[DataRequired(),])
Here is the view portion
#route('new/', methods=['GET', 'POST'])
def new(self):
form = CustomerPick()
if form.validate_on_submit():
This is a picture of the dropdown also for reference, if there is anything else needed for you guys to have a go please let me know. Thanks in advance!
Photo
I don't quite get your question but you want to be able to click a user and populate the dropdown based on the location?
This involves some Ajax sending data back and forth.
I'll give you a minimized version of code snippet (not tested).
// front-end - this handles user behavior in dropdown. When a user changes a value
// in a user drop down, it will send a request to your Flask view function.
$("#user_drop_down").change(function () {
let user_identifier = this.value;
$.ajax({
type: "GET",
url:/url_to_flask_view_function/,
data: {user_identifier: user_identifier},
success: function (resp) {
$('#your_designated_div_for_both_dropdowns_div').html(resp.data)
}
});
});
# back-end - this receives the request sent from front-end and process the Flask-WTF
# form options so that it can render different options in the dropdowns. In this view
# function, we will return jsonified template with newly processed form_object
# instead of rendering the option data. You can return the json with only the data
# but it involves more javascript parsing and may be difficult for you.
#app.route('/url_to_flask_view_function/')
def form_processing():
user_identifier = request.args.get('user_identifier)
# now we've gotten the user_identifier, you will need to query. Below query is totally made up.
query_to_where_location = Location.query.filter(Location.user_identifier= user_identifier).first()
# your query result will not be in a tuple format
# if your query result is like this "locA, locB, locC, locD", you need to process
# it so that you make [('locA', 'locA'), ('locB', 'locB').......]
form_object = MyForm()
form_object.location.choices = processed_list
return jsonify({"data":render_template('template_that_contains_your_drodpdowns.html',
form_obj=form_obj)})
<!-- HTML piece, you should've had this already but make sure to specify your fields in HTML following Jinja2 synthax.-->
<form>
{{form_object.user}}
{{form_object.dropdown}}
</form>
In conclusion, the idea here is that you catch user behavior using .change, then based on the change, you will send request with user_identifier to server side. Once it reaches server-side, you will make a query into the DB and render the same template again with differently processed forms.
The best way to go about doing this is that, once you get the user_identifier into your view, you make query and return jsonified location object, then in your success block, you would alter the of that dropdown input element.
Let me know if you have more questions.

Validate WTForm before submitting

Is it possible to validate a WTForm field after leaving the field but before submit?
For example, after entering a username, that field is validated to see if its available and shows a checkmark, before the user clicks submit.
When the field is changed, perform a check and change the text in an adjacent node. Some things can be validated directly in the browser. To validate against data on the server, send a request with JavaScript to a view that checks the data and returns a JSON response.
#app.route('/username-exists', methods=['POST'])
def username_exists():
username = request.form['username']
exists = check_if_user_exists(username)
return jsonify(exists=exists)
<input id='username' name='username'>
<p id='username-status'></p>
var username_input = $('#username');
var username_status = $('#username-status');
$('#username').on('focusout', function () {
$.post(
"{{ url_for('username_exists') }}",
{
username: username_input.val()
},
function (data) {
username_status.text(data.exists ? '✔️' : '🙅');
}
);
});
This example uses jQuery, but the concept is not specific to any library.
Alternatively, post the entire form to a separate view that only validates the fields, then return jsonify(form.errors) and do something with them in the browser. The code would be essentially the same as above, with some extra logic to put the error messages next to the correct fields.
Remember to still validate the data when the form is submitted, as requests can be made outside the browser with other

Django Search Bar Implementation

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.

How to show user status (Online, Offline) in Django 1.9?

I am trying to make a "social" site, where you can add friends, create posts, etc. So the main problem is, how to show user status, using Django Admin API?
Thanks in advance!
In html:
{{ user.is_authenticated }}
In view:
def index(request):
user = User.objects.get(username="root")
return render(request, 'blog/index.jade', {'users': users})
So this basically returns me True or False, but this is the status not for only "root" user, but anyone.
Make Ajax request every 5 seconds which will be handled by view. And on each request update table column last_active corresponding to that user which will update the timestamp (you have to make a last_active column of timestamp type).
Make another Ajax request every 5 seconds to fetch all the users who are online by comparing current time and last_active timestamp corresponding to each user. It will return all the users online.
You can use this logic to make multiuser/singleuser chat system also.
Code for making Ajax request:
(function getOnline() {
$.ajax({
url: '/get_online',
type: "GET",
data:
{
user:user
},
success: function(data) {
console.log("success");
},
complete: function() {
// Schedule the next request when the current one is complete
setTimeout(getOnline, 5000);
},
error: function(xhr, errmsg, err) {
console.log("error");
}
});
})();
You won't be using the Django admin page for that - that is just for database management. What you are referring to with {{ user.is_authenticated }} is part of the Django templating system. That is a variable that is written to the page on page load. It will not change until the user reloads the page. What you're going to need to do is use javascript's setInterval function to routinely do an ajax call back to the server. So you have a js file with an initialization function that calls the setInterval function, which in turn makes an ajax call every 20 seconds or so. The ajax call goes to a url that is defined in your urls.py file, which associates it with a view that is defined in your views.py file. That view queries the database to see if a user is authenticated or not, and then it returns that info in an HttpResponse to your ajax call, which has a callback that saves the response to an object, which you then render to the page in whatever way you want, to let the user know that other users are or are not logged in.

How to make view that add product working correct in django/python

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].

Categories

Resources