KEY ERROR on self.cleaned_data.get('file') - python

I'm getting a key error while I'm trying to validate a simple form in Django:
I've tried accessing the file name using:
all_clean_data['file']
self.cleaned_data.get('file')
self.cleaned_data['file']
To no avail. If i'm accessing as a key, I'm getting key error, if I'm using the get method I get None.
Can you please tell my what I'm doing wrong?
class UploadForm(forms.Form):
url_cell_start = forms.IntegerField()
url_cell_end = forms.IntegerField()
write_cell_start = forms.IntegerField()
write_cell_end = forms.IntegerField()
file = forms.FileField()
def clean(self):
all_clean_data = super(UploadForm, self).clean()
url_c_start = all_clean_data['url_cell_start']
url_c_end = all_clean_data['url_cell_end']
write_c_start = all_clean_data['write_cell_start']
write_c_end = all_clean_data['write_cell_end']
xl_file = all_clean_data["file"]
extension = os.path.splitext(xl_file.name)[1]
VALID_EXTENSION = '.xlsx'
if extension.lowercase != VALID_EXTENSION:
self.add_error('file', 'The file has to be .XLSX')
if (url_c_start is None) or not (url_c_start > 0):
self.add_error('url_cell_start', 'The URL column needs to be a number grater than zero.')
if (url_c_end is None) or not (url_c_start > 0):
self.add_error('url_cell_end', 'The URL column needs to be a number grater than zero.')
else:
if url_c_start is not None:
if url_c_end <= url_c_start:
self.add_error('url_cell_end',
'The URL-End column need to be a number grater or equal than URL Start')
if (write_c_start is None) or not (write_c_start > 0):
self.add_error('write_cell_start', 'The URL column needs to be a number grater than zero.')
else:
if url_c_end is not None:
if url_c_end > write_c_start:
self.add_error('write_cell_start',
'The URL-Write-start column number needs to be a number grater than URL-cell-end')
if (write_c_end is None) or not (write_c_end > 0):
self.add_error('write_cell_end', 'The URL column needs to be a number grater than zero.')
else:
if write_c_start is not None:
if write_c_end <= write_c_start:
self.add_error('write_cell_end',
'The URL-Write-end column number needs'
' to be a number grater or equal than URL-Write-start')
return all_clean_data

Finally, after three hours of intensive google searching I've found the solution.
The reason I wasn't getting the key was that haven't added "enctype="multipart/form-data" to the HTML tag in the template.
Like:
<form method="POST" enctype="multipart/form-data">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" class="btn btn-primary" value="Submit">
</form>

Related

Increment Last Id from String in python Django

I am trying to generate an auto-increment number as an ID with Company Label. Company Labels will be changing for every user. So I cant use slicing here.
My ID is like TES-PRODUCT-01
TES is a company label
PRODUCT is as it is name
But now I wish to change my last number when I am adding new product to TES-PRODUCT-02 and so on
Getting Error **can only concatenate str (not "int") to str**
Here is my code
views.py
def add_new_stock(request):
stock_data=New_Stock_Entry.objects.all()
if not stock_data:
latest_item_code="TES-PRODUCT-001"
else:
latest_item_code = (New_Stock_Entry.objects.last()).item_code+1
get_tax_code=Tax_Settings.objects.values('name', 'tax_percentage','tax_id')
if request.method == 'POST':
item = request.POST['item']
hsn = request.POST['hsn']
item_code=latest_item_code
stock_in_date=request.POST['stock_in_date']
quantity_in_hand=request.POST['quantity_in_hand']
sales_price=request.POST['sales_price']
item_description=request.POST['item_description']
unit=request.POST['unit']
tax_code = request.POST['tax_code']
tax_obj = Tax_Settings.objects.get(tax_id=tax_code)
item_creation_details = New_Stock_Entry.objects.create(item=item, hsn=hsn, item_code=item_code,stock_in_date=stock_in_date,quantity_in_hand=quantity_in_hand,sales_price=sales_price ,item_description=item_description, unit=unit, tax_code=tax_obj)
item_creation_details.save()
print("item_details",item_creation_details)
return render(request,"inventory/add-stock.html")
return render(request,"inventory/add-stock.html",{'get_tax':get_tax_code,'latest_item_code':latest_item_code})
html
<input type="text" class="form-control" placeholder="TES-PRODUCT-{{latest_item_code}}" name="item_code">
How Can I increment my last number from string?
You can simply use "f-strings"
total_stock = New_Stock_Entry.objects.all().count() + 1
latest_item_code=f"TES-PRODUCT-{total_stock}"

