Django - Page not fully loaded after exception in form - django

In my django project, I have a view that displays details of an event. In that view there is a link to another view that contains the form to register people to that event.
If the maximum number of participants is reached and someone still tries to register, my view throws an exception. And returns from the formular-site back to the event-site. But the event-site is not fully loaded, I think the queries are no performed.
I don't know how to write the return function.
Additionally, is there a syntax error in my exception? When I create new instances of registration in the shell, I can save as many as I want.
def validate_category_full(category_id):
cat = Category.objects.get(id=category_id)
regs = cat.registration_set.all()
if len(regs) >= cat.max_people:
raise ValidationError('Category is already full.')
def registration(request, category_id, event_id):
"""Add a new registration to a category."""
cat = Category.objects.get(id=category_id)
myevent = Event.objects.get(id=event_id)
if request.method != 'POST':
form = RegistrationForm()
else:
form = RegistrationForm(request.POST)
try:
validate_category_full(cat.id)
except(ValidationError):
return render(request, 'events/event.html',
{'myevent': myevent,
'error_message': 'Event is full.',})
else:
if form.is_valid():
reg = form.save()
return HttpResponseRedirect(reverse('events:category',
args=(cat.id,)))
context = {'form': form, 'cat': cat, 'myevent': myevent}
return render(request, 'events/registration.html', context)`

Mostly a wild guess since I don't know what you mean by "the event-site is not fully loaded" (and assuming "event-site" and "formular-site" really mean "event page" and "formular page"):
First: you shouldn't render the template from another view but redirect to that view instead. If you want to display a message to the user (the "event is full" message in this case) when he gets redirected back to the other view, use the messages framework:
from django.contrib import messages
def registration(request, category_id, event_id):
# snip a lot of code
try:
validate_category_full(cat.id)
except(ValidationError):
messages.error(request, 'Event is full.')
return redirect(your_event_view_name, whatever_args_it_takes)
A few notes why we're at it:
=> Use queryset.count() instead of len(queryset.all())
def validate_category_full(category_id):
cat = Category.objects.get(id=category_id)
regs = cat.registration_set.count()
if regs >= cat.max_people:
raise ValidationError('Category is already full.')
Also since you already have the category, you may want to pass it instead of the category.id

Related

Django CustomTags Using Session

I'm trying to use Django to output an HTML page based on whether the session is set or not.
when I submit my Django Form (via my view) I set the session like this:
def index(request):
users = Users.objects.all()
totalUsers = len(users)
form = CreateUserForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
form = CreateUserForm()
context = {
"form": form,
'users': users,
"totalUsers": totalUsers,
}
request.session.set_expiry(300)
request.session['loggedIn'] = True
return render(request, 'SmartCity/index.html', context)
I know this is successful because I can see the value set in the DB.
In my CustomTags.py file, I want to more or less check the session variable "loggedIn" is set, and if it is, return one thing, otherwise, return something else. This is how I thought to achieve it, but it's not working:
from Django import template
register = template.Library()
#register.inclusion_tag('SmartCity/index.html', takes_context=True)
def hello_world(context):
request = context['request']
loggedInStatus = request.session.get('logged_in', 'False')
if loggedInStatus == True:
return "Hello world"
The error I receive is:
https://preview.ibb.co/dqAe8k/2017_09_19_18_06_57.png
I could totally be on the wrong track... I would appreciate any advice you might be able to give a Django beginner :)

Can't figure out why form.has_changed() is always true?

I'm trying to learn Django and have come up with a situation I can't figure out. I have the following code:
def contact_add(request):
if request.method == 'POST':
form = ContactManageForm(request.POST)
if form.is_valid():
if form.has_changed(): # <-- ALWAYS RETURNS TRUE!
form.clean()
...
elif 'id' in request.GET: # Request to show an existing contact
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(person_static = request.GET['id'],
current_record_fg = True)
form = ContactManageForm(new_contact_dynamic.__dict__, initial=new_contact_dynamic.__dict__)
else: # This must be to add a new contact
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
So, if I'm sent an ID number, I read a record and display it on the screen. My template gives the user a 'submit changes' button. My problem, as noted above, is that Django always shows that the form has changed, even if the user hasn't changed any data on the screen (i.e. he just hit the submit changes button without changing anything).
So, am I doing something obviously wrong in my code that's creating this situation? Am I misinterpreting how the form.has_changed() method works?
It's my assumption that when I use the initial=parameter after a GET request, Django is storing that data somewhere and knows the context when the user then hits the 'submit data' button, is this wrong?
Yes you need to initialize your Form with initial data.
In your view the GET and POST requests have no common context. You may want to use sessions for that.
But in this case, it is not necessary. You can retrieve the instance on each request:
def contact_add(request):
if 'id' in request.GET:
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(
person_static = request.GET['id'],
current_record_fg = True
)
if request.method == 'POST':
form = ContactManageForm(request.POST, initial=new_contact_dynamic.__dict__)
...
else: # Show an existing contact
form = ContactManageForm(initial=new_contact_dynamic.__dict__)
else:
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})

int() argument must be a string or a number, not 'SimpleLazyObject'

I got, following Error messages,
TypeError at /save/ int() argument must be a string or a number, not
'SimpleLazyObject'
While executing following form.
views.py
def bookmark_save_page(request):
if request.method == 'POST':
form = BookmarkSaveForm(request.POST)
if form.is_valid():
# create or get link
link, dummy = Link.objects.get_or_create(
url = form.cleaned_data['url']
)
# create or get bookmark
bookmark, created = Bookmark.objects.get_or_create(
user=request.user,
link=link
)
# update bookmarks title
bookmarks.title = form.cleaned_data['title']
# if the bookmark is being updated, clear old tag list.
if not created:
bookmark.tag_set.clear()
# create new tag list
tag_names = form.cleaned_data['tags'].split()
for tag_name in tag_names:
tag, dummy = Tag.objects.get_or_create(name=tag_name)
bookmark.tag_set.add(tag)
# save bookmark to database.
bookmark.save()
return HttpResponseRedirect(
'/user/%s/' % request.user.username
)
else:
form = BookmarkSaveForm()
variables = RequestContext(request, {
'form': form
})
return render_to_response('bookmark_save.html', variables)
I thought I got error because I passed link at
bookmark, created = Bookmark.objects.get_or_create(
user=request.user,
link=link
)
But I can not figure out how to fix it.
How can I fix it?
The likely cause is that you're setting user = request.user, where request.user is not a real User object, but a SimpleLazyObject instance. See django: Purpose of django.utils.functional.SimpleLazyObject? for more details, but using request.user.id should fix your issue.
You have to login when running this piece of code on localhost. Otherwise the request.user will be a SimpleLazyObject, then the errors comes out.
Here you trying to create a Bookmark object based on request.user , but request.user is a SimpleLazyObject , so we can get a more secure user object by :
from django.contrib import auth
current_user = auth.get_user(request)
and further your query should be
bookmark, created = Bookmark.objects.get_or_create(
user=current_user,
link=link
)
Most likely the user who is loading the page is not authenticated. Therefor the error is thrown. If you want to save a request.user reference to the database, you obviously have to ensure that only authenticated users are able to call the function.
In your case there are two possibilities - add the "#login_required" decorator to the function or check if the user is authenticated inside the code. Here are the snippets:
With Decorator:
from django.contrib.auth.decorators import login_required
#login_required
def bookmark_save_page(request):
if request.method == 'POST':
form = BookmarkSaveForm(request.POST)
if form.is_valid():
# Do something
OR - checking inside the code if the user is authenticated:
def bookmark_save_page(request):
if request.method == 'POST' and request.user.is_authenticated():
form = BookmarkSaveForm(request.POST)
if form.is_valid():
# Do something
Thats strange, I had the same problem and the same solution. After trying a bunch of alternatives I went back to user = request.user and it worked
if you want to set an addition request attr, based on user -> you need to use lazy Django function which is correctly handled by Field.get_prep_value
from django.utils.functional import lazy
request.impersonator = lazy(lambda: request.user.id, int)()

Django view functions used just for logic ending with a http redirect

Hy there,
i just read here that posted data can't be sent by a redirect,
and then found that people are generally not happy with using redirects. And my question is why?
My situation
I have a Django app which starts by rendering a page which gives the choice of using Registered or Anonymous usage.
def start(request):
request.session.flush()
return render_to_response('start.html')
If Registered usage is chosen a simple form is rendered to insert the registration data, and it loops to itself until form is valid after which 'auth/' takes place.
def registration(request):
if request.method == "POST":
form = RegistrationForm(request.POST)
if form.is_valid():
return HttpResponseRedirect('auth/')
else:
return render_to_response('registration.html',
{'form':form})
form = RegistrationForm()
return render_to_response('registration.html',
{'form':form})
If anonymous usage is chosen, the registration page is skipped, and is redirected to 'auth/', the same Django view function like in upper code:
return HttpResponseRedirect('auth/')
What i was hoping to achieve with auth is for it to just set the sessions, write the data to a database and then redirect the next page which is identical to Registered and Anonymous users (only the sessions would differ).
def auth(request):
ipdb.set_trace()
if request.method == "POST":
request.session['user_type'] = 'REG'
request.session['email'] = request.POST['email']
request.session['first_name'] = request.POST['first_name']
RegisteredUser.objects.create(first_name = request.POST['first_name'],
email = request.POST['email'],
company = request.POST['company'])
else:
request.session['user_type'] = 'ANONIM'
return HttpResponseRedirect('next_page')
Of course, when the debugger starts, request.method always returns GET.
My reasons
There is a lots of talking about separating the view from the logic, and i don't find it very readable or loosely coupled when i have a Django view function (if I drop the 'auth/', 'next_page' would have to take it's functionality) which has to
Check for request.post
If there if the reason for it is form validation of the current page, or if it's from the previous page, in which case write that data to a base
Do additional checks to set the sessions for registered or anonymous user
which I would have to do since using separated functions just for logic that redirects instead of rendering doesn't seem as the generally accepted way.
Can someone please shed some light on things?
UPDATE WITH SOLUTION
Thanks to the answers i figured out the pattern on how to do this. The code for registration is now:
def registration(request):
if request.method == "POST":
form = RegistrationForm(request.POST)
if form.is_valid():
request.session['user_type'] = 'REG'
request.session['email'] = request.POST['email']
request.session['first_name'] = request.POST['first_name']
RegisteredUser.objects.create(first_name = request.POST['first_name'],
email = request.POST['email'],
company = request.POST['company'])
return HttpResponseRedirect('param_select/')
else:
return render_to_response('registration.html',
{'form':form},
context_instance = RequestContext(request))
form = RegistrationForm()
return render_to_response('registration.html',
{'form':form},
context_instance = RequestContext(request))
From now on, I'm using the "Loop into yourself with POST data, and then perform the logic" method.
To demystify the name, thats what the new registration(request) does.
First it renders the form to input the data
Than it is recalled once the data is submitted because of link to itself
form action="" method="post"
This is repeated until correct form data isn't submitted after which the logic is called (in this case, writing to a base and setting the sessions).
It ends with a httpRedirect to a different page leaving the registration completely handled by registration(request).
By default you can assume that user is anonymous and set request.session['user_type'] = 'ANONIM' in your start() view.
Whatever you are doing in auth() you can do it in registration() along with request.session['user_type'] = 'REG'. With this you can use the validated form to create RegisteredUser and flag errors as well if required.
And once POST processing is valid and complete it can redirect to your next_page.
If anonymous user is chosen, start will redirect to next_page rather than auth/

RequestContext on django redirect()

I'm wondering if there is any way to include the RequestContext in the django redirect function or any other context.
The thing is that I need to add a message after an object is created, but the messages framewotk requires a RequestContext to work or another context that returns the messages. How can I do to return a context?
My view:
#permission_required('spaces.add_space')
def create_space(request):
"""
Returns a SpaceForm form to fill with data to create a new space. There
is an attached EntityFormset to save the entities related to the space. Only
site administrators are allowed to create spaces.
:attributes: - space_form: empty SpaceForm instance
- entity_forms: empty EntityFormSet
:rtype: Space object, multiple entity objects.
:context: form, entityformset
"""
space_form = SpaceForm(request.POST or None, request.FILES or None)
entity_forms = EntityFormSet(request.POST or None, request.FILES or None,
queryset=Entity.objects.none())
if request.user.is_staff:
if request.method == 'POST':
space_form_uncommited = space_form.save(commit=False)
space_form_uncommited.author = request.user
if space_form.is_valid() and entity_forms.is_valid():
new_space = space_form_uncommited.save()
space = get_object_or_404(Space, name=space_form_uncommited.name)
ef_uncommited = entity_forms.save(commit=False)
for ef in ef_uncommited:
ef.space = space
ef.save()
# We add the created spaces to the user allowed spaces
request.user.profile.spaces.add(space)
# This message does not work since there's no context.
messages.info(request, 'Space %s created successfully.' % space.name)
return redirect('/spaces/' + space.url)
return render_to_response('spaces/space_add.html',
{'form': space_form,
'entityformset': entity_forms},
context_instance=RequestContext(request))
else:
return render_to_response('not_allowed.html',
context_instance=RequestContext(request))
Storing a message doesn't require a RequestContext, it's only displaying it that does. In what way doesn't your code work? Your message should be added to the database and available to be displayed after the redirect.
I had the same question and just want to clarify that it 'just works'
def first_view(request)
...
messages.info(request, "Some info message")
...
return redirect('second_view')
def second_view(request)
...
return render_to_response('template.html',{},context_instance=RequestContext(request))
-> will correctly display the message