Python Flask WTForms Dependent Dropdown - python

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.

Related

Converting like button to Ajax powered like button

I am working on a reverse blog project where users can like and comment.
Although I successfully implement the like button to work, But it refreshes the page.
My problem is I am trying to convert it to rather use Ajax to power the button.
And to as well uses a flag to decide which button to be shown, like or unlike.
Please I need help or the logic to guide me on converting the button to work on using Ajax
Please Note: I use pagination and the like button is on the list view and detail view (CBV).
Thank you in advance.
here is my view for the button
here is my model
here is my template rendering
Set a unique id for each like button if you have multiple like buttons. Bind the click event to JS function in which you will create an ajax call. Pass the ID of your model object (to increment likes count) to your JS function which will be passed in your ajax call to your view. Specify the URL of your view at which your like increment function resides.
Below is the implementation of ajax call on button click in django
https://stackoverflow.com/a/13733855/11979793
UPDATE: Class based view changes for the ajax
In your class-based view function:
Accept the parameters:
model_id = json.loads(request.POST.get('param_name')) # model_id could be your post ID to increase the likes
instead of render return JSON response for ajax:
return JsonResponse({
'like_count': model_to_dict(total_likes), # if you want to display total likes
'success': True
})
You can use total likes in success function of ajax to display it in your template.
P.S. import json at start of the file
UPDATE: example class-based view to handle ajax POST request
class myClassView(View):
template_name='index.html'
def post(self,request): # overriding the post method
myparam = json.loads(request.POST.get('paramFromAjax'))
# perform your actions here
# make a dictionary/context say mydic and pass parameters in that
return JsonResponse({
'paramToAjax': mydic,
'success': True
})

What would be the robust design pattern for multiple class forms render/process (create, update) on single page in django