in flask, give user a list of options to select from with arbitrary length

i'm new to flask, and python in general so let me know if i'm doing anything wrong...
i'm learning by building a web-app. my current goal is to make playlists on spotify from user input. everything i'm trying to do here i have already made work for myself in pycharm scripts, it's the web-dev and interacting with the user part i'm struggling with atm.
i want to:
take input (artist name) from user (done)
send artist name as search term to spotify and return a list of dictionaries of matching artists. these dictionaries have a bunch of info including 'name' and 'id'. (done)
display the names in the result dicts to the user who will select which to include in their playlist. (please help!)
use the list of ids which match the selected artists to interogate spotify for all the tracks by those artists and create a playlist.
i just need help with #3
i have a functioning flask / bootstrap web-app that allows user to authenticate via spotify and input the artist name and which conducts the search and returns the list of dicts. i'm using flask forms to take the input, and assume i'll use another to display the results as radio-button fields for user to select one/many from but the list is of arbitrary length and i don't know how to pass this dynamically to the html. the dictionary i make from the spotify response has names as keys and ids as values - i only want to pass the names to the user to select from, but i'll need to maintain acces to the dictionaries so i can retrieve the ids for the spotify search later...
i've added the code i use to make and populate the playlists in case anyone likes/wants it/has any comments.
my code:
python
class ArtistTracksForm(FlaskForm):
artist = StringField('Who are you searching for?')
submit = SubmitField('search')
...
#app.route('/artist_playlist', methods=['GET', 'POST'])
def artist_playlist():
# prepare spotify
cache_handler = sp.cache_handler.CacheFileHandler(cache_path=session_cache_path())
auth_manager = sp.oauth2.SpotifyOAuth(cache_handler=cache_handler)
if not auth_manager.validate_token(cache_handler.get_cached_token()):
return redirect('/')
spotify = sp.Spotify(auth_manager=auth_manager)
#get form
form = ArtistTracksForm()
if form.validate_on_submit():
artist = form.artist.data
results = spotify.search(q=artist, type='artist')
items = results['artists']['items']
#make a dictionary of results
names_list = {}
for item in items:
artist_name=item['name']
artist_id = item['id']
names_list.update({artist_name:artist_id})
# temp. make a string of keys (artist names) by unpacking the dict
name_string = ''
for name in [*names_list]:
name_string = name_string + name + ", "
return name_string
#app.route('/handle_data')
def handle_data():
data_handler = request.form['APform']
i don't really know what i'm doing with the 'action=' and handle_data(): parts but can work that out, it's the dynamically sized list of fields and interacting between python and html i'm really perplexed by
html.j2:
{% extends "layout.html.j2" %}
{% block content %}
<div class="content-section">
<form method="POST" action="{{ url_for('handle_data')}}" name=APform>
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Artist Playlists</legend>
<div class="form-group">
{{ form.artist.label(class="form-control-label") }}
{{ form.artist(class="form-control form-control-lg") }}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
any input greatly appreciated, i've been searching google and SO for hours now...
thanks
here's the python code i use to get all the tracks from all the albums that an artist has at least one track on, and make a playlist on my spotify account for each artist.... max tracks is 10k so it makes multiple if that is exceeded... in case that's of interest to anyone who read this far... i've been making sql databases and inserting huge spotify playlists into my account. XD
def get_releases(list_of_artist_dicts, form='dict'):
"""takes list of artist dicts
return list of dicts (form = 'dict)
or list of id strings 'form='list'"""
album_list = []
for artist_dict in list_of_artist_dicts: # iterate artist dicts
artist = artist_dict['artist']
artist_id = artist_dict['artist_id']
results = sp.artist_albums(artist_id) # call spotify for artist_album details
releases = results['items']
while results['next']: # check for more pages
results = sp.next(results)
releases.extend(results['items'])
for release in releases:
album_group = release['album_group']
album_type = release['album_type']
album = release['name']
album_id = release['id']
spotify_album_url = release['external_urls']['spotify']
release_date = release['release_date']
release_date_precision = release['release_date_precision']
release_date = date_stripper(release_date,
release_date_precision) # convert date string to datetime obj, insert spurious 1s if precision < day
album_artist = release['artists'][0]['name'] # get (first) album artist details
album_artist_id = release['artists'][0]['name']
release_dict = {
'artist': artist,
'artist_id': artist_id,
'album_artist': album_artist,
'album_artist_id': album_artist_id,
'album': album,
'album_id': album_id,
'album_group': album_group,
'album_type': album_type,
'release_date': release_date,
'release_date_precision': release_date_precision,
'spotify_album_url': spotify_album_url
}
if form == 'dict':
album_list.append(release_dict)
if form == 'list':
album_list.append(release_dict['album_id'])
return album_list
def get_tracks(list_of_artist_dicts, form=dict):
""" takes list of artist dicts, returns a list of track dicts, or track ids if form=list """
track_dicts_list = []
for artist_dict in list_of_artist_dicts:
album_list = []
artist = artist_dict['artist']
artist_id = artist_dict['artist_id']
artist_dict = [artist_dict]
release_list = get_releases(artist_dict, 'dict')
for release in release_list:
album_id = release['album_id']
album_list.append(album_id)
for offset in range(0, len(album_list), 20):
batch = album_list[offset:offset + 20]
result = sp.albums(batch)
albums_batch = result['albums']
for batched_album in albums_batch:
album = batched_album['name']
album_id = batched_album['id']
# release_date = batched_album['release_date'] tracks dont have release dates?
album_type = batched_album['album_type']
track_count = len(batched_album['tracks']['items'])
for act in batched_album['artists']:
album_artist = act['name']
album_artist_id = act['id']
track_id_list = []
for track in batched_album['tracks']['items']:
track_artist = track['artists'][0]['name']
track_artist_id = track['artists'][0]['id']
title = track['name']
spotify_track_url = track['external_urls']['spotify']
track_id = track['id']
track_id_list.append(track_id)
track_dict = {
'artist': artist,
'artist_id': artist_id,
'album_artist': album_artist,
'album_artist_id': album_artist_id,
'album': album,
'album_id': album_id,
'track_artist': track_artist,
'track_artist_id': track_artist_id,
'title': title,
'track_id': track_id,
'album_type': album_type,
'spotify_track_url': spotify_track_url
}
track_dicts_list.append(track_dict)
if form == 'list':
return track_id_list
return track_dicts_list
def make_playlist(playlist_name='python_play'):
"""creates a playlist on user account, returns playlist_id"""
user = sp.me()['id']
return sp.user_playlist_create(user=user,name=playlist_name)['id']
def add_to_playlist(playlist_id, track_list):
sp.playlist_add_items(items=track_list, playlist_id=playlist_id)
def check_playlist_exists(term):
""" searches current user's playlists for playlist_name = term returns true if exists"""
limit = 50
offset = 0
current_names = []
for step in range (0,1000,50):
current_playlists = sp.current_user_playlists(limit=limit, offset=offset)['items']
for playlist in current_playlists:
current_names.append(playlist['name'])
offset += 50
if term in current_names:
return True
def get_alpha(my_dict):
sorter = my_dict['name']
sortering = sorter.lower()
return sortering
def create_playlist_all_artist_tracks(artist_list):
artist_list.sort(reverse=True, key=get_alpha) # reverse sort list so playlists will be front-sorted in spotify
already = []
making = []
for artist in artist_list:
artist_name = artist['name']
artist_id = artist['id']
playlist_name = f"{artist_name} and friends - all tracks from all releases including features_on- made in python"
if check_playlist_exists(playlist_name):
print (f"ALREADY EXISTS - {playlist_name}")
already.append(playlist_name)
else:
print (f'PLAYLIST NOT FOUND making playlist "{playlist_name}"')
making.append(playlist_name)
artist_tracks = get_track_id_list_artist(artist_id)
if len(artist_tracks) < 10000:
playlist_id = make_playlist(playlist_name)
start = 0
for step in range(start, len(artist_tracks), 100):
batch = artist_tracks[start:start + 100]
add_to_playlist(playlist_id, batch)
start += 100
else:
count = 1
begin = 0
start=0
for section in range(begin, len(artist_tracks), 10000):
playlist_id = make_playlist(f"{playlist_name} number {count}")
chunk = artist_tracks[begin:begin + 10000]
for step in range(start, len(chunk), 100):
batch = artist_tracks[start:start + 100]
add_to_playlist(playlist_id, batch)
start += 100
count+=1
begin += 10000
print ("PLAYLISTS ALREADY MADE:")
pprint (already)
print ("NEW PLAYLISTS CREATED :")
pprint (making)

