Django update template image after save it on server storage - django

I have a question about file upload and refresh it on template.
I have a custom variation of file saving system in my view:
sp = os.path.join(os.path.dirname(__file__), '../media/avatars')
av_name = os.path.join(sp, u.username + "_avatar.jpg")
dataUrlPattern = re.compile('data:image/(png|jpeg);base64,(.*)$')
ImageData = request.POST.get('u_avatar')
ImageData = dataUrlPattern.match(ImageData).group(2)
ImageData = base64.b64decode(ImageData)
ava = open(av_name, 'wb')
ava.write(ImageData)
ava.close()
And it works fine. I can upload and update avatars and save it with a custom name in media folder. But when I try to save a new file, it doesn't refresh on template! I mean that file saved correctly, I see a new image in server folder, but template render old version of them. When I try to open a image link, I still see an old file.
I think the reason is some kind of cache, but I can't understand, how it works.
Could you, please, help me, how I can refresh image after each upload?
EDIT:
This is my complete view:
def edit(request):
u = request.user
p = get_object_or_404 (Profile, user=request.user)
if request.method == 'POST':
profile_form = ProfileForm(request.POST)
if profile_form.is_valid():
if request.POST.get('u_avatar'):
sp = os.path.join(os.path.dirname(__file__), '../media/avatars')
av_name = os.path.join(sp, u.username + "_avatar.jpg")
os.remove(av_name)
dataUrlPattern = re.compile('data:image/(png|jpeg);base64,(.*)$')
ImageData = request.POST.get('u_avatar')
ImageData = dataUrlPattern.match(ImageData).group(2)
ImageData = base64.b64decode(ImageData)
ava = open(av_name, 'wb')
ava.write(ImageData)
ava.close()
profile = profile_form.save(commit=False)
profile.user = u
profile.avatar = str("/media/avatars/" + u.username + "_avatar.jpg")
profile.filled = True
if p.rate < 0.5:
profile.rate = 0.5
else:
profile.rate = p.rate
profile.save()
return HttpResponseRedirect('profile')
else:
profile_form = ProfileForm()
return render(request, 'profile_edit.html', {'profile_form': profile_form})
And this is my avatar on template:
<div class="lavatar"><img src="{{ user.profile.avatar }}" class="img-circle"></div>
BTW, I use CharField for avatar instead of ImageField or FileField. Maybe problem with that.
EDIT2
Now I found a new interesting fact: the image is updated after some time. I uploaded a new image and didn't touch site about 1,5 - 2 hours. And avatar had refreshed.

Finally I found a problem!
Cloudflare! I set "Bypass cache" on a page rule and it seems to work well.

Related

How do I tackle this issue in my Guess The Flag Django web app

I'm completely new to Django and I'm trying to build this guess the flag web game with it. In main page when someone presses the 'play' button, he's sent to a page where a list of 4 randomly selected countries from the DB is generated, and only of one these 4 countries is the actual answer.
Here's the code from views.py in my App directory :
context = {}
context['submit'] = None
context['countries_list'] = None
score = []
score.clear()
context['score'] = 0
def play(request):
len_score = len(score)
countries = Country.objects.all()
real_choice = None
if request.POST:
get_guess = request.POST.get('guess')
print(request.POST)
if str(get_guess).casefold() == str(context['submit']).casefold():
score.append(1)
else:
score.clear()
len_score = len(score)
choices = random.sample(tuple(countries),4)
real_choice = random.choice(choices)
context['countries_list'] = choices
context['submit'] = real_choice
context['score'] = len_score
return render (request, 'base/play.html', context)
Everything works as expected when there's only one person playing, or the site is opened in only one tab.
The issue here is one someone else opens the site or it's opened in more than one tab, the score gets reset and a new list of random countries is generated for all users, so your guess will never be right!
How do I go about to solve this? Again, I'm completely new to this so I'm left clueless here.
The best way to store short term, per user information is by session variables. This way you can maintain the user's individual settings. So, something like:
def play(request):
len_score = len(score)
countries = Country.objects.all()
real_choice = None
if request.POST:
get_guess = request.POST.get('guess')
print(request.POST)
#Compare guess to session variable
#(using .get() which returns none rather than error if variable not found)
if str(get_guess).casefold() == str(request.session.get('real_choice').casefold():
score.append(1)
else:
score.clear()
len_score = len(score)
choices = random.sample(tuple(countries),4)
real_choice = random.choice(choices)
#Store answer in session variable
request.session['real_choice'] = real_choice
context['countries_list'] = choices
context['submit'] = real_choice
context['score'] = len_score
return render (request, 'base/play.html', context)

Flask wtforms AttributeError: 'HTMLString' object has no attribute 'paginate'

Somebody has helped me with some great code here to show the same form multiple times each with a submit button, it works a treat, But as I will have hundreds of forms I need to paginate the page, I have been able to paginate pages in the past but I dont no how to use that code with a form in a for loop.
here is my code:(with lots of help from Greg)
#bp.route('/stock', methods=['GET', 'POST'])
#bp.route('/stock/stock/', methods=['GET', 'POST'])
#login_required
def stock():
stocks = Stock.query.all()
forms = []
for stock in stocks:
form = AddStockForm()
form.id.default = stock.id
form.image.default = stock.image_url
form.date.default = stock.date
form.description.default = stock.description
form.event.default = stock.event
form.achat.default = stock.achat
form.vente.default = stock.vente
form.sold.default = stock.sold
forms.append(form)
for form in forms:
if form.validate_on_submit():
if form.modify.data:
stock = Stock.query.filter_by(id=form.id.data).one()
stock.date = form.date.data
stock.description = form.description.data
stock.event = form.event.data
stock.achat = form.achat.data
stock.vente = form.vente.data
stock.sold = form.sold.data
db.session.add(stock)
db.session.commit()
elif form.delete.data:
stock = Stock.query.filter_by(id=form.id.data).one()
db.session.delete(stock)
db.session.commit()
return redirect(url_for('stock.stock'))
form.process() # Do this after validate_on_submit or breaks CSRF token
page = request.args.get('page', 1, type=int)
forms = forms[1].id().paginate(
page, current_app.config['ITEMS_PER_PAGE'], False)
next_url = url_for('stock.stock', page=forms.next_num) \
if forms.has_next else None
prev_url = url_for('stock.stock', page=forms.prev_num) \
if forms.has_prev else None
return render_template('stock/stock.html',forms=forms.items, title=Stock, stocks=stocks)
I am trying to use the fact "forms" is a list to paginate the results, I obviously dont understand how to do this, I have looked at flask-paginate but I didnt understand that either!
all help is greatly needed
Warm regards, Paul.
EDIT
I have tried to use flask_pagination, here is my code:
#bp.route('/stock/stock/', methods=['GET', 'POST'])
#login_required
def stock():
search = False
q = request.args.get('q')
if q:
search = True
page = request.args.get(get_page_parameter(), type=int, default=1)
stocks = Stock.query.all()
forms = []
#rest of code here#
pagination = Pagination(page=page, total=stocks.count(), search=search, record_name='forms')
form.process() # Do this after validate_on_submit or breaks CSRF token
return render_template('stock/stock.html',forms=forms, title=Stock, pagination=pagination)
This gives a different error "TypeError: count() takes exactly one argument (0 given)" I also tried with "total=forms.count()" and got the same error!
I hate doing this as it shows a lack of patience at the begining but this answer may help others, I solved my problem in two ways the first was the query which decides the order of display (descending or ascending) this then allowed me to use flask-paginate to display the results on several pages, I realised that I was dealing with a list, and the example by one of the developers link showed me the way, here is my code,
from flask_paginate import Pagination, get_page_args
#bp.route('/stock', methods=['GET', 'POST'])
#bp.route('/stock/stock/', methods=['GET', 'POST'])
#login_required
def stock():
stocks = Stock.query.order_by(Stock.id.desc())# this gives order of results
forms = []
def get_forms(offset=0, per_page=25): #this function sets up the number of
return forms[offset: offset + per_page] #results per page
for stock in stocks:
form = AddStockForm()
form.id.default = stock.id
form.image.default = stock.image_url
form.date.default = stock.date
form.description.default = stock.description
form.event.default = stock.event
form.achat.default = stock.achat
form.vente.default = stock.vente
form.sold.default = stock.sold
forms.append(form)
for form in forms:
if form.validate_on_submit():
if form.modify.data:
stock = Stock.query.filter_by(id=form.id.data).one()
stock.date = form.date.data
stock.description = form.description.data
stock.event = form.event.data
stock.achat = form.achat.data
stock.vente = form.vente.data
stock.sold = form.sold.data
db.session.add(stock)
db.session.commit()
elif form.delete.data:
stock = Stock.query.filter_by(id=form.id.data).one()
db.session.delete(stock)
db.session.commit()
return redirect(url_for('stock.stock'))
#this is the code from the link that I used to paginate
page, per_page, offset = get_page_args(page_parameter='page',
per_page_parameter='per_page')
total = len(forms) # this code counts the resulting list to be displayed
pagination_forms = get_forms(offset=offset, per_page=per_page)
pagination = Pagination(page=page, per_page=per_page, total=total)
form.process() # Do this after validate_on_submit or breaks CSRF token
return render_template('stock/stock.html', title=Stock, stocks=stocks
page=page, forms=pagination_forms, per_page=per_page, pagination=pagination)
#And finally this is the pagination passed to the html
so this for all those numptys like me who struggle with everything but still love it.

Read user uploaded csv file and store data in Django modele live on heroku

This is my firts question here. so I would like to thank you for your help.
I have a Django School management app. and i would like the user to be able to read csv file and store in database with specific header.
My code runs locally very well. but I recently push it on heroku so that I can test it. I may note that all static assets are stored on Amazon s3. and it works.
but when I try to read a csv file, I get an Internal server error.
here is my code to store Pupils.
def convert_header(csvHeader):
cols = [ x.replace(' ', '_').lower() for x in csvHeader ]
return cols
def import_data(request):
if request.method == 'GET':
return render(request, 'school/import.html')
if request.method == 'POST' and request.FILES['myfile']:
if request.POST.get("object") == '':
message = 'You may chose an object'
return render(request, 'school/import.html', {'message': message })
if request.POST.get("object") == 'Pupil':
myfile = request.FILES['myfile']
fs = FileSystemStorage(location='eSchool/media/documents')
filename = fs.save(myfile.name, myfile)
uploaded_file_url = fs.path(filename)
data = csv.reader(open(uploaded_file_url), delimiter=',')
header = next(data)
header_cols = convert_header(header)
i = 0
k = 0
for row in data:
pupil = Pupil()
for k in range(len(header_cols)):
row_item = row[k].split(',')
for item in row_item:
key = header_cols[k]
if key == 'responsible':
item = Responsible.objects.get(pk= int(item))
print(item.first_name)
setattr(pupil, key, item)
else:
setattr(pupil, key, item)
k +=1
pupil.save()
i = i + 1
detailed = 'Sucessfully created '+ str(i) + ' Pupils '
return render(request, 'school/import_success.html', {'detailed' : detailed })
I would like to store data in a modele called Document. I create it. and try it I still get the error. Please help.
I find the solution of that problem. I first create a Django model to store the url of the uploaded file. like that:
class Document(models.Model):
uploaded_at = models.DateTimeField(auto_now_add=True)
upload = models.FileField(upload_to='documents')
Obvously I had to configure MEDIA_ROOT and MEDIA_URL
I configure the settings file to store the reference of all media assets and all static file on Amazon s3.
So instead of using the class FileSystemStorage. I install with pip a package named "requests"
pip install requests
and I import requests,csv and codecs.
import requests
import codecs
After that my view I just make litle modification tm my function.
def import_data(request):
if request.method == 'GET':
return render(request, 'school/import.html')
if request.method == 'POST' and request.FILES['myfile']:
if request.POST.get("object") == '':
message = 'You may chose an object'
return render(request, 'school/import.html', {'message': message })
if request.POST.get("object") == 'Pupil':
myfile = request.FILES['myfile']
doc = Document()
doc.upload = myfile
doc.save()
print(doc.upload.url)
if settings.DEBUG == True: # execute that block locally
data = csv.reader(open(doc.upload.path), delimiter=',') # locally the file is red frm the path so i us doc.upload.path
else: #this block is executed in production. So the file is red from the url. so I use doc.upload.url
rep = requests.get(doc.upload.url)
text = codecs.iterdecode(rep.iter_lines(), 'latin_1') #codecs is use to decode the lines. I don't really master how it works.
data = csv.reader(text, delimiter = ',')
header = next(data)
header_cols = convert_header(header)
i = 0
k = 0
for row in data:
pupil = Pupil()
for k in range(len(header_cols)):
row_item = row[k].split(',')
for item in row_item:
key = header_cols[k]
if key == 'responsible':
item = Responsible.objects.get(pk= int(item))
#print(item.first_name)
setattr(pupil, key, item)
else:
setattr(pupil, key, item)
k +=1
pupil.save()
i = i + 1
detailed = 'Sucessfully created '+ str(i) + ' Pupils '
return render(request, 'school/import_success.html', {'detailed' : detailed })
So right now. everything work well. If you see something that i can improve about this code, I Already thank you for your help!

How to resize images before uploading them in Flask?

I have four images i want to upload them, but their sizes are very big it takes long time to upload them into the site.
I want to resize each one of them, i wrote a small route that handle the request:
#team_route.route('/team/dashboard/add/product', methods=['GET', 'POST'])
#requires_auth
#master_login_required(role='master')
def team_add_product():
form = AddProduct()
imagesList = []
size = 1024, 764
if request.method == 'POST' and form.is_submitted():
product = Goods()
file = request.files.getlist("image[]")
if file:
for zipfile in file:
fi = Image.open(BytesIO(zipfile.stream.read()))
im.thumbnail(size)
img2 = im.rotate(-90, expand=True)
img2.seek(0)
img2.save(UPLOAD_FOLDER + '/crops/' + zipfile)
When i hit upload it gives me this error:
AttributeError: 'JpegImageFile' object has no attribute 'read'
Please any help solving this ?
You have to remove .stream
zipfile.read() its the only you need.

Create a container object, then add an image object into that container object after it has been created

form_image_container = ImageContainerForm(request.POST or None, request.FILES or None)
if form_image_container.is_valid():
instance_container = form_image_container.save(commit=False)
instance_container.user = request.user
instance_container.save()
return redirect(# redirect to the recently created canvas page)
# somehow need to create image container first, then add the a image into the image container
form_image = ImageForm(request.POST or None, request.FILES or None)
if form_image.is_valid():
instance_image = form_image.save(commit=False)
instance_image.user = request.user
instance_image.image_container = instance_container
instance_image.image = obj.image
instance_image.save()
Is there a way to create the first object (container for images) and then add the second object (image) into the first (container for images)
Combinging two forms into one always leads to complications. But your trouble is because you are returning halfway through!! please change your code as follows
form_image_container = ImageContainerForm(request.POST,request.FILES) # or None is redundant
if form_image_container.is_valid():
instance_container = form_image_container.save(commit=False)
instance_container.user = request.user
instance_container.save()
form_image = ImageForm(request.POST, request.FILES or None)
if form_image.is_valid():
instance_image = form_image.save(commit=False)
instance_image.user = request.user
instance_image.image_container = instance_container
instance_image.image = obj.image
instance_image.save()
return redirect(# redirect to the recently created canvas page)
# somehow need to create image container first, then add the a image
return render(request, form_image_container)
Note that this still wont work unless your ImageContainerForm has fields that overlap with ImageForm which makes the second form pretty much redundant anyway!
I think you may probably be looking for Formsets, or breaking up the process into two different forms.