How to pass a set of objects in between two views? - python

I'm building a quiz app in which I'm storing questions in the database by creating a model class. I am retrieving a random question set for each user from the database and then rendering them on an HTML page. The problem is after logging a user in, a random set of questions appears but that random set is lost after refreshing the page.
How do I solve this
One thing that I thought was retrieving the object set in another view....say after logging a user in and passing it as a dictionary to another view.
But I can't find the syntax or any function (if it exists). Please help.
I'm using django 3.1 and MySQL as my database.
My views.py looks like this:
from django.shortcuts import render, redirect
from .models import *
from .forms import UserForm
from django.contrib.auth.forms import AuthenticationForm
import random
from django.contrib.auth import login, logout, authenticate
# Create your views here.
def home(request):
return render(request, 'testapp/home.html')
def loginuser(request):
#form = UserForm()
if request.method == 'GET':
form = AuthenticationForm()
return render(request, 'testapp/login.html', {'form':form})
else:
user = authenticate(request, username=request.POST['username'], password=request.POST['password'])
if user is None:
return render(request, 'testapp/login.html', {'form':AuthenticationForm(), 'error':'Username or password incorrect'})
else:
login(request, user)
return redirect('paper')
def paper(request):
#objects = Questions.objects.all()
"""count = Questions.objects.all().count()
slice = random.random() * (count-5)
objects = Questions.objects.all()[slice:slice+5]"""
#objects = {{ objects }}
objects = Questions.objects.all().order_by('?')[:5]
return render(request, 'testapp/paper.html', {'objects':objects})

There isn't really a direct way to pass values between views such as args or kwargs. I would recommend using the request session to store values and access them again.
def paper(request):
question_set = Questions.object.all()
question_set = question_set.order_by('?')[:5]
# Retrieve the primary keys from the 5 questions selected.
question_pks = question_set.values_list('pk', flat=True)
# Store the pks in a list on the request session.
request.session['question_ids'] = list(question_pks)
context_data = {'objects': question_set}
return render(request, 'testapp/paper.html', context_data)
def home(request):
# Get all the pks from the request session again.
question_pks = request.session['question_ids']
# Use the pks to retrieve the same question objects from the database.
question_set = Questions.objects.filter(pk__in=question_pks)
context_data = {'objects': question_set}
return render(request, 'testapp/home.html', context_data)

You can make use of request.session to store your questions ids the first time :
def paper(request):
if 'question_ids' not in request.session:
request.session['question_ids'] = list(Questions.objects.all().order_by('?').values_list('id', flat=True)[:5])
objects = Questions.objects.filter(id__in=request.session['question_ids'])
return render(request, 'testapp/paper.html', {'objects':objects})

Related

The view urlshort.views.page_redirect didn't return an HttpResponse object. It returned None instead

I'm making a url shortener with django. I have a form that has a long_url attribute. I'm trying to get the long_url and add it to a redirect view with a HttpResponseRedirect.
# Form
from .models import ShortURL
from django import forms
class CreateNewShortURL(forms.ModelForm):
class Meta:
model=ShortURL
fields = {'long_url'}
widgets = {
'long_url': forms.URLInput(attrs={'class': 'form-control'})
}
# View
def page_redirect(request, url):
if request.method == 'GET':
form = CreateNewShortURL(request.GET)
if form.is_valid():
original_website = form.cleaned_data['long_url']
return HttpResponseRedirect(original_website)
When I go to the link, it gives me The view urlshort.views.page_redirect didn't return an HttpResponse object. It returned None instead. Does anyone know why this is happening?
It is not returning because there is no valid form, you need to redirect to a form for the user to enter data first, you will then get that data from the form to perform your redirect. additionally because a user is returning data you will need to get the data from request.POST.
def page_redirect(request, url):
if request.method == 'GET':
form = CreateNewShortURL(request.POST)
if form.is_valid():
original_website = form.cleaned_data['long_url']
return HttpResponseRedirect(original_website)
return render(request,'template') # this is the form for the user to enter the url

Finding if lastname exists in database