Passing list dict to template not working django

I am trying to pass a list called eligable so that I can display on my website but when I run my website it does not display the list. I do not understand what is wrong.
code:
def specificDate(response):
empName = employeeName.objects.all()
eligable = []
if 'checkEmployee' in response.POST:
n = response.POST.get("nameEmployee")
specDate = response.POST.get("date")
if employeeName.objects.filter(employee=n).exists() and Name.objects.filter(date=specDate).exists():
emp = employeeName.objects.get(employee=n)
t = Name.objects.get(name=emp, date=specDate)
overT = Name.objects.filter(name=emp, overtime=True)
for item in overT:
eligable.append(item.date)
checkIn = t.timeIn.strftime("%H:%M:%S")
checkOut = t.timeOut.strftime("%H:%M:%S")
datee = datetime.strptime(specDate,'%Y-%m-%d')
print("Here:: ",t.date)
print("Month:: ",datee.month)
messages.info(response, checkIn + ' - ' + checkOut)
return redirect('/specificDate')
else:
messages.info(response, 'Name does not exist')
else:
pass
return render(response, "main/specificDate.html", context={"empName":empName, "eligable":eligable})
This is the html to print my list:
{% for item in eligable %}
<div class="pad3">
{{item}}
</div>
{% endfor %}
There are two return statements in your code:
return redirect('/specificDate')
and
return render(response, "main/specificDate.html", context={"empName":empName, "eligable":eligable})
The first one is just redirecting without populating context.
The second one does populate context but is reached only when eligable is empty.
I think changing the first return to the second one should solve it.
edit: and you are missing {% endfor %} here. But it should give you an error if you miss it in your complete code.

