Why are my ValidationErrors not rendering anymore? - python

I have a form that has to check a few things before submission to make sure the data is valid. Before I did a few changes (went from class based views to function based) everything worked fine, but when I went back to test everything I noticed that a very important part was not working properly, which is the number validation.
What used to happen before was that, if a number that was not in the DB was entered, the user would be shown an error at the top. If they entered a number that was in the DB, but it was not the right "team" then it would show an error. I was handling this in forms.py, and completely forgot about this since it was working before, and all the things I started working with were in views.py. Now, while it will not actually submit the form (so it is still going through the logic) it will not display any errors. It will just re-render the page but empty, and not submit anything unless the data entered is right. The logic that would handle this was clean_employee_number.
I'm not sure If I deleted something from my html without noticing, but I have been trying to figure out what could have cause this to stop working and I cannot figure it out. I am also not sure if it could have been due to the widget, since I made that change afterwards (it is a widget because the employee_number is tied to the "id" of the employee which is stored in another model called Salesman).
models.py
class EmployeeWorkAreaLog(TimeStampedModel, SoftDeleteModel, models.Model):
employee_number = models.ForeignKey(Salesman, on_delete=models.SET_NULL, help_text="Employee #", null=True, blank=False)
work_area = models.ForeignKey(WorkArea, on_delete=models.SET_NULL, null=True, blank=False, help_text="Work Area", related_name="work_area")
station_number = models.ForeignKey(Station, on_delete=models.SET_NULL, null=True, help_text="Station", related_name="stations", blank=True)
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'employee_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('employee_number').remote_field, site, attrs={'id':'employee_number_field'}),
}
fields = ('employee_number', 'work_area', 'station_number', 'edited_timestamp')
def clean_employee_number(self):
employee_number = self.cleaned_data.get('employee_number')
if employee_number is None:
raise forms.ValidationError("Must enter emp #")
elif employee_number.team is None:
raise forms.ValidationError("Not valid")
elif employee_number.team not in ('WF', 'WP', 'OM') or employee_number.employee_status not in 'A':
raise forms.ValidationError("Employee not valid, please contact manager")
views.py
def enter_exit_area(request):
form = WarehouseForm(request.POST or None)
enter_without_exit = None
exit_without_enter = None
if request.method == 'POST':
temp = request.POST.copy()
form = WarehouseForm(temp)
if form.is_valid():
emp_num = form.cleaned_data['employee_number']
area = form.cleaned_data['work_area']
station = form.cleaned_data['station_number']
edited_time = form.cleaned_data['edited_timestamp']
if 'enter_area' in request.POST:
new_entry = form.save()
EmployeeWorkAreaLog.objects.filter((Q(employee_number=emp_num) & Q(work_area=area) & Q(time_out__isnull=True) & Q(time_in__isnull=True)) & (Q(station_number=station) | Q(station_number__isnull=True))).update(time_in=datetime.now())
# If employee has an entry without an exit and attempts to enter a new area, mark as an exception 'N'
enters_without_exits = EmployeeWorkAreaLog.objects.filter(Q(employee_number=emp_num) & Q(time_out__isnull=True) & Q(time_exceptions="")).exclude(pk=new_entry.pk).order_by("-time_in")
if len(enters_without_exits) > 0:
enter_without_exit = enters_without_exits[0]
enters_without_exits.update(time_exceptions='N')
message = 'You have entered %(area)s' % {'area': area}
if station is not None:
message += ': %(station)s' % {'station': station}
messages.success(request, message)
elif 'leave_area' in request.POST:
# Something similar to above
form = WarehouseForm()
return render(request, "operations/enter_exit_area.html", {
'form': form,
'enter_without_exit': enter_without_exit,
'exit_without_enter': exit_without_enter,
})
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" novalidate >
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.source.errors }}
{{ form.source }}
<div>
<div>
<div>{{ form.employee_number.errors.as_text }}</div>
<label>Employee #</label>
{{ form.employee_number }}
</div>
<div>
<div>{{ form.work_area.errors.as_text }}</div>
<label>Work Area</label>
{{ form.work_area }}
</div>
<div>{{ form.station_number.errors.as_text }}</div>
<div>
<label>Station</label>
{{ form.station_number }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
<button type="submit" name="leave_area" value="Leave">Leave Area</button>
</div>
</div>
</form>

You construct a new form in case it is a POST request and the form is not valid. The workflow of your enter_exit_area should be:
def enter_exit_area(request):
enter_without_exit = None
exit_without_enter = None
if request.method == 'POST':
form = WarehouseForm(request.POST)
if form.is_valid():
# …
return redirect('some-view')
else:
form = WarehouseForm()
return render(request, "operations/enter_exit_area.html", {
'form': form,
'enter_without_exit': enter_without_exit,
'exit_without_enter': exit_without_enter,
})
Notice thus that the form = WarehouseForm() is constructed in an else block of the if request.method == 'POST'.
In case of a successful POST request, you normally make a redirect, to implement the Post/Redirect/Get pattern [wiki].

Related

I am confused while rendering my views.py "Django"

My vews.py:
if you want me to share another piece of information feel free to ask!
I just have a problem with the bidding system !!, I tried the bidding but when I add the new number into the input field and click the place bid button the page just reloads which means the function doesn't work!!
I have another problem when I try to close the bid I git this Django error
ValueError at /bidding/26
The view auctions.views.bidding didn't return an HttpResponse object.
It returned None instead.
The code:
def viewList(request, id):
# check for the watchlist
listing = Post.objects.get(id=id)
user = User.objects.get(username=request.user)
if listing.watchers.filter(id=request.user.id).exists():
is_watched = True
else:
is_watched = False
if not listing.activate:
if request.POST.get('button') == "Close":
listing.activate = True
listing.save()
else:
price = request.POST.get('bid', 0)
bids = listing.bids.all()
if user.username != listing.creator.username:
if price <= listing.price:
return render(request, 'auctions/item.html', {
"listing": listing,
'form': BidForm(),
"message": "Error! Your bid must be largest than the current bid!",
'comment_form': CommentForm(),
'comments': listing.get_comments.all(),
'is_watched': is_watched,
})
form = BidForm(request.POST)
if form.is_valid():
bid = form.save(commit=False)
bid.user = user
bid.save()
listing.bids.add(bid)
listing.bid = price
listing.save()
else:
return render(request, 'acutions/item.html', {'form'})
context = {
'listing': listing,
'comment_form': CommentForm(),
'comments': listing.get_comments.all(),
'is_watched': is_watched,
'form': BidForm()}
return render(request, 'auctions/item.html', context)
models.py
class Bid(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
bid = models.DecimalField(max_digits=10, decimal_places=2)
time = models.DateTimeField(default=timezone.now)
class Post(models.Model):
# data fields
title = models.CharField(max_length=64)
textarea = models.TextField()
# bid
price = models.FloatField(default=0)
currentBid = models.FloatField(blank=True, null=True)
imageurl = models.CharField(max_length=255, null=True, blank=True)
category = models.ForeignKey(
Category, on_delete=models.CASCADE, default="No Category Yet!", null=True, blank=True)
creator = models.ForeignKey(
User, on_delete=models.PROTECT, related_name="all_creators_listings")
watchers = models.ManyToManyField(
User, blank=True, related_name='favorite')
date = models.DateTimeField(auto_now_add=True)
# for activated the Category
activate = models.BooleanField(default=False)
bids = models.ManyToManyField(Bid, )
def __str__(self):
return f"{self.title} | {self.textarea} | {self.date.strftime('%B %d %Y')}"
item.html
<div class="card-body">
<ul class="list-group">
<div class="info">
<li class="list-group-item mb-2">Description:<br>{{listing.textarea}}</li>
<li class="list-group-item mb-2">category: {{listing.category.name}}</li>
<li class="list-group-item mb-2"><h5>Start bid: {{listing.price}}$</h5></li>
</div>
<div class="newbid">
<p>{{ message }}</p>
<form action="{% url 'viewList' listing.id %}" method="POST">
{% csrf_token %}
<div class="form-group">
<label for="bid">{{ listing.bids.count }} bid(s) so far. Your bid is the current bid</label>
</div>
<div class="form-group">
{{ form }}
</div>
<div class="form-group">
<input type="submit" name="button" class="btn btn-primary" value="Place Bid">
</div>
</form>
{% if listing.activate %}
<li><strong>Winner: </strong>{{ listing.bids.last.user.username }}</li>
{% endif %}
{% if user.username == listing.creator.username and not listing.activate %}
<form action="{% url 'viewList' listing.id %}" method="POST">
{% csrf_token %}
<button type="submit" name="button" class="btn btn-danger" value="Close">Close</button>
</form>
{% endif %}
</div>
</ul>
</div>
inside this view, I added a punch of my project requirement (comments/watchlist(bookmark)/and the last thing(that what I have a lot of problem with it) is the system of Bid) that lets users add bids on such posts and let the creator of that post the ability to close it.... please help I am sticking in this zone, I tried many times to understand!
Note I am new at Back-end Development!
There is the two problem in you code first one is hi-lighted by Husam
user = User.objects.get(username=request.user.username)
and the other one is in the return statement in the else part
render(request, 'acutions/item.html', {'form'}) instead of context object you pass the string in which is consider as set object in python and thats why you are getting None type error.
here is the refactored code :-
def viewList(request, id):
# check for the watchlist
listing = Post.objects.get(id=id)
user = User.objects.get(username=request.user.username)
form = BidForm()
is_watched = listing.watchers.filter(id=request.user.id).exists():
context = {}
if not listing.activate:
if request.POST.get('button') == "Close":
listing.activate = True
listing.save()
else:
price = request.POST.get('bid', 0)
bids = listing.bids.all()
if user.username != listing.creator.username:
if price <= listing.price:
context.update({'message':"Error! your bid must be largest than the current bid !"})
else:
form = BidForm(request.POST)
if form.is_valid():
bid = form.save(commit=False)
bid.user = user
bid.save()
listing.bids.add(bid)
listing.bid = price
listing.save()
else:
return render(request, 'acutions/item.html', {'form': form})
context.update({'listing': listing,
'comment_form': CommentForm(),
'comments': listing.get_comments.all(),
'is_watched': is_watched,
'form': form})
return render(request, 'auctions/item.html', context)
The error you face in bidding view and the view you have shared is view list, it would be better if you shared the bidding view and highlight the error line/s
Anyway I have noticed that this line has one mistake:
user = User.objects.get(username=request.user)
Which suppose to be :
user = User.objects.get(username=request.user.username)
Hope this can help you little bit

ValueError at /getGiftFromFaucet/ The view contract.views.getGiftFromFaucet didn't return an HttpResponse object. It returned None instead

I'm doing my first question on StackOverFlow about something ig going me crazy.
My project is an Auction site, and is based on django and Web3 to interact with a smart contract deployed on my Ganache.
Now I have got this error on my view :
ValueError at /getGiftFromFaucet/ The view contract.views.getGiftFromFaucet didn't return an HttpResponse object. It returned None instead.
The problem is:
If I register a customer and an address,
then I try to receive tokens from the Faucet,
If I put customer address I don't receive anything.
But if i select another address, I receive token in the first address.
I really don't understand why...
my Model:
class Customer(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
address = models.CharField(max_length=256,
blank=False,
null=False,
unique=True,
error_messages={'unique': 'This address is already registered'})
tokenBalance = models.FloatField(default=0)
dollarBalance = models.FloatField(default=0)
my Registration to the site
def registrationForm(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
address = form.cleaned_data['address']
user = User.objects.create_user(
username=username,
password=password,
)
newCustomer = Customer(
user=user,
dollarBalance=random.randrange(500, 1500),
address=address,
tokenBalance=0
)
user.save()
newCustomer.save()
user = authenticate(username=username, password=password)
login(request, user)
messages.success(request, f'''Welcome in DNote {request.user}''')
return redirect('homepage')
else:
form = RegistrationForm()
context = {'form': form}
return render(request, 'registration.html', context)
my Form:
class ReceiveTokenFromFaucet(forms.ModelForm):
class Meta:
model = Customer
fields = ['address']
My view:
def getGiftFromFaucet(request):
customer = Customer.objects.get(user=request.user)
customerAddress = customer.address
if request.method == 'POST':
form = ReceiveTokenFromFaucet(request.POST)
if form.is_valid():
form.save(commit=False)
customerAddress = form.cleaned_data['address']
if customerAddress not in alreadyRecompensed:
contract.functions.transfer(
customerAddress, 100000000000000000000
).transact({'from': faucet})
alreadyRecompensed.append(customerAddress)
customer.tokenBalance += 100000000000000000000
customer.save()
messages.success(request, 'Your Tokens Are accreditate on your Account')
return redirect('/homepage/')
if customerAddress in alreadyRecompensed:
messages.error(request, 'Already Recompensed')
return redirect('/homepage/')
else:
form = ReceiveTokenFromFaucet()
context = {'form': form, 'alreadyRecompensed': alreadyRecompensed}
return render(request, 'requireFromFaucet.html', context)
My Html:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block head_title %}{{ block.super }} ReceiveFreeTokens {% endblock head_title %}
{% block content %}
<div class="row justify-content-center mt-4">
<div class="col-4">
<h2>DNote</h2>
<h3>Receive Free Tokens</h3>
<p>Available only one time</p>
<br>
<form method="POST" enctype="multipart/form-data" style="max-width:100%">
{% csrf_token %}
{{ form|crispy }}
<br>
<input type="submit" class="btn btn-info" value="Create">
</form>
</div>
</div>
{% endblock content %}

Update a single object from a database - Django/Python

So I am trying to allow users to edit menu item prices that are in the database currently. The fields will auto-populate data into the page and users will be able to edit that data to change the price. Here is what I have. I have tried and asked many questions, but I am still lost. I have googled a lot and it helped me understand forms a bit, but I'm not able to fix it. Please let me know if you need more info.
Views.py:
def edit_menu(request):
queryset = Product.objects.all()
context = { "object_list": queryset }
if request.method == 'POST':
post=ProductModelForm(request.POST)
if request.POST.get('price') and request.POST.get('name'):
if 'name' == Product.name:
post.name= request.POST.get('name')
post.price= request.POST.get('price')
post.save()
return redirect('Edit Menu Item')
else:
return redirect('Edit Menu Item')
else:
return render(request, 'mis446/edit-menu-item.html', context)
else:
return render(request, 'mis446/edit-menu-item.html', context)
forms.py:
class ProductModelForm(forms.ModelForm):
class Meta:
model = Product
fields = ['name','price'] # specify which field you need to be in the form
HTML:
<title>ACRMS - Edit Menu Price</title>
<div class = "container">
<form action = "" method = 'POST'>
{% csrf_token %}
{% for instance in object_list %}
<input name = "name" value = "{{ instance.name }}"></input>
<input type="number" name="price" value = "{{ instance.price }}"/><br>
{% endfor %}
</select>
<button type ="submit">Submit Changes</button>
</form>
</div>
Urls.py:
url('edit-menu/edit/',views.edit_menu, name='Edit Menu Item'),
models.py:
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField()
slug = models.SlugField()
def __str__(self):
return self.name
For your current implementation, you do not need a form. Instead, update the view like this:
# view
def edit_single_menu(request, pk):
if request.method == 'POST':
post=Product.objects.get(pk=pk)
if request.POST.get('price') and request.POST.get('name'):
post.name= request.POST.get('name')
post.price= request.POST.get('price')
post.save()
return redirect('Edit Menu Item')
else:
return redirect('Edit Menu Item')
return render(request, 'mis446/edit-menu-item.html', context)
# url
url('edit-menu/edit/<pk:id>/',views.edit_single_menu, name='edit_single_menu'),
# template (mis446/edit-menu-item.html)
<title>ACRMS - Edit Menu Price</title>
<div class = "container">
{% for instance in object_list %}
<form action = "{% url 'edit_single_menu' instance.pk %}" method = 'POST'>
{% csrf_token %}
<input name = "name" value = "{{ instance.name }}"></input>
<input type="number" name="price" value = "{{ instance.price }}"/><br>
<button type ="submit">Submit Changes</button>
</form>
{% endfor %}
</div>
Here I am sending individual edit to a new separated view named edit_single_menu and store the changes there.
Update
New url is not meant to replace the old one. It is only to assist you to update individual product. So, you need to keep both of the urls. Also, here is an answer based on #brunodesthuilliers's suggestion:
# view
def edit_single_menu(request, pk):
if request.method == 'POST':
post=Product.objects.get(pk=pk)
form = ProductForm(request.POST, instance=post)
if form.is_valid():
form.save()
return redirect('Edit Menu Item')
Also, do some changes on edit_menu view:
def edit_menu(request):
queryset = Product.objects.all()
context = { "object_list": queryset }
return render(request, 'mis446/edit-menu-item.html', context)
And urls should look like this:
from django.urls import include, path
# rest of the code
path('edit-menu/edit/<int:pk>/',views.edit_single_menu, name='edit_single_menu'),
path('edit-menu/edit/',views.edit_menu, name='Edit Menu Item'),

Django - Why is this random text rendering when I get errors upon form submission attempt?

I have a page with a form that takes in an employee # (using foreignkey), and when it is submitted it verifies that this employee # is in fact in another model (Salesman), and checks if 'WF' is in the team field for this employee. While the logic works and everything is being displayed, I keep getting this random bold text under the box Salesman object (406) (or whichever number I entered that would give me an error) after submitting the form, along with the proper error on top.
I think this is related to the foreignkey field part, but I'm not sure how to prevent this from showing up when there are errors.
models.py
class EmployeeWorkAreaLog(TimeStampedModel, SoftDeleteModel, models.Model):
employee_number = models.ForeignKey(Salesman, on_delete=models.SET_NULL, null=True, blank=False)
...
def __str__(self):
return self.employee_number
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'employee_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('employee_number').remote_field, site, attrs={'id':'employee_number_field'}),
}
fields = ('employee_number', 'work_area', 'station_number')
def clean_employee_number(self):
employee_number = self.cleaned_data.get('employee_number')
if 'WF' in employee_number.team:
raise forms.ValidationError("Employee not valid, please contact manager")
else:
pass
return self.cleaned_data
views.py
class EnterExitArea(CreateView):
model = EmployeeWorkAreaLog
template_name = "operations/enter_exit_area.html"
form_class = WarehouseForm
def form_valid(self, form):
emp_num = form.cleaned_data['employee_number']
area = form.cleaned_data['work_area']
station = form.cleaned_data['station_number']
if 'enter_area' in self.request.POST:
form.save()
EmployeeWorkAreaLog.objects.filter((Q(employee_number=emp_num) & Q(work_area=area) & Q(time_out__isnull=True) & Q(time_in__isnull=True)) & (Q(station_number=station) | Q(station_number__isnull=True))).update(time_in=datetime.now())
return HttpResponseRedirect(self.request.path_info)
elif 'leave_area' in self.request.POST:
form.save()
return HttpResponseRedirect(self.request.path_info)
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" novalidate >
{% csrf_token %}
<div>
<div style="color: red">{{ form.employee_number.errors.as_text }}</div>
<div>
<label>Employee</label>
{{ form.employee_number }}
</div>
<!-- ... More fields ... -->
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
<button type="submit" name="leave_area" value="Leave">Leave Area</button>
</div>
</div>
</form>
{% endblock main %}
That is part of the ForeignKeyRawIdWidget widget and it's the representation of the selected object (the Salesman object with ID 406).
If you wanted to get rid of it you would have to create a new widget which extends ForeignKeyRawIdWidget and removes that bit from the template. Here you can see how ForeignKeyRawIdWidget and its template look like.
Alternatively, and possibly better, you could consider to define the __str__ method of the Salesman model to show something more meaningful, in the same way you did for EmployeeWorkAreaLog.

Django Forms: Cannot show Validation Error

I have a Django Form where I allow the user to submit a value only if his password matches a predefined password set by me : Pass_matcher
Validation works fine, such that if passwords match, value is entered and stored and if not, nothing happens.
However I want that if passwords do not match, I show a simple custom warning message. I looked at other SO questions such as here and here, but I can't seem to get it right.
Note: If the user enters the correct password I do not want to redirect to another page.
forms.py
from django import forms
class TactForm(forms.Form):
password = forms.CharField(widget=forms.PasswordInput(
attrs = {
'class' : 'form-control mb-2 mr-sm-2 mb-sm-0',
'placeholder' : 'Enter Password:',
'id' : 'inlineFormInput',
'required' : 'true'
}
), required = True, label='Tact Password', max_length=100)
tacttime = forms.CharField(widget=forms.TextInput(
attrs = {
'class': 'form-control mb-2 mr-sm-2 mb-sm-0',
'placeholder': 'Enter Tact Time:',
'id' : 'inlineFormInput2'
}
),label='Tact Time', max_length=100)
def clean(self):
cleaned_data = super(TactForm,self).clean()
password = cleaned_data.get('password')
current_tact = cleaned_data.get('tacttime')
if password != 'Pass_matcher':
print('incorrect') #this prints to console if incorrect
raise forms.ValidationError('Incorrect Password') # this does not work
else:
print('correct') #this prints to console if correct
return cleaned_data
views.py
def details(request):
form = TactForm(request.POST or None)
if request.method == 'POST':
form = TactForm(request.POST)
if form.is_valid():
print('here1')
current_tact = form.cleaned_data['tacttime']
password = form.cleaned_data['password']
else:
form = TactForm()
return render(request, 'linedetails/index.html',{'form':form})
template
<div class="user_in" style="text-align:center;">
<form class="form-inline" method="POST" action="{% u$
{% csrf_token %}
{{ form.password }}
{{ form.tacttime }}
<br>
<button type="submit" class="btn btn-outline>
</form>
</div>
The below code is experimental
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
I cannot understand why the raise forms.ValidationError('Incorrect Password')
is not shown if the statement above it is correctly printed to terminal.
I am thinking that I have something missing in else statement of the views.py script.
Thanks for any suggestions.
You redefined form instance if form is not valid. just remove else block to fix it:
def details(request):
form = TactForm(request.POST or None)
if request.method == 'POST':
form = TactForm(request.POST)
if form.is_valid():
print('here1')
current_tact = form.cleaned_data['tacttime']
password = form.cleaned_data['password']
return render(request, 'linedetails/index.html',{'form':form})
Also you actually dont need if request.method == 'POST' validation since form will be populated with post data automatically here form = TactForm(request.POST or None). So you can simply rewrite your view to this:
def details(request):
form = TactForm(request.POST or None)
if form.is_valid():
print('here1')
current_tact = form.cleaned_data['tacttime']
password = form.cleaned_data['password']
return render(request, 'linedetails/index.html',{'form':form})

Categories

Resources