render a flask MultipleSelectField with selected values highlighted - javascript

In my flask app, I need to render a form that has a MultipleSelectField with some already selected options. I have done this successfully but the problem is that they are not highlighted and thus the user can't know which options are already selected which can also enable them be deselected.
Any ideas on how I can get this done from the backend or even using vanilla javascript.
Here is the view function that renders the form:
#home.route('/edit/<int:id>', methods=['GET', 'POST'])
#login_required
def edit_post(id):
post = Post.query.get_or_404(id)
if current_user != post.author and not current_user.can(Permission.ADMINISTER):
abort(403)
form = PostForm()
form.tag.choices = [(tag.id, tag.name) for tag in Tag.query.order_by('name')]
if form.validate_on_submit():
post.body = form.body.data
posttags = db.session.query(categories).filter_by(post_id=id).all()
for id in form.tag.data:
if (post.id, id) not in posttags:
post.tags.append(Tag.query.get(id))
db.session.commit()
return redirect(url_for('.view_post', id=post.id))
form.body.data = post.body
# already selected values
form.tag.data = [tag_id for tag_id in db.session.query(categories).filter_by(post_id=post.id).all()]
return render_template('home/edit_post.html', form=form, title='Edit Post')
Have done some research. So, in a nutshell, how can I add the selected attribute to the pre-selected options.

Related

I have a problem in submitting comments to server using json and fetch in django