HTML flask To accept only integer input

I'm trying to accept only integer input using the int() function and the type to number with min set. But I noticed that when I give an input like 5.0 it should treat it as an invalid input and accept only integers.
<input autocomplete="off" class="form-control" min="0" name="shares" id="shares" placeholder="Shares" type="number">
def sell():
"""Sell shares of stock"""
rows = db.execute("SELECT * FROM tracker WHERE id=:id", id = session["user_id"])
if request.method == "GET":
return render_template("sell.html", rows = rows)
else:
symbol = request.form.get("symbol")
shares = request.form.get("shares")
if not shares:
return apology("missing shares",400)
if not symbol:
return apology("missing symbol",400)
lis = db.execute("SELECT * FROM tracker WHERE id=:id AND name=:name", id=session["user_id"], name=symbol)
share = int(shares)
if lis[0]["no"] < share:
return apology("too many shares",400)
price = lookup(symbol)["price"]
cost = price * share;
saving = db.execute("SELECT cash FROM users WHERE id=:id", id=session["user_id"])[0]["cash"]
db.execute("UPDATE users SET cash=:cash WHERE id=:id", cash=cost + saving, id=session["user_id"])
if lis[0]["no"] == share:
db.execute("DELETE FROM tracker WHERE id=:id AND name=:name", id=session["user_id"], name=symbol)
else:
db.execute("UPDATE tracker SET no=:no where id=:id AND name=:name",no=lis[0]["no"] - share,id=session["user_id"], name=symbol)
db.execute("UPDATE users SET cash=:cash WHERE id=:id", cash=saving+cost, id=session["user_id"])
flash("Sold!")
return redirect("/")
I dont' see the error code,however I guess is caused by the html input.
UPDATE
Try this:
<input autocomplete="off" class="form-control" step="0" name="shares" id="shares" placeholder="Shares" type="number">
UPDATED:
Obviously you can instead run the check in Python, by doing something like:
symbol = request.form.get("symbol")
shares = request.form.get("shares")
if not symbol:
return apology("missing symbol",400)
if shares:
try:
shares = int(shares)
except ValueError:
error = 'It must be an integer'
return redirect("/", error=error)
else:
return apology("missing shares",400)
lis = db.execute("SELECT * FROM tracker WHERE id=:id AND name=:name", id=session["user_id"], name=symbol)
if lis[0]["no"] < share:
return apology("too many shares",400)
(For reference i leave the older answer)
Which won't work because shares would be a string
shares = intrequest.form.get("shares")
if isinstance(shares, float)):
error = 'It must be an integer'
return redirect("/", error=error)
This will work because first it checks if is a float or not, if it is then it will redirect , else you turn it a

Python Flask output dropdown values to page

I have a two dictionaries with several keys and one value for each key. One dict has weekly cumulative work hours, the other weekly cumulative overtime hours. The key is a datetime object, which is calculated to be the beginning of a week. On my webpage I have a dropdown box, which contains the week beginnings
<form action="{{ url_for('index') }}" method="post">
<select class="form-control" name="cumuls" id="select1" onchange="if(this.value != 0) { this.form.submit(); }">
<option value="0">Cumulative for week beginning...</option>
{% for i,j in zip(wks, wks2) %}
<option value="{{ i }}">{{ j }}</option>
{% endfor %}
</select>
</form>
(wks2 is just a nicely formatted version of wks, and wks is just a list of the keys from the dicts which are datetime objects (the dictionaries have the same keys))
I want to be able to click on an option from the dropdown box and for the corresponding values from the two dictionaries to appear below the dropdown box.
I handle the form here (within an index() function), after the if is the end of the index function:
if request.method == 'POST':
if 'cumuls' in request.form:
week_Begin = request.form['cumuls']
listHours = listDates(current_user.get_id())
dct_hours, dct_overtime, wks = weeklySum2(listHours)
#strfdelta is a custom function that formats timedeltas
cumul_hrs = strfdelta(dct_hours[datetime.fromisoformat(week_Begin)], '%H:%M')
cumul_overtime = strfdelta(dct_overtime[datetime.fromisoformat(week_Begin)], '%H:%M')
return redirect(url_for('index'))
listHours = listDates(current_user.get_id())
dct_hours, dct_overtime, wks = weeklySum2(listHours)
print(wks)
# wkbgn = weekbeginning, this just formats the dates nicely
wks2 = [wkbgn.strftime('%A') + ' ' + wkbgn.strftime('%-d') + ' ' + wkbgn.strftime('%b') + ' ' + wkbgn.strftime('%Y') for wkbgn in wks]
currentDay = datetime.today()
start_week = (currentDay - timedelta(days=currentDay.weekday()))
return render_template('index.html', form=form, hoursDb = listHours, dct_overtime=dct_overtime,dct_hours=dct_hours, wks=wks, wks2=wks2, zip=zip)
So within the if 'cumuls'..., I basically get the cumulative working and overtime hours for the selected week, in cumul_hrs and cumul_overtime, which works. I essentially want these variables to be displayed on the webpage (below the dropdown box), and the default variable that is displayed will be the current weeks cumulative so far. How could I do this?
Any pointers/tips are greatly appreciated.
I ended up just changing the redirect within the elif statement to
return redirect(url_for('index', cumul_hrs=cumul_hrs, cumul_overtime=cumul_overtime))
and then getting the variables from the URL with:
cumul_hrs, cumul_overtime = request.args.get('cumul_hrs'), request.args.get('cumul_overtime')
and passing them through render_template.

Categories