I am working on a django project in which I have to design a sales order page for order processing,now as per requirement I have to create multiple information forms on single page with different fields (formset does not helpful in this case) which I achieved by creating specific form class and then render them in template through class view function
def get_context_data(self, **kwargs):
context = super(CreateOrderView, self).get_context_data(**kwargs)
context['ord_info_form'] = OrderInformationForm()
context['billing_form'] = BillingInformationForm(prefix='billing')
context['shipping_form'] = ShipingInformationForm(prefix='shipping')
context['payment_form'] = PaymentInfoForm()
context['summary_form'] = OrderProductsForm()
return context
My template look like this:
Now, I have to save data for all forms which belongs to multiple models like billing, contact and shipping information. So for save information I created another form class for create order and set it in form_class variable in Createview class, and I override the save function in form class in which I manipulate the data for all model forms. For update order (put operation) I did the same I create another form class for update like create one in form class.
As per my understanding I implement this strategy, but this seems to me repetitive design. How can I design it better for multiple forms with different fields. Please let me know what kind of design pattern I can apply for this.
P.S: If require I'll post more code and assets for clear understanding.
Thanks in advance.
I've been working on exactly the same requirement. What we have done is the following:
Create the classbasedview to render the forms and add them to the context just as you did on the top.
I see you've created the template based on the forms you added to the context, but as someone else pointed out you need to use an alternative rather than just django to make the post of each item. In my case I used plain old ajax to perform my post. So it looks something like this:
function firePost() {
var endpoint = '/sales_center/orders/add';
var channel = getChannelForm();
var order = getOrderForm()
var ip = getIPForms();
var collections = getCollections();
var subscriptions = getBookSubs();
var perpetual = getBookPerp();
$.ajax({
type: 'POST',
url: endpoint,
data: {
'order':JSON.stringify(order),
'channel': JSON.stringify(channel),
'ips':JSON.stringify(ip),
'collections': JSON.stringify(collections),
'subscriptions': JSON.stringify(subscriptions),
'perpetual': JSON.stringify(perpetual)
},
success: function(data) {
console.log(data);
},
error: function(data) {
console.log(data);
}
});
}
In the data section I divided the information by name. My order is composed of a channel, multiple collections, multiple ips, multiple subscriptions and multiple perpetual purchases. The endpoint is the same as the one in the page so in the backend in order to process that we do it like this:
#transaction.atomic
def post(self, request):
channel = self._create_channel(json.loads(request.POST.get('channel')))
order = self._create_order(json.loads(request.POST.get('order')), canal)
ips = self._assign_ips(json.loads(request.POST.get('ips')), canal)
return JsonResponse({}, status=200)
The code above is just an example the _create_channel is a function in our classbasedview. Each function performs the posts in our backend for each model. That way it looks clean and easy to read each step of the order.
I added the #transaction.atomic to perform
any rollbacks in case in one critical step of the order we have
inconsistent data and we need to rollback or delete the data saved before in the database.
In each function we then use the forms we sent on the template in the context data, we use them to save or get errors in case something isn't filled out correctly. We do something like this:
def _create_channel(self, data):
form = ChannelForm(data)
if form.is_valid():
new_channel = form.save(commit=False)
new_channel.save()
return new_channel
else:
return JsonResponse(form.errors, status=400)
I hope the example above helps you.
My suggestion would be:
Build a stable(take your time here, you don't want any redundant data) database using django models.
Use Django Rest Framework to build proper serializers and validators against every model.
Route them to urls and perform other linking operations on your views.
Choose a javascript framework, you are comfortable with(Vue.js,Angular2,React which ever you want) and write the frontend separately(this part will have your multiple forms and stuff).
Use axios to post your data in django.
This is my personal suggestion, you can also go for django formsets but that will not be suitable in your example.

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 - how to implement a 2-step publish mechanism

I'm new to both web development and django so maybe that's a noob question.
I want to do the following:
Ask user to fill some form and submit it.
Then, parse and format the content and display it back to the user to let him verify it.
User can accept the result or go back to the previous view, update data and resend.
This is as far as I can think:
views.py
def add_content(request):
if request.method == 'POST':
form = AddContentForm(request.POST)
if form.is_valid():
content = form.save(commit=False)
return verify_content(request, content)
else:
form = AddContentForm()
return render(request, 'myapp/add_content.html', {'form' : form})
def verify_content(request, content):
return render(request, 'myapp/verify_content.html', {'content' : content})
The verify_content template will obviously contain two buttons ('back', 'ok'), but I don't know how to pass the content object to a view for saving it in the db, or send it back to the previous view from there. Should I use js? Can i do it with just server side code?
Maybe my whole logic is wrong. Should I save the object in the db before verification and then delete it if needed (sounds ugly)? What is a good way to implement this?
Thanks in advance for your time.
You could use the users session for this:
request.session['content'] = content
and in the view where the user should verify his input do:
content = request.session['content']
and voilá you got the content between 2 views.
Django also secures that users can't tinker with its data by either saving it server side, or in a signed cookie.
I would save the form with commit=True in the add_content view, and would add a verified field or something to the model. Then you can append the pk as GET parameter to the link which will get you back to add_content view from verify. You can extract the parameter from request.GET dict.

Customize Django Admin Advice

I have a series of filters I need to run and then I want the ability to loop though the results. I'm thinking of starting with a form where I can select the filter options. I want a next/previous buttons when I'm looping.
How would I implement this? I'm just looking for high level advice and sample code if available.
I know I can set index_template in AdminSite to create the first page. I know there is the SimpleListFilter but I don't think I can use it since I want multiple filters that need to be configured. Also I don't want to have to select all the models to loop though them. I plan on writing a custom add/change view.
I'm not sure how to go from the selected filter options to looping though each of the selected models. I'm not sure if I can pass and store a query set for when I loop though each model. Some of the options I've thought about is storing the filter parameters in the url and the current model number. Another thing I thought about is storing the results in database and recalling it.
Update
Someone thought this was too broad so I'll be a little more specific. I think the best solution will be to inherit from AdminSite and overwrite index_template to be a form that will contain the filters. How would I link the form submit to a view that will loop though the items? I assume I'll need to add a custom view to the admin but I'm not sure how to pass the data to the view.
This is quite a broad question but I'll give it a shot.
There are a few ways you can achieve this:
Setting up a model with filter queries as variables.
models:
class Filter(models.Model):
Filter_Query = models.CharField(max_length=30)
views:
from app_name.models import Filter, Some_Model
def filter(request, pk):
template = loader.get_template("app_name/filter_search.html")
filter_1 = Filter.objects.get(id=pk)
some_model = Some_Model.objects.all()
filter_1_search = model_name.filter(some_option=filter_1)
context = RequestContext(request, {'filter_1_search': filter_1_search})
return HttpResponse(template.render(context))
Then in a separate page, you can load the results like so.
{$("#some_div").load(filter/1)
or even easier you can you can just use AJAX to send whatever filter query you want.
views:
from app_name.models import Some_Model
def filter_query(request):
filter_1 = request.GET.get('filter_query', '')# Receives from AJAX
some_model = Some_Model.objects.all()
filter_1_search = model_name.filter(some_option=filter_1)
jsonDump = json.dumps(str(filter_1_search))
return HttpResponse(jsonDump, content_type='application/json')
javascript:
var data_JSON_Request = {'filter_query': filter_search1, 'csrfmiddlewaretoken': "{{csrf_token}}"};//JSON package.
function ajax_call(data_JSON_Request){
$(function jQuery_AJAX(){
$.ajax({
type: 'GET',
url: '/filter_query/',
data: data_JSON_Request,
datatype: "json",
success: function(data) {$("#sove_div").load(data);
open_model_menu();
},//success
error: function() {alert("failed...");}
});//.ajax
});//jQuery_AJAX
};//ajax_call

Categories

Resources