Django: window.onpopstate unexpected behavior with Chrome 79 - django

EDIT
finally I use window.onpopstate event to prevent user go back
it works with Firefox but not with Chrome 79 (and other browsers?)
I have read many posts and official documentation that seems to say that this 'bug' was fixed after Chrome 34... but doesn't seems to be
to be honest, event reading the documentation, I do not really understand this event and the behavior so far...
How can I resolve this?
I know this topic has been already discuss but none of solutions resolve my problem.
I have 3 forms that update the same models.
I would like to manage/prevent user going back using back arrow.
I tried using window.location, window.onhashchange = function () {}, flag session variable but nothing works and I am lost
When user click on back arrow from 2nd form to go back to 1st form, I would like either the event is blocked (best), either user is redirected to home page
I notice that click on back arrow on the 2nd form do not call index view, reason why 'flag strategy' do not work
#login_required
#permission_required('unblind.can_unblind')
def index(request):
if request.method == "POST":
form = AveugleProcedureForm(request, data=request.POST or None)
if form.is_valid():
unblind = form.save()
# database treatment
return redirect('unblind:edit', pk = unblind.pk)
else:
form = AveugleProcedureForm(request)
return render(request, 'unblind/index.html', {'form': form})
#login_required
#permission_required('unblind.can_unblind')
def edit(request, pk):
if request.method == "POST":
form = AveugleForm(request, data=request.POST or None)
if form.is_valid():
# unblind = form.save()
# database treatment
unblind = Aveugle.objects.get(unb_ide = pk)
unblind.unb_pro = form.cleaned_data['unb_pro']
unblind.unb_nom_dem = form.cleaned_data['unb_nom_dem']
unblind.unb_nom_rea = form.cleaned_data['unb_nom_rea']
unblind.unb_dat = form.cleaned_data['unb_dat']
unblind.pat = form.cleaned_data['pat']
unblind.unb_num = form.cleaned_data['unb_num']
unblind.unb_mot = form.cleaned_data['unb_mot']
unblind.save()
# contrôler le couple numéro patient/boite de médicament
# récupération du traitement
treatment = Medicament.objects.get(med_num = unblind.unb_num).med_dru
# envoie d'un email de confirmation
if treatment == 1:
unblind_treatment = _('ASPIRIN')
else:
unblind_treatment = _('PLACEBO')
email(unblind.pat,unblind.unb_num,unblind_treatment,unblind.unb_mot)
return redirect('unblind:result', pk = unblind.pk)
else:
form = AveugleForm(request)
return render(request, 'unblind/edit.html', {'form': form,'pk': pk})
#login_required
#permission_required('unblind.can_unblind')
def result(request, pk):
# Récupération de l'enregistrement
unblind = Aveugle.objects.get(unb_ide = pk)
treatment = Medicament.objects.get(med_num = unblind.unb_num).med_dru
if request.method == "POST":
form = AveugleResultForm(request, data=request.POST or None)
if form.is_valid():
# database treatment
unblind.unb_rec = form.cleaned_data['unb_rec']
# unblind.unb_com = form.cleaned_data['unb_com']
unblind.unb_log_dat = datetime.now()
unblind.unb_log = request.user.username
unblind.unb_log_sit = request.session['selected_site']
unblind.save()
return redirect('home')
else:
form = AveugleResultForm(request)
return render(request, 'unblind/result.html', {'form': form,'unblind':unblind,'treatment':treatment})

Related

Get Kwargs through an intermediate page

I have a model (grade) inside another model (homework) which is inside another model (activity) and when I submit the grade of a homework and try to get back to activty I loose the id of the activity. How do I get the right kwargs to get back to activity after submiting a grade? Or any other solution you may think about. Thanks in advance.
Views.py
def grade_homework(request, id):
if not request.user.is_authenticated:
return render(request, "auctions/login.html")
try:
activity = Activity.objects.get(id=id)
except Activity.DoesNotExist:
activity = None
try:
hw_upload = Hw_upload.objects.get(id=id)
except Hw_upload.DoesNotExist:
hw_upload = None
if request.method == 'POST':
form = Hw_gradeForm(request.POST, request.FILES or None)
if form.is_valid():
hw_grade = form.save(commit=False)
hw_grade.grader = request.user
hw_grade.hw_upload = Hw_upload.objects.get(id=id)
hw_grade.save()
url = reverse('activity', kwargs={'id': id})
return HttpResponseRedirect(url)
Urls.py
path("auction/course/module/activity/<str:id>", views.activity, name="activity"),
path("grade_hw/<int:id>", views.grade_homework, name="grade_hw"),