I'm trying to write a simple IF statement on checking if a lastname in a database exists before a user hits the submit button to create a new record. Here is my code so far, I'm new to Django and Python so the help is appreciated.
I made a variable called lastname, the thought process here is when the user hits submit, it checks the database first before the commit to warn them with a popup if the lastname exists to prevent duplicate records. It would actually be really cool to have it when a person exits the field for it to run the script before they finish filling out the form to save time.
#views.py
from .models import StudentCheck
from django.shortcuts import render
from django.http import HttpResponse, Http404, HttpResponseRedirect
from forms.forms import NewStudentForm
def NewStudentFormCheckList (request):
if request.method == 'POST':
form = NewStudentForm(request.POST)
lastname = StudentCheck.lastname
if form.is_valid():
newstudent= form.save()
else:
form = NewStudentForm()
return render(request, 'forms/newstudentcheck_form.html', {'form': form})
Here is my test code to see if query is working correctly and i keep getting a error that the query set doesnt exists.
from .models import StudentCheck
from django.core.exceptions import ValidationError
from django.shortcuts import render
from django.http import HttpResponse, Http404, HttpResponseRedirect
from forms.forms import NewStudentForm
def NewStudentFormCheckList (request):
if request.method == 'POST':
form = NewStudentForm(request.POST)
lastname = StudentCheck.lastname
number_lastnames = StudentCheck.objects.get(lastname__exact=lastname)
if form.is_valid():
newstudent= form.save()
print (number_lastnames)
else:
form = NewStudentForm()
return render(request, 'forms/newstudentcheck_form.html', {'form': form})
You can get all the entries in the database which have a given value already set using the field lookup exact (see here for more informations).
In your case it'll be StudentCheck.objects.get(lastname__exact=yourvalue). This would give you a QuerySet, and if you want to know how many entries have the given last name, you have to use count() on this QuerySet to know how many entries it has.
You can use this solutions in two different places:
Directly in the view, when receiving the POST values.
In a custom validator, which would be used in the definition of the model (see here to know how to do)
I would recommend to use the second one, as it would be easier to provide custom information for the user on why it's data where not accepted.
Both of these methods requires the data to be passed to the server to be validated though. Otherwise you can define a view that would receive the lastname and return if it's present already in the database in some way (JSON for example), which would be called using Ajax when the user click on the submit button.
Edit:
As per request of OP, here's an example of the custom validator:
from django.core.exceptions import ValidationError
from .models import StudentCheck
def validate_lastname(value):
number_lastnames = StudentCheck.objects.get(lastname__exact=value)
if number_lastnames > 0:
raise ValidationError(
'%s already exists' % value,
)
Now you can use this custom validator with attribute validators, either in the definition of your model or inside the definition of your form like this: lastname = models.CharField(validators=[validate_lastname]).
Hope it helps!

'QueryDict' object has no attribute 'id'

On the 'panel' page, I have a choice field with a list of uploaded documents or 'bots' as I usually refer to them. This list only displays 'bots' that have been uploaded by the current user.
panel\forms.py
from django import forms
import os
from upload.models import Document
#### RETRIEVE LIST OF BOTS UPLOADED BY CURRENT USER ####
def get_files(user):
bots = Document.objects.filter(user=user.id)
file_list = []
for b in bots:
file_list.append((b.id,b.docfile))
return file_list
class botForm(forms.Form):
def __init__(self, user, *args, **kwargs):
super(botForm, self).__init__(*args, **kwargs)
self.fields['bot'] = forms.ChoiceField(choices=get_files(user))
This works fine and displays a list of all the users bots. The problem arises when I try to pass these values over to the 'game' page and access them here.
game\views.py
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from game.models import game
from game.forms import GameForm
from upload.models import Document
from panel.forms import botForm
import league
def RPS(request):
if request.method == 'POST': # If the request is a POST method...
if 'PanelPlay' in request.POST:
panel = botForm(request.POST)
if panel.is_valid():
print panel.cleaned_data['bot']
elif 'GamePlay' in request.POST:
form = GameForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
leagueOuput = []
leagueOutput = league.run(form.cleaned_data['bot1'],form.cleaned_data['bot2'])
newGame = game()
newGame.bot1 = leagueOutput[0]
newGame.bot2 = leagueOutput[1]
newGame.bot1wins = leagueOutput[2]
newGame.bot2wins = leagueOutput[3]
newGame.save()
return HttpResponseRedirect(reverse('game.views.RPS')) # Redirect after POST
form = GameForm() # An unbound form
results = game.objects.all() # Load messages for the list page
return render_to_response('game.html', {'results': results, 'form': form}, context_instance=RequestContext(request))
When attempting to access and validate the panel data, I get the following error.
'QueryDict' object has no attribute 'id'
Referring to this specific line.
bots = Document.objects.filter(user=user.id)
I have found and read about a number of similar issues but I can't seem to carry over their solutions to my own project.
Thanks in advance for any and all help.
When you are constructing the botForm, you're passing request.POST (a QueryDict) as the user parameter. Did you mean
panel = botForm(request.user, data=request.POST)
?
(assuming you're using django authentification).

reverse() is not working

I am trying to pass the id through reverse. But it's not working. I'm getting this error
Reverse for 'reg.views.thanks' with arguments '(20,)' and keyword arguments '{}' not found.
Here is my views.py:
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.core.urlresolvers import reverse
from reg.models import registration, registrationform
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
def registration(request):
if request.method == 'POST':
form = registrationform(request.POST)
if form.is_valid():
data = form.save()
id = data.id
return thanks(request,id)
else:
form = registrationform()
return render_to_response('registration.html', {'form' : form}, context_instance=RequestContext(request))
def thanks(request, id):
p = get_object_or_404(registration, pk=id)
return render_to_response('thanks.html', {'reg' : p})
Here is my urls.py:
from django.conf.urls import patterns, include, url
url(r'^registration/$', 'reg.views.registration'),
url(r'^thanks/$', 'reg.views.thanks'),
url(r'^$','django.views.generic.simple.direct_to_template', {'template' : 'index.html'}),
)
Here is thanks.html:
<html>
<body>
<p>Thank you for registration mr.{{reg.username}}</p>
</body>
</html>
and I'm also showing my models.py:
from django.db import models
from django.forms import ModelForm
class registration(models.Model):
username = models.CharField(max_length=100)
password = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class registrationform(ModelForm):
class Meta:
model = registration
Thanks.
from this links (django tutorial):
https://docs.djangoproject.com/en/dev/topics/http/urls/#django.core.urlresolvers.reverse
example:
def myview(request):
return HttpResponseRedirect(reverse('arch-summary', args=[1945]))
so your code goes to:
in urls.py:
url(r'^thanks/(?P<id>\d+)$', 'reg.views.thanks', name='my_thanks_url')
in your function:
return HttpResponseRedirect(reverse('my_thanks_url', args=[id]))
This line
return HttpResponseRedirect(reverse('reg.views.thanks', args=(id,)))
Is trying to construct a url to your view reg.views.thanks, with the id variable used as a parameter.
This line in urls.py
url(r'^thanks/$', 'reg.views.thanks'),
Does not have anywhere for that parameter to go.
The first thing that you need to figure out is whether you actually want to send an HTTP redirect to the browser to tell it to go to the 'thanks' page. If you really do, then you need a way to send that id in the URL. You can do it as part of the URL path itself, as #moguzalp suggests, or you can put it in the query string, like
/thanks/?id=12345
Or you can do other things, like stashing the id in the user's session, and pulling it out when they request the thanks page. That's a bit more complicated, though.
If you don't actually need to issue an HTTP redirect, then there's nothing stopping you from just calling the thanks() function from inside your view function, like this:
def registration(request):
if request.method == 'POST':
form = registrationform(request.POST)
if form.is_valid():
data = form.save()
id = data.id
return thanks(request, id)
else:
form = registrationform()
return render_to_response('registration.html', {'form' : form}, context_instance=RequestContext(request))
The URL won't change in the browser, but the correct ID will be used, and doesn't need to appear anywhere else, in the URL, the query parameters, or the session