I am working on a bookstore project using django and javascript. I want to allow user to add comments on each book and send the comment without reloading the page using JSON
html:
<h3 id="authorname">{{book.authorname}}</h3>
<h1 id="bookname"> {{book.name}}</h1>
<p id="slogan">-{{book.slogan}}-</p>
<h2 id="price">Price:- <span>${{book.price}}</span></h2>
<form id="addcomment">
{% csrf_token %}
<input hidden name='thecommentuser' value="{{ request.user.id }}" id="commentsubmitter">
<input hidden name='thecommentbook' value="{{ book.id }}" id="commentbook">
<textarea name='thecomment'id="comment"></textarea>
<input type="submit">
</form>
script:
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('#addcomment').onsubmit = function() {
fetch('/comments', {
method: 'POST',
body: JSON.stringify({
thecomment: document.querySelector('#comment').value,
thecommentuser: document.querySelector('#commentsubmitter').value,
thecommentbook: document.querySelector('#commentbook').value,
})})}});
models.py:
class books(models.Model):
bookuser= models.ForeignKey('User', on_delete=models.CASCADE, default= None, blank=True)
name = models.CharField(max_length=80)
slogan = models.CharField(max_length=200, null=True,
price = models.IntegerField()
authorname= models.CharField(max_length=30)
class comments(models.Model):
comment = models.CharField(max_length= 100)
commentuser = models.ForeignKey('User', on_delete=models.CASCADE, default=None)
commentbook = models.ForeignKey('books', on_delete=models.CASCADE, default=None)
def serialize(self):
return {
'thecomment': self.comment,
'thecommentuser': self.commentuser,
'thecommentbook': self.commentbook
}
urls.py:
urlpatterns = [
path('book/<int:id>', views.book, name='book'),
path('comments', views.addcomments, name='comments')
]
views.py:
from django.views.decorators.csrf import csrf_exempt
from .models import *
from django.contrib.auth.decorators import login_required
import json
def book(request, id):
submitedbook = books.objects.get(id=id)
bkchapters = chapters.objects.filter(chapterbook=submitedbook)
bkrewards = rewards.objects.filter(rewardbook = submitedbook)
return render(request, 'network/book.html', {'book':submitedbook, 'chapters':bkchapters, 'rewards':bkrewards})
#csrf_exempt
#login_required
def addcomments(request):
if request.method == 'POST':
print('posted')
data= json.loads(request.body)
comment = data.get('thecomment', '')
commentuser = data.get('thecommentuser')
commentbook = data.get('thecommentbook', '')
cuser = User.objects.get(id = commentuser)
cbook = books.objects.get(id = commentbook)
thecommentt = comments(
thecomment=comment,
thecommentuser=cuser,
thecommentbook=cbook
)
thecommentt.save()
when I open the comments in the admin page I don't find any submitted data and the page reload upon submitting a comment. the order of print('posted') in views.py isn't displayed to me upon adding a comment and that means that the request isn't sent but I don't know why
this appears to me in the terminal window upon adding the comment:
[30/Oct/2022 01:47:19] "GET /book/1?csrfmiddlewaretoken=9QgGiDOUnVxMlxZQ6UkSmiO4auq5BLojV6iWW55qE9LER929Qj7WB8LVtkBfpnJ4&thecommentuser=1&thecommentbook=1&thecomment=Book HTTP/1.1" 200 14927
I tried to delete if request.method == 'POST': in views and to print in views but nothing changed. Also I tried to console.log('submitted') on submitting the comment but nothing appeared
new update:
I find that the error comes from the browser itself as I find that data is sent upon submitting from another browser so although the two browsers are updated. so how could I solve this?

Why I get empty form? How to automatically create Iniline models?

There are 2 forms on one page.
There are 2 models: 1. Product. 2. SpeciallyPrice. SpeciallyPrice is linked via FK to Product. At the same time, SpeciallyPrice is Inline model in Product.
The fields of the SpecialPriceForm are automatically created using JS. That is, them may be the n-th number. It is necessary to create a record for each created field. In principle, I guess how to do it - use the cycle to drive the values obtained. But the problem is that for some reason None comes from the form. Please help me.
class ProductsCreate(CreateView):
model = Product
form_class = ProductCreateForm
http_method_names = ['get', 'post']
def get_initial(self):
initial = super(ProductsCreate, self).get_initial()
initial['request'] = self.request
return initial
def get_context_data(self, *args, **kwargs):
ctx=super(ProductsCreate, self).get_context_data(*args, **kwargs)
ctx['special_form'] = SpeciallyPriceForm()
return ctx
def get(self, request, *args, **kwargs):
self.object = None
if kwargs.get('slug'):
category = Category.objects.filter(slug=kwargs.get('slug')).first()
self.initial.update({'category': category})
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
self.object = None
form = ProductCreateForm(request.POST, request.FILES, initial={'request': request})
special_form = SpeciallyPriceForm(request.POST)
print(special_form) #Template of form, without values.
if form.is_valid() and special_form.is_valid():
return self.form_valid(form, special_form)
else:
return self.form_invalid(form, special_form)
def form_valid(self, form, special_form):
product = form.save(commit=False)
product.user = self.request.user
product.save()
special = special_form.save(commit=False)
#Here I think, depending on the number of list items, to cycle through it and create the corresponding number of `special` records associated with this` product`. Is the logic correct?
special.product = product
special.save()
for spec_price in special_form.cleaned_data.get('adittional_specially_price'):
print(spec_price)
special.adittional_specially_price = spec_price
for spec_numb in special_form.cleaned_data.get('adittional_specially_number'):
print(spec_numb)
special.adittional_specially_number = spec_numb
forms
class SpeciallyPriceForm(forms.ModelForm):
class Meta:
model = SpeciallyPrice
fields = ['adittional_specially_price', 'adittional_specially_number']
def clean(self):
cleaned_data = super(SpeciallyPriceForm, self).clean()
cd_adittional_specially_price = cleaned_data.get('adittional_specially_price')
print(cd_adittional_specially_price) #None
cd_adittional_specially_number = cleaned_data.get('adittional_specially_number')
print(cd_adittional_specially_number) #None
template + js
<html><body>
Special price from {{ special_form.adittional_specially_price }} kg {{ special_form.adittional_specially_number }} usd
<script>
(function(){
var copy = document.querySelector('.field.inline.specially').cloneNode(true);
document.querySelector('html').addEventListener('input', function(e){
if(e.target.classList.contains('event') && e.target.tagName == 'INPUT'){
var error = 0;
for(var evt of document.querySelectorAll('.field.inline.specially input.event')){
evt.value = evt.value.replace(/[^\d]/,'');
if(!evt.value || +evt.value < 1) error++;
}
if(!error){
var last = document.querySelectorAll('.field.inline.specially');
last[last.length-1].insertAdjacentHTML('afterEnd', copy.outerHTML);
}
}
});
})();
</script>
</body></html>
This form i get in views, when print form for checking
<label for="specially" class="subhead">Special price from</label>
<span class="id_specially_price"><input type="text" name="adittional_specially_price" style="width: 165px" class="event" id="id_adittional_specially_price"></span>
<span>kg</span>
<span class="id_specially_number"><input type="text" name="adittional_specially_number" style="width: 100px" class="event" id="id_adittional_specially_number"></span>
<span>usd</span>
I looked in the views - the form is rendered there, but only with one field, and not with everything. And the form is empty .. How to solve this issue? Maybe Ajax should be connected and it somehow process the request? Or is there a more Django's option?
Answering this bit of comments: "And really come the last two empty created with JS fields. How to make all fields come, tell me, please?"
To save an inline formset in a CBV:
def form_valid(self, form):
context = self.get_context_data()
inline_form = context['inline_form']
if inline_form.is_valid():
self.object = form.save()
inline_form.instance = self.object
inline_form.save()
Clearly you'll have to use the right name for inline_form within the context.

Dynamically update Django form field options using Ajax to GET new queryset

I'm new to coding and django and I'm struggling to find the solution to the following problem having reviewed the answers I've found.
Im creating a search form with multiple fields. When the user selects the first field category (and before hitting search) I would like to dynamically change the queryset for the second field sub_category such that only related values are shown.
I have models.py as follows:
class Product(models.Model):
category = models.ForeignKey("Category")
sub_category = models.ForeignKey("SubCategory")
class Category(models.Model):
name = models.CharField(max_length=256)
class SubCategory(models.Model):
category = models.ForeignKey("Category")
name = models.CharField(max_length=256)
And my forms.py includes:
class BasicSearchForm(forms.Form):
category = forms.ModelChoiceField(
label='Category',
queryset=Category.objects.all(),
to_field_name="name",
empty_label=None,
initial="Red")
sub_category = forms.ModelMultipleChoiceField(
required=False,
label='Type',
queryset= SubCategory.objects.all(),
to_field_name="name",
widget=forms.Select)
And my views.py includes:
def search(request):
if request.method == 'POST':
form = BasicSearchForm(request.POST)
if form.is_valid():
category = form.cleaned_data['category']
sub_category = form.cleaned_data['sub_category']
return render(request, 'myapp/search.html', {'form': form})
else:
form = BasicSearchForm()
return render(request, 'myapp/search.html', {'form': form})
And finally the search.html includes:
<form class="search-form" role="search" action="/search/" method="get">
{{ form }}
<input type="submit" value="Search" />
</form>
I've played around with a few answers but nothing seems to work. I'd really appreciate some help. Thanks in advance!
Update:
Thanks for the feedback. As a result I updated the following:
In my urls.py:
urlpatterns = [
url(r'^ajax/update_subcategories/$', views.update_subcategories, name='update_subcategories'),
And in my views.py:
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(sub_category, safe=False)
And I have this in my myapp/search.html:
{% block javascript %}
<script>
$("#id_category").change(function () {
var category = $(this).val();
$.ajax({
url: '{% url "myapp:update_subcategories" %}',
data: {
'category': category,
},
success: function (response) {
var new_options = response;
alert(new_options[0].name); // works
$('#id_sub_category').empty();
$.each(new_options, function(key, value) {
$('#id_sub_category')
.append($('<option>', { value : key })
.text(value.name));
});
}
});
</script>
{% endblock %}
Update: The sub_category options were showing as [object Object] until I changed value to value.name and it looks like it's working. I'll test it out and close unless there are any comments.
Update: Im still having an issue with the browser back button. When a user clicks back the dropdown values have changed back to the original queryset rather than the updated version.
You can't do this from Django views side, ie, backend. You could try an ajax request for implementing this kind of requests, by sending a GET request to the server for populating the drop-down or whatever you are into.
For a simple example, you could refer
here
How do I POST with jQuery/Ajax in Django?
EDIT
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(dict(sub_category=sub_category))
Then in ajax response you could grab it like response.data.sub_category
Use ajax to send the category and retrieve subcategory elements.
For the category, send it via get request, and using the orm return the subcategories in a json format which you can show using jQuery.

compare two results (of many) from api data with django/python

I'm learning django/python/css/etc... and while doing this, I've decided to build an app for my website that can pull simple movie data from TMDb. What I'm having trouble with is figuring out a way to add a way for the user to select two different movies, and once selected, see the differences between them (run time, budget, etc).
I've got grabbing the data from the API covered in that doing a search for a movie on my site returns expected results. But now I'm having a really tough time trying to figure out how to select 1 item from the results to "save" it, search again, select the second movie, and have the comparison show up.
I know it's pretty vague, but any help getting me pointed in the right direction would be greatly appreciated!
here's what I'm doing so far with the code:
views.py:
from django.shortcuts import render
from django.conf import settings
from .forms import MovieSearch
import tmdbsimple as tmdb
tmdb.API_KEY = settings.TMDB_API_KEY
def search_movie(request):
"""
Search movie title and return 5 pages of results
"""
parsed_data = {'results': []}
if request.method == 'POST':
form = MovieSearch(request.POST)
if form.is_valid():
search = tmdb.Search()
query = form.cleaned_data['moviename']
response = search.movie(query=query)
for movie in response['results']:
parsed_data['results'].append(
{
'title': movie['title'],
'id': movie['id'],
'poster_path': movie['poster_path'],
'release_date': movie['release_date'][:-6],
'popularity': movie['popularity'],
'overview': movie['overview']
}
)
for i in range(2, 5 + 1):
response = search.movie(query=query, page=i)
for movie in response['results']:
parsed_data['results'].append(
{
'title': movie['title'],
'id': movie['id'],
'poster_path': movie['poster_path'],
'release_date': movie['release_date'][:-6],
'popularity': movie['popularity'],
'overview': movie['overview']
}
)
context = {
"form": form,
"parsed_data": parsed_data
}
return render(request, './moviecompare/movies.html', context)
else:
form = MovieSearch()
else:
form = MovieSearch()
return render(request, './moviecompare/compare.html', {"form": form})
def get_movie(request, movid):
"""
from search/movie results, get details by movie id (movid)
"""
movie = tmdb.Movies(movid)
response = movie.info()
context = {
'response': response
}
return render(request, './moviecompare/detail.html', context)
movies.html:
{% extends 'moviecompare/compare.html' %}
{% block movies_returned %}
<div class="wrap">
<div class="compare-gallery">
{% for key in parsed_data.results|dictsortreversed:'release_date' %}
{% if key.poster_path and key.release_date and key.title and key.overview %}
<div class="gallery-item">
<img src="http://image.tmdb.org/t/p/w185/{{ key.poster_path }}">
<div class="gallery-text">
<div class="gallery-date"><h5><span><i class="material-icons">date_range</i></span> {{ key.release_date }}</h5></div>
<div class="gallery-title"><h3>{{ key.title }}</h3></div>
<div class="gallery-overview">{{ key.overview|truncatechars:80 }}</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endblock %}
and I've got a simple detail.html that doesn't do anything just yet, but it's just for showing detail results for a single movie, so not important to get into yet as it'll just be styling.
I'd like for each result in the gallery to have a link to a details page for the movie(done), and also a select box (or similar) to select it as one of the comparison movies.
If I can just get some help on how to select two different movies from the search results, and compare those, I think I could work out a way to do the same with two separate movie searches. Thanks for any help!
edit: here's what I have so far on pythonanywhere -
Assuming you want to be able to add movies from different searches (e.g. search for rambo, add rambo, search for south park, add south park), you can probably do something like:
#require_POST
def save_compare(request, pk):
""" Async endpoint to add movies to comparison list """
movies = request.session.get('comparing_moviews', [])
movies.append(movies)
# if you only need the data the API served originally, you could also save that to a data-base when you read it from the API and use it here
request.session['comparing_movies'] = movies
return JsonResponse("")
def show_comparison(request):
""" show differing stats like "1 hour 3 mins longer """
mov1, mov2 = request.session.get('comparing_movies') # need to handle wrong number of selected
# not sure from the description how to look up the movies by id
mov1 = tmdb.Search().search(pk=mov1)
mov2 = tmdb.Search().search(pk=mov2)
return render_to_response("comparison.html", context={
"mov1": mov1, "mov2": mov2,
"length_diff": mov1['length'] - mov2['length']})
to give you the comparison stats you want. You'll also need session middleware installed.
From the search results page, you'll add a button that triggers an async js post to save_compare which starts accumulating a comparison list. When they're done, they can click "compare" or something on that list, which will render "comparison.html" with the two movies and the diff however you like it.
As an example of some calculation for your "comparison.html", you can grab the hours and minutes from the length_diff and render them as "X hours and y minutes longer."

How to use the value of <select> in with django function?

So I want to make a function with if in my html using django.
My function looks like :
function try() {document.getElementById("demo").inerHTML = '{%for x in xl%}'+
'{%if x.name == document.getElementById("select1").value%}'+
'<button type="button">hello</button>'+
'{%endif%}{%endfor%}'}
And the relevant html is :
<select id="select1">
<option value = "tiger"></option>
<option value = "bear"></option>
<option value = "eagle"></option>
</select>
<p id="demo"></p>
<script>try();</script>
So just to make it clear :
'xl' is a list, the items in this list have a name variable.
I want my web to show in the "demo" paragraph 'hello' buttons for each time that the selcted animal is the name of item in the list.
When trying to run this it says : " Could not parse the remainder: '("select1").value;' from 'document.getElementById("select1").value;' "
Please help me find what`s wrong and how to fix it, Thanks.
Why not just use a Django form, and then get the selected option value with JavaScript? Example code has not been tested. You still need an app within your Django project, settings, templating, routing, etc.
# forms.py
class AnimalForm(forms.Form):
TIGER = 'Tiger';
BEAR = 'Bear'
EAGLE = 'Eagle'
ANIMAL_CHOICES = (
(TIGER, TIGER,),
(BEAR, BEAR,),
(EAGLE, EAGLE,),
)
animal = forms.CharField(max_length=20, choices=ANIMAL_CHOICES,
default=TIGER)
def save(self):
return NotImplementedError
# implement
# views.py
from django.shortcuts import render
from .forms import AnimalForm
def choose_animal(request):
form = AnimalForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
form.save()
# do something, like redirect to a success page, etc
return render(request, 'animal_form.html',
{'form': form})
#animal_form.html
<form action="." method="post" enctype="application/x-www-form-urlencoded">
{{ form.animal }}
<button type="submit">Save</button>
</form>
<p id="animal-selection"></p>
<script>
// assuming jQuery is already linked for brevity
(function($) {
$(function() {
$('#id_animal').on('change', function() {
$('#animal-selection').html($(this).val());
});
});
})(jQuery);
</script>

Categories

Resources