Django post-form cannot validate when using Form with additional inputs

I have a form containing af MultipleChoiceField where the choices are created dynamic based on the given user
class UpdateForm(forms.Form):
def __init__(self,names,*args,**kwargs):
super(UpdateForm,self).__init__(*args,**kwargs)
self.fields["list_names"] = forms.MultipleChoiceField(choices = zip(names,names),widget=forms.CheckboxSelectMultiple,label="Pick some names")
add_new = forms.BooleanField(initial=True, label="Add new names?",required=False)
delete_missing = forms.BooleanField(label = "Delete names?",required=False)
and it works fine as GET-request, the issues arrives with the post-request:
My view is the following:
def update(request):
user = request.user
list_names = MyModel.objects.filter(user=user).all().values_list("nick_name",flat=True).distinct()
form = UpdateWishlistForm(names =list_names)
if request.method == "POST":
post_form = UpdateForm(request.POST)
if post_form.is_valid():
list_names = post_form.cleaned_data["list_names"]
add_new = post_form.cleaned_data["add_new"]
delete_missing = post_form.cleaned_data["delete_missing"]
messages.success(request, "Success")
context = {
"form":form,
}
redirect("home")
else:
#invalid post_form
messages.error(request, "Error")
context = {
"form":form,
}
return render(request, "discounttracker/update.html")
else: #Get request
context = {
"form":form,
}
return render(request, "myapp/update.html",context=context)
The post_form = UpdateForm(request.POST) does not validate and the post_form.errors is empty.
It does contain data though (before calling post_form.is_valid())
print(post_form)
# UpdateForm: <UpdateForm bound=False, valid=Unknown, fields=(add_new;delete_missing;list_names)>
print(request.POST.dict())
#<QueryDict: {'csrfmiddlewaretoken': ['...'], 'add_new': ['on'], 'list_names': ['test_name_1']}>
but I notice it is not bound, thus not validating. But I cannot understand why it's not "binding" when parsing request.POST?
In the POST request, you need to pass the names as well, so:
list_names = MyModel.objects.filter(user=user).values_list("nick_name",flat=True).distinct()
form = UpdateWishlistForm(names=list_names)
if request.method == 'POST':
post_form = UpdateForm(names=list_names, data=request.POST)
# …
# …
But I would advise to work with a ModelMultipleChoiceField [Django-doc] and thus pass a queryset. Since the nick names apparently can contain duplicates, it might be better to make a Nickname model, and use ForeignKeys to that model.

Django returning None instead of a HTTP response

OK im probably doing this all wrong!
I am trying to run a function in a view which calls another view.
This seems to pass my request into the next function as a POST method before loading the form from the second function.
my views.py
''' This section of hte code seems to function correctly '''
#login_required()
def joinLeague(request):
if request.method == 'POST':
league = JoinLeagueQueue(user=request.user)
form = JoinLeagueForm(instance=league, data=request.POST)
if form.is_valid():
form.save()
context = int(league.id) # displays id of model, JoinLeagueQueue
return HttpResponseRedirect(confirmLeague(request, context))
else:
form = JoinLeagueForm()
context = {'form':form}
return render(request, 'userteams/joinleagueform.html', context)
This section of the views file is not working correctly.
it seems to run the POST request without displaying the GET request with the form first.
#login_required()
def confirmLeague(request, league):
# gets ID of application to join league
joinleagueid=JoinLeagueQueue.objects.get(id=league)
pin = joinleagueid.pin # gets pin from application
user = joinleagueid.user # get user from application
leagueinquestion=League.objects.get(leaguePin=pin) # gets the league record for applied league
manager=leagueinquestion.leagueManager # Gets the league manager for league applied for
leaguename=leagueinquestion.leagueName # Gets the league name for league applied for
if request.method == 'POST':
if 'accept' in request.POST:
LeaguesJoinedTo.objects.create(
leaguePin = pin,
playerjoined = user,
)
return redirect('punterDashboard')# user homepage
else:
print("Error in POST request")
else:
context = {'leaguename':leaguename, 'pin':pin, 'manager':manager}
return render(request, 'userteams/confirmleague.html', context)
I now get an error saying Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/userteams/None
Using the URLconf defined in fanfoo_proj.urls, Django tried these URL patterns, in this order:
... im skipping a list of the patterns.
10. userteams/ confirmLeague [name='confirmLeague']
Ok i think the better way would be a HttpRedirect to the second view:
return confirmLeague(request, context)
should change to something like:
return redirect(confirmLeague,args=league)
django doc to urlresolvers: https://docs.djangoproject.com/en/3.0/topics/http/shortcuts/#redirect
def joinLeague(request):
if request.method == 'POST':
league = JoinLeagueQueue(user=request.user)
form = JoinLeagueForm(instance=league, data=request.POST)
if form.is_valid():
form.save()
context = league.id
return HttpResponseRedirect( reverse("your_confirmLeague_url",kwargs={'league':context}) )
else:
form = JoinLeagueForm()
context = {'form':form}
return render(request, 'userteams/joinleagueform.html', context)
def confirmLeague(request, league):
league = get_object_or_404(JoinLeagueQueue, pk=league)
pin = league.pin
if request.method == 'POST':
if 'accept' in request.POST: # This refers to the action from my form which is waiting for a button press in a html form.
LeaguesJoinedTo.objects.create(
leaguePin = pin,
playerjoined = request.user.id,
)
return redirect('punterDashboard')
else:
context = {'league':league}
return render(request, 'userteams/confirmleague.html', context)