How to let Django's generic view use a form with initial values?

I know how to set initial values to a form from the view. But how do I go about letting a generic view set initial values to a form? I can write a wrapper view for the generic view, but I still have no access to the form object instantiation.
The specific goal I'm trying to achieve is to have a user create a new object, using the create_object generic view. However, I'd like to set a field of the object 'user' to the currently logged in user, which is only accessible as request.user. How can the form be initialized to have this field?
Edit: I came across __new__. Could this call its own constructor with some default arguments?
Many thanks.
Unfortunately, you cannot achieve this behavior through Django's create_object generic view; you will have to write your own. However, this is rather simple.
To start off, you must create a new form class, like this:
from django import forms
class MyForm(forms.ModelForm):
class Meta:
model = MyModel # model has a user field
Then you would be able to create a view like this:
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required
#login_required
def create_mymodel(request):
if request.method == 'POST':
# Get data from form
form = MyForm(request.POST)
# If the form is valid, create a new object and redirect to it.
if form.is_valid():
newObject = form.save()
return HttpResponseRedirect(newObject.get_absolute_url())
else:
# Fill in the field with the current user by default
form = MyForm(initial={'user': request.user})
# Render our template
return render_to_response('path/to/template.html',
{'form': form},
context_instance=RequestContext(request))
You could do this in a generic view wrapper by dynamically constructing a form class and passing it to the generic view, but that cure is probably worse than the disease. Just write your own view, and wait eagerly for this to land.
If you want all the features of the generic view then you can just create a new generic view using the original as a template.
Eg:
def create_object_with_initial(request, model=None, template_name=None,
template_loader=loader, extra_context=None, post_save_redirect=None,
login_required=False, context_processors=None, form_class=None, initial=None):
if extra_context is None: extra_context = {}
if login_required and not request.user.is_authenticated():
return redirect_to_login(request.path)
model, form_class = get_model_and_form_class(model, form_class)
if request.method == 'POST':
form = form_class(request.POST, request.FILES)
if form.is_valid():
new_object = form.save()
msg = ugettext("The %(verbose_name)s was created successfully.") %\
{"verbose_name": model._meta.verbose_name}
messages.success(request, msg, fail_silently=True)
return redirect(post_save_redirect, new_object)
else:
print "creating", form_class, " with initial data ", initial
form = form_class(initial=initial)
# Create the template, context, response
if not template_name:
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
t = template_loader.get_template(template_name)
c = RequestContext(request, {
'form': form,
}, context_processors)
apply_extra_context(extra_context, c)
return HttpResponse(t.render(c))
This is copied from /site-packages/django/views/generic/create_update.py with only lines 3 and 21 changing to incorporate the initial data.
Then use it as you might expect:
object_info = {
'model': YourModel,
'initial': {'data' : 'Initial Value'},
'template_name': 'template.html'
}
url(r'^path/$',
login_required(create_object_with_initial),
object_info,
name='url_name'),
That should work.

Categories