I'm trying to customize the default error list in django. I've tried following the docs, but the problem is that the supposed html gets outputted as text istead of html:
I'm not quite sure why this is happening, as the code is pretty much copy-paste from the docs. Code:
forms.py
class DivErrorList(ErrorList):
def __str__(self):
return self.as_divs()
def as_divs(self):
if not self: return ''
return '<div class="test">%s</div>' % ''.join(['<div class="error">%s</div>' % e for e in self])
views.py
def addUser(request):
add_user_form = AddUserForm()
messages.warning(request, 'message')
if request.method == 'POST':
add_user_form = AddUserForm(request.POST, error_class=DivErrorList)
if add_user_form.is_valid():
user = add_user_form.save()
customer_group = Group.objects.get(name = 'group_name')
user.roles.add(customer_group)
messages.success(request, 'message')
return redirect('users')
else:
messages.error(request, 'message')
context = {
'add_user_form': add_user_form,
}
return render(request, 'users/backend/user/user_add.html', context)
html
<div class="w-full mt-6">
{% include 'components/typography/form_label.html' with label='Gjenta passord' %}
{{ add_user_form.password2 }}
{{ add_user_form.password2.errors }}
</div>
You need to mark the string as "safe", i.e. this:
return '<div class="test">%s</div>' % ''.join(['<div class="error">%s</div>' % e for e in self])
should be
return mark_safe('<div class="test">%s</div>' % ''.join(['<div class="error">%s</div>' % e for e in self]))
where mark_safe comes from:
from django.utils.safestring import mark_safe
Related
This works but..
views.py
from django.http import HttpResponse
from django.db import connection
def query():
with connection.cursor() as cursor:
cursor.execute('SELECT some, stuff FROM here;')
row = cursor.fetchall()
return row
def index(request):
return HttpResponse(query())
What if I want to get a filtered response that is restricted to a user or group from the admin?
For example, if the user is in the BAIT group they could filter results WHERE email LIKE 'bob#bait.com';
I did it like this...
/opt/my_project/my_app/templates/my_app/generate_html.html
<html>
<h1>Generate HTML</h1>
<form method="POST" action="">
{% csrf_token %}
{{ form }}
<button type="submit">Submit Query</button>
</form>
</html>
/opt/my_project/my_project/settings.py
'DIRS': ['/opt/my_project/my_app/templates/my_app'],
/opt/my_project/my_app/urls.py
path('generate_html/', generate_html, name = "generate_html"),
/opt/my_project/my_app/forms.py
from django import forms
class InputForm(forms.Form):
email = forms.EmailField(max_length = 100)
/opt/my_project/my_app/views.py
def query(email):
with connection.cursor() as cursor:
query = '''SELECT some, stuff
FROM here
WHERE email = %s
ORDER BY stuff;
'''
values = (email,)
cursor.execute(query, values)
select = cursor.fetchall()
return select
def generate_html(request):
if request.method == 'POST':
email = request.POST.get('email', None)
try:
html = '<!DOCTYPE html><html><body>'
for row in query(email):
some, stuff = row
html += 'Row: ' + some + ' ' + stuff + '<br>'
html += '<br><br>' + 'Search Again!' + '</body></html>'
return HttpResponse(html)
except Exception as e:
return HttpResponse(str(e))
else:
context ={}
context['form']= InputForm()
return render(request, "generate_html.html", context)
When I try to submit my form I get this message:
The view views.save_flow_data didn't return an HttpResponse object. It returned None instead.
Views.py
def save_flow_data(request):
if request.method == 'POST':
if request.POST.get('upload_flow') \
and request.POST.get('water_flow_rate')\
and request.POST.get('water_flow_rate_unit')\
data=CalcData()
data.water_flow_rate = request.POST.get('water_flow_rate')
data.water_flow_rate_unit = request.POST.get('water_flow_rate_unit')
data.save()
return render(request, 'io/flow.html')
else:
return render(request,'io/flow.html')
models.py
class CalcData(models.Model):
upload_flow = models.BooleanField(default=False)
water_flow_rate = models.DecimalField(max_digits=100, decimal_places=5)
water_flow_rate_unit = models.TextChoices('wfr_unit', 'm3/day m3/month')
datetime = models.DateTimeField(default=timezone.now)
submit button in form
<div>
<button action="{% url 'MyProject:save_flow_data' %}" type="submit" class="btn btn-light" style="width: 517.5px;" >Calculate</button>
</div>
urls.py
urlpatterns = [
path('', views.home, name='MyProject-home'),
path('io/flow/', views.io_flow, name='MyProject-io-flow'),
path('io/flow/save_flow_data', views.save_flow_data, name='save_flow_data')
]
I'm really not sure where to go from here. I had a perfectly working form but as soon as I scaled it up it flopped.
The case
if request.POST.get('upload_flow') \
and request.POST.get('water_flow_rate')\
and request.POST.get('water_flow_rate_unit')\
is False is not covered.
You need to either remove the last else :
def save_flow_data(request):
if request.method == 'POST':
if request.POST.get('upload_flow') \
and request.POST.get('water_flow_rate')\
and request.POST.get('water_flow_rate_unit')\
data=CalcData()
data.water_flow_rate = request.POST.get('water_flow_rate')
data.water_flow_rate_unit = request.POST.get('water_flow_rate_unit')
data.save()
return render(request, 'io/flow.html')
return render(request,'io/flow.html')
Or add a return to the uncovered path:
def save_flow_data(request):
if request.method == 'POST':
if request.POST.get('upload_flow') \
and request.POST.get('water_flow_rate')\
and request.POST.get('water_flow_rate_unit')\
data=CalcData()
data.water_flow_rate = request.POST.get('water_flow_rate')
data.water_flow_rate_unit = request.POST.get('water_flow_rate_unit')
data.save()
return render(request, 'io/flow.html')
return render(request, 'io/flow.html') # Or return something else
else:
return render(request,'io/flow.html')
Remove else statement and keep indentation of:
return render(request,'io/flow.html')
At function level.
Dear Python community,
Could you please share some insights with a newbie like myself regarding the following topic:
I would like to dynamically modify the inputs into form field input, specifically
forms.ChoiceField(choices=((text, name), widget=forms.Select())
As I was not able to access requestfrom class in forms.py, I'd like to try editing the choices from Django template engine. Is it possible to edit the choices, taking the parameters from views.py method using jinja?
The question is conceptual, a few lines of code as an example would be enough, I'll pick it up.
The tricky part is - the data should be dependent on logged-in User's created model instances.
If there's no actual way to do it via python, but js only - please let me know so I don't dry to do the impossible.
Thank you!
Code sample for reference:
forms.py
class InformForm(forms.Form):
flight_number = forms.CharField(5, widget=forms.TextInput())
date = forms.DateField(widget=forms.DateInput(attrs={'class': 'datepicker'}))
template = forms.ChoiceField(choices=tuple([(template.text, template.name) for template in Template.objects.all()]),
widget=forms.Select(attrs={'id': 'select_box',
'onchange': 'javascript:changer();'}))
text = forms.CharField(widget=forms.Textarea(attrs={'id': 'txt_box', 'class': 'latin',
'maxlength': "160", 'onchange': 'javascript:validateTextArea();'}))
template
<form class="form-signin form-container" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form-element-wrapper">
<div class="error-form-element">
<span class="error-span">{{field.errors}}</span>
</div>
<div class="form-label">{{field.label_tag}}</div>
<div class="form-data">{{field}}</div>
</div>
{% endfor %}
<button id="cr_inf" type="submit" class="btn btn-lg btn-primary btn-block stl-color"><span id="loader" class=""></span>Create inform</button>
</form>
views.py
class InformFill(View):
form_class = InformForm
temlate_name = 'distrib_db/inform_fill.html'
def get(self, request):
if request.user.is_authenticated():
form = self.form_class(None)
return render(request, self.temlate_name, context={'form': form})
else:
return redirect('distrib_db:login')
def post(self, request):
if request.user.is_authenticated():
form = self.form_class(user=request.user, data=request.POST)
if form.is_valid():
inform = Inform(flt_numbr=form.cleaned_data['flight_number'], date=form.cleaned_data['date'],
template=form.cleaned_data['text'], request=request)
inform.save()
date = form.cleaned_data['date']
flt_numbr = form.cleaned_data['flight_number']
try:
emails, contacts = get_mail_cnt(date, flt_numbr)
# inform = get_object_or_404(Inform, pk=request['pk'])
paxdata = PaxData(inform=inform, emails=' '.join(emails), contacts=' '.join(contacts))
paxdata.save()
return redirect('/inform/{0}/'.format(inform.pk))
# 'distrib_db:detail', context={'pk': inform.id}
except Exception as e:
return render(request, 'distrib_db/sample.html',
context={'date': date, 'flight_number': flt_numbr, 'error': e})
# return render(request, 'distrib_db/sample.html', context={'date': date, 'flt_numbr': flt_numbr})
return render(request, self.temlate_name, context={'form': form})
else:
return redirect('distrib_db:login')
QuerySet issue:
>>> usr = User.objects.filter(username='aleks')
sample = tuple([(template.text, template.name) for template in usr.template_set.all()])
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'QuerySet' object has no attribute 'template_set'
In InformForm class override __init__
def __init__(self, user, *args, **kwargs):
super(InformForm, self).__init__(*args, **kwargs)
self.fields['template'] = forms.ChoiceField(choices="make choice based on user")
My first foray into Django, allows the user to input a day of the week, and searches my database for restaurants open on that given day. Right now, the restaurant objects (Resto) have a days_open attribute, with each day separated by a comma (Monday, Tuesday, etc...).
When I input a day, the resulting page only displays the title and '[]' and I can't seem to get it to return the list of Resto objects. What is displaying the square brackets, and how do I go about fixing it to display the results of the search?
The code is below- my apologies if I neglected to include any relevant bits.
my forms.py:
from django import forms
from .models import Resto
class RestoSearch(forms.ModelForm):
class Meta:
model = Resto
fields = ('title', 'description', 'opening_hour', 'closing_hour')
models.py:
from django.db import models
class Resto(models.Model):
title = models.CharField(max_length=300)
description = models.TextField()
opening_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
closing_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
days_open = models.TextField(blank=True)
views.py:
from django.shortcuts import render
from django.http import Http404
from django.shortcuts import HttpResponse
from belize.models import Resto
from django.core.exceptions import *
from .forms import RestoSearch
def index(request):
return render(request, 'form.html')
def search(request):
form = RestoSearch()
if request.method == 'POST':
search_id=request.POST.get('textfield', None)
try:
#I think this is where my problem is
available = Resto.objects.filter(days_open = search_id)
html = ("<H1>Hello</H1>", available)
return HttpResponse(html)
except Resto.DoesNotExist:
return HttpResponse("Please try another day")
else:
return render(request, 'belize/form.html')
def restaurant_detail(request, id):
try:
restaurant = Resto.objects.get(id=id)
except Resto.DoesNotExist:
raise Http404('This restaurant does not exist')
return render(request, 'belize/restaurant_detail.html', {
'restaurant': restaurant,
})
template form.html:
<form method="POST" action="/search/">
{% csrf_token %}
<input type="text" name="textfield">
<button type="submit">Enter a day of the week</button>
</form>
I presume what you are trying to show is the RestoForm in that case the index method is not correct. It should be
def index(request):
form = RestoForm()
return render(request, 'form.html', {'form': form })
And then your template should change as
<form method="POST" action="/search/">
{% csrf_token %}
{{ form }}
<button type="submit">Enter a day of the week</button>
</form>
For additional details please see the examples at https://docs.djangoproject.com/en/1.9/topics/forms/#the-template
The [] means that your .filter() returned no results, its not surprising as you have a few issues with your code, lets start from the top:
You are declaring a form that you never use.
You are trying to catch an exception that is never raised by .filter()
You filter condition will only work for exact matches.
I've annotated your code as well:
def search(request):
form = RestoSearch() # You aren't using this form anywhere in your code?
if request.method == 'POST':
# Where is 'textfield' coming from?
search_id = request.POST.get('textfield', None)
try:
# If search id is "Tuesday", and a restaurant is
# open on monday and tuesday, so it has "Monday,Tuesday"
# in the days_open field, then this search will not
# return any results, because its looking for an exact
# match
available = Resto.objects.filter(days_open=search_id)
html = ('<H1>Hello World</H1>', available)
return HttpResponse(html)
except Resto.DoesNotExist:
# This exception is not raised by .filter(),
# .filter() will return an empty list, [] if no results are found
# so this entire try/except is not doing anything
return HttpResponse("Please try another day")
else: # in your code, this else is not indented correctly
return render(request, 'belize/form.html')
So there is a lot going on here, lets try something simple, starting with the template:
{% if results %}
{% for restaurant in results %}
{{ restaurant }}
{% endfor %}
{% else %}
Sorry, no results for your search. Try again.
{% endif %}
<form>
{{ form }}
<input type="submit" name="Search" />
</form>
Next, the search form:
class SearchForm(forms.Form):
search_field = forms.CharField('Search', strip=True)
Finally the view:
from django.db.models import Q
def search(request):
form = SearchForm(request.GET)
results = [] # Set results to an empty list
if form.is_valid():
needle = form.cleaned_data['search_field'].capitalize()
results = Resto.objects.filter(Q(days_open__startswith='{},'.format(needle)) |
Q(days_open__endswith=',{}'.format(needle)) |
Q(days_open__contains=',{},'.format(needle)) |
Q(days_open='{},'.format(needle)) |
Q(days_open='{}'.format(needle)))
return render(request, 'search.html', {'results': results, 'form': form})
Lets assume the user entered 'Thursday' as a search field. In this view you are searching for all restaurants whose days_open field:
Either starts with Thursday, or
Ends with ,Thursday or
Contains ,Thursday, in the middle or
Has the value Thursday, or
Has the value Thursday
Your template will then only show the results if there are any values to display; since an empty list [] is false, then the {% if results %} condition will fail, so on empty lists the template will display the error notice instead.
In your view, you only do the database check if someone enters something in the search field, that's what if form.is_valid(): does. In django, by default all form fields are required - so a blank form will return an error. Using this trick, we make sure we only search if someone enters a value in the search box.
The main action happens with all the Q() calls. A Q object is a way to do multiple queries and chain them together. It is used whenever you want to do an "or" or "and" type query. Our search is an "or" type query, because we want to return any results if the value in days_open matches any number of conditions.
I want to upload a csv file and store it in a database. My Django version is 1.9 and Python3.4. Below is the code. My app name is 'pft'
pft/utils.py
from .models import Account
import csv
def handle_uploaded_file(file, valid_fields_method, record_creation_function):
file.seek(0)
# !importtant
# csv file must be encoded in UTF-8
sniffdialect = csv.Sniffer().sniff(file.read(10000), delimiters='\t,;')
file.seek(0)
#print sniffdialect.fieldnames
data = csv.DictReader(file, dialect=sniffdialect)
if not valid_fields_method(data.fieldnames):
return False, -1
result, rows_error = record_creation_function(data)
return result, rows_error
def account_valid_fields(field_names):
required_fields = ('species', 'x', 'y')
for field in required_fields:
if field not in field_names:
return False
return False
def create_account_in_db(dict_data):
list_data = []
result = False
rows_error = 0
for record in dict_data:
species = record['species']
x = record['x']
y = record['y']
account = models.Account(species=species,\
x=x,\
y=y)
list_data.append(account)
if list_data:
# bulk_create will create multiple object in a single query
created_accounts = models.Account.objects.bulk_create(list_data)
if len(list_data) == len(created_accounts):
result=True
else:
rows_error = len(list_data) - len(created_accounts)
return result, rows_error
models.py
class Account(models.Model):
species=models.CharField(max_length=255)
x=models.IntegerField()
y=models.IntegerField()
last_modified = models.DateTimeField(auto_now = True)
first_created = models.DateTimeField(auto_now_add = True)
def __str__(self):
return "%s %s %s" % (self.species, self.x,self.y)
views.py
def add_multiple_accounts(request):
if request.method == 'POST':
csv_upload_form = UploadFileForm(request.POST, request.FILES)
if csv_upload_form.is_valid():
file = csv_upload_form.cleaned_data['file']
csv_result, rows_error = utils.handle_uploaded_file(file, utils.account_valid_fields, utils.create_account_in_db)
if csv_result:
message = 'Successfully imported accounts from the csv file to the database.\n'
message += 'The system is creating Active Directory Accounts using those information in the background.\n'
message += 'Please wait...'
messages.add_message(request, messages.INFO, message)
else:
message = 'There are some errors occured. Please try again.'
messages.add_message(request, messages.INFO, message)
else:
csv_upload_form = UploadFileForm()
return render_to_response('add_multiple.html', locals(), context_instance=RequestContext(request))
urls.py
url(r'^csv',add_multiple_accounts),
add.mutilple.html
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ csv_upload_form.as_p }}
<button type="submit" class="action green btn-center" name="action" value="add_csv"><span class="label">Upload</span></button>
<input type="submit" />
</form>
When I click the submit button I get the above error. How can I solve this?
try:
file_content = bytes(file.read(10000), 'UTF-8')
sniffdialect = csv.Sniffer().sniff(file_content, delimiters='\t,;')
it is because string in Python3.x is not the same as in python2.x, you need to cast it with bytes and encode as utf-8