Create, get and edit user information in same form and template

I'm trying to retrieve data from user. The form where i want to show the user information is also the same that i use to update this information.
Update3
After some updates I make this work and this is my code. If somenone have a better way to do this can share it :)
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class informacionFacturacion(models.Model):
usuario = models.ForeignKey(User, on_delete=models.CASCADE)
apellidos = models.CharField(max_length=100)
nombres = models.CharField(max_length=100)
[More fields...]
def __str__(self):
self.apellidos
forms.py
from .models import informacionFacturacion
#Create your forms here.
class informacionFacturacionForm(ModelForm):
class Meta:
model = informacionFacturacion
fields = [
"usuario",
"apellidos",
"nombres",
[More fields...]
]
views.py
#login_required
def datosPersonales(request):
#Filter query by user ID
query = informacionFacturacion.objects.filter(usuario=request.user)
form = informacionFacturacionForm()
#If query has content, edit record, else, create a new record
if query:
if request.method == "POST":
form = informacionFacturacionForm(request.POST or None, instance=query[0])
if form.is_valid():
edit_content = form.save()
edit_content.save()
else:
if request.method == "POST":
form = informacionFacturacionForm(request.POST)
if form.is_valid():
create_content = form.save(commit=False)
create_content.save()
return HttpResponseRedirect(reverse('datosPersonales'))
context = {
"titulo": "Datos personales | Co.",
"body_class": "class= sidebar_main_open sidebar_main_swipe",
"form": form,
"infoFacturacion": query,
}
template = "micuenta/datosPersonales.html"
return render(request, template, context)
Thanks for the support.
At first glance, it seems that the informacionFacturacion table is not being populated. Have you checked that the instance.save() is reached? (in other words, that the form is valid)
Second, in the template you want to use the informacionFacturacion object as the form elements, and you are handling them separately. Do:
if request.POST:
form = informacionFacturacionForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
else:
# handle here the form error's, maybe report it in the template
else:
query = informacionFacturacion.objects.filter(usuario=request.user)
form = informacionFacturacionForm(instance=query[0])
and render the form parameter insead of infoFacturacion:
{{ form.as_p }}
finally, make sure that your template form id's matches the form element names, otherwise the form won't be filled.
UPDATE
Based on your edit, now the error is in this line:
form = informacionFacturacionForm(request.POST, instance=query_id)
query_id is an int, and it is expecting a model. Change the following line:
query_id = informacionFacturacion.objects.get(usuario=request.user).id
to
query = informacionFacturacion.objects.get(usuario=request.user)
and the faulty line to:
form = informacionFacturacionForm(request.POST, instance=query)
that should work for now, although code can be simplified a lot.
EDIT 2
Here is what I assume you want:
#login_required
def datosPersonales(request):
query = informacionFacturacion.objects.filter(usuario=request.user)
if request.method == "POST": # This will handle the template form's POST
form = informacionFacturacionForm(request.POST)
if form.is_valid():
asd = form.save(commit=False)
asd.save()
# Here you may want to redirect to somewhere else
# Im not sure here, I guess that you want to handle the GET method if
# there is no form in the request. Post your template form to see what
# is happening.
else:
form = informacionFacturacionForm(instance=query)
# you dont need to save it, it is already in DB
context = {
"titulo": "Datos personales | Co.",
"body_class": "class= sidebar_main_open sidebar_main_swipe",
# I think here is your main issue, you are handling a form object
# AND a infoFacturacion object. You need to use just the
# form object in the template and render it accordingly.
"form": form,
"infoFacturacion": query,
}
template = "micuenta/datosPersonales.html"
return render(request, template, context)
Well, I was with the same problem on my sytem, so I made this solution, maybe it works to you! =D
I'm changing the value of the submit button and using the same form:
<button type="submit" id="submitButton" name="button" value="">Save</button>
If is a new task, I change the value of the button with JQuery:
$('#submitButton').val('new');
And if is an edition, I change the value again:
$('#submitButton').val('edit');
On my views.py, I check if is an edit or a new save by the value of the button:
def index(request):
tasks = Task.object.filter()
context = {
'tasks': tasks
}
if request.method == 'POST':
form = NewTask(request.POST or None)
if request.POST['button'] == 'new':
if form.is_valid():
context['is_valid'] = True
form.save()
form = NewTask()
else:
context['is_valid'] = False
if request.POST['button'] == 'edit':
instance = Task.object.filter(pk=request.POST['id']).first()
form = NewTask(request.POST, instance=instance)
if form.is_valid():
context['is_valid'] = True
form.save()
else:
context['is_valid'] = False
else:
form = NewTask()
context['form'] = form
return render(request, 'index.html', context)

submitted django form properly contains user data but shows as unbound and invalid

I have a form with a single ChoiceField (widget radioselect) whose choices are dynamically generated and render perfectly. When the form is submitted the POST data shows the correct value (1-5) for the choice selected (in live debug and in the browser debug info) but for whatever reason the form is considered unbound and invalid. Here's the form:
class ReValidateStudentForm(forms.Form):
bs_answer = forms.ChoiceField(widget=RadioSelect, label='Answer', required=True)
def __init__(self, possible_answers, *args, **kwargs):
super(ReValidateStudentForm, self).__init__(*args, **kwargs)
self.fields['bs_answer'].choices = possible_answers
FYI, "possible_answers" is a list of tuples passed to the form that is presenting perfectly as radio select choices when the fresh form is rendered. request.method does equal POST.
Any advice?
EDIT
Here's the form view code:
#login_required(login_url='/login/')
def ask_identity_questions(request):
si = StudentInfo.objects.get(student=request.user)
qnum = si.bs_question_number
if request.method == 'POST':
revalidateform = ReValidateStudentForm(request.POST)
try:
va = VerificationAnswers.objects.get(student=request.user, question_num=qnum)
except VerificationAnswers.DoesNotExist:
return render(request, "informational.html", {'message': 'User--question does not exist.'})
if revalidateform.is_valid():
va.student = request.user
va.question_num = si.bs_question_number
va.answer_choice = revalidateform.cleaned_data['bs_answer']
va.question_answered_time = timezone.now()
si.bs_question_number += 1
si.save()
return render(request, "informational.html", {'message': 'Thank you! Please click here to continue working through the course material.'})
else:
return render(request, 'questions.html', {'form': revalidateform, 'question_num': qnum, 'question': va.question_text})
else:
if qnum == 6:
return render(request, "informational.html", {'message': 'You have already answered all validation questions.'})
elif si.bs_qset_id == '':
return render(request, "informational.html", {'message': 'There is a problem with your identity validation information. Please contact customer support.'})
else:
# initialize blockscore account
client = blockscore.Client({'api_key':'sk_live_149dthis3ddiseb2f5notbebforebreal'})
si = StudentInfo.objects.get(student=request.user)
# set question ask time
# retrieve the user's question set using question set id stored in StudentInfo
# retrieve specific question from set based on value in StudentInfo
qset = client.question_sets.retrieve(si.bs_qset_id)
qset = qset.body
question = qset["questions"][qnum - 1].get("question")
# add verification question and mark time asked
try:
va = VerificationAnswers.objects.get(student=request.user, question_num=qnum)
except VerificationAnswers.DoesNotExist:
va = VerificationAnswers()
va.student = request.user
va.question_num = qnum
va.question_text = question
va.question_asked_time = timezone.now()
va.save()
# create answer list
possible_answers = []
# fill answer list with tuples of answer ids + answers
for answer in qset["questions"][qnum - 1].get("answers"):
possible_answers.append((answer.get("id"), answer.get("answer")))
revalidateform = ReValidateStudentForm(possible_answers)
return render(request, 'questions.html', {'form': revalidateform, 'question_num': qnum, 'question': question})