I'm working on a project using flask and pymongo where I have a movies collection and an instance of my movies collection is like :
movie = {"title":"Hobbit" , "year":"2013"}
I have an html jinja 2 page where I submit a form and I can change the title or the year of the movie . It is not required to change both of them .
My html template :
<h1> Select a field in the movie to update </h1>
<form action = "{{url_for('execute_movie_update')}}" method = "post">
<label="update-title">Change the title</label>
<input type = "text" id="new-title" name = "new-title" >
<label="update-year"> Change year of publish </label>
<input type = "text" id = "new-year" name = "new-year">
<label="update-plot"> Change plot of movie </label>
<input type = "text" id = "new-plot" name = "new-plot">
<button type = "submit">Submit changes</button>
</form>
If I use my flask endpoint which I will include below to change both the title and the year of the movie I have in my session the update is succesful . However if I change only one field
(ex. if I change the title only the year I have is set to " " instead of being the orginal year )
I have my flask endpoint here . It updates a specific movie I have stored in a session :
#app.route('/executemovieupdate' , methods = ['GET', 'POST'])
def execute_movie_update():
if 'Email' in session and 'User' in session:
email = session['Email']
user = session['User']
if user == 'Admin':
if 'Movie' in session and 'Movie_year' in session and 'Movie_plot' in session:
movie = session['Movie']
year = session['Movie_year']
plot = session['Movie_plot']
tainia = movies.find_one({'title':movie , "year":year}) #the movie that I want to update
if request.method == 'POST':
new_title = request.form.get('new-title') #get the fields I submitted in html
new_year = request.form.get('new-year')
if new_title!=None: #if I submit only title year is set to " "
print("update the title")
movies.update_one({"title":movie , "year":year} , {"$set":{"title":new_title} } )
session['Movie'] = new_title
movie = session['Movie']
print(session)
if new_year != None: #if i submit both title and year the program works
print("update the year")
movies.update_one({"title":movie , "year":year} , {"$set": {"year":new_year}})
session['Movie_year']=new_year
year = session['Movie_year']
print(session)
return ''' movie has been updated '''
else:
return render_template('movie-update.html' , movie = tainia)
else:
return redirect(url_for('admin.html'))
else:
return redirect(url_for('login'))
else:
return redirect(url_for('login'))
I would appreciate your help in guiding me to solve this issue . Thank you in advance.
EDIT: It seems that the problem lies in my if statements as I always enter both of them even if an item is not submitted (ex. I did not submit a title )
SOLVED :
by changing title if statement to : if new_title: and year statement to if new_year:
Related
When a user click <button> on index page, can save data immediately?
views.py
def index(request):
...
...
context = { ... }
response = render(request, 'docu/index.html', context)
### Save visitors' IP and Keywords to KeyLOG table ###
ip = request.META.get('REMOTE_ADDR')
keywords = request.META.get('HTTP_REFERER', '')
keylog = KeylogForm(request.GET)
keylog = keylog.save(commit=False)
keylog.ipaddr = ip
keylog.keyquery = keywords
keylog.svae()
return response
def keylog(request):
if request.method == "GET":
keylog_list = list(Keylog.objects.all().order_by()\
.values('idpaddr', 'keywords', 'tel_log', 'regtime'))
return JsonResponse(keylog_list, safe=False)
return JsonResponse({'message':'error'})
forms.py
class KeylogForm(forms.ModelForm):
class Meta:
model = Keylog
fields = ['ipaddr','keyquery','tel_log']
models.py
class Keylog(models.Model):
ipaddr = models.CharField(max_length=32,null=True, blank=True) #save IP addr
keyquery = models.CharField(max_length=128,null=True, blank=True) #save keywords
tel_log = models.CharField(max_length=256,null=True, blank=True) #save tel_log
regtime = models.DateTimeField(auto_now_add=True) #created time
Currently, it makes table as below;
Ipaddr
keyquery
tel_log
regtime
192.168.x.yy
?query=apple
2023.01.01.12.24.56
192.168.a.bb
?query=banana
2023.01.01.11.22.33
I hope to save Tel_log data in the above table, when a user clicks <butto>...</button>.
So I made an index.html as below;
index.html
<div id="tel_log">
<button class="btn btn-danger">
hx-get="keylog/"
hx-vals="[tel_log='CALL']"
hx-trigger="click">
Phone NUMBER
</button>
</div>
My expectation is that when I clicked [Phone NUMBER] on the index page, "CALL" is appeared on the "tel_log" column of the above table, but it didn't work, just shows {message:error}
I'm a newbie on programming, so my approach for my needs may be wrong, but I'm really eager to make a web site for now, especially on this question. I also did try to use Ajax, but I didn't get any response also.
Thank you for all.
Click [button], save data in real time on the column of the table.
I'm trying to update the values of my database using a HTML Form.
When I Click Edit it brings me to the edit the values above.
However as I am clicking the submit button, it returns me a database but with no other values.
Is there anyone that can help me understand what I did wrong and point me to the right documentation (if any)
editclaims.html:
<div class="arrange2">
<h1>Edit Claim Form - #{{claims.id}} </h1>
</div>
<form method="POST" action="/update/{{claims.id}}">
{% csrf_token %}
views.py:
def editclaims(request,id):
context = initialize_context(request)
user = context['user']
claims = SaveClaimForm.objects.get(id=id)
if request.method == 'POST':
name = request.POST['name']
email = request.POST['email']
claim = request.POST['claim']
claimtype = request.POST.get('claimtype')
description = request.POST['description']
receipt = request.FILES['receipt']
cheque = request.POST.get('Cheque')
form = SaveClaimForm(name=name, email=email, claim=claim, claimtype=claimtype, description=description, receipt=receipt, cheque=cheque)
form.save()
return render(request, "Login/editclaims.html", {'claims':claims, 'user':user})
urls.py:
urlpatterns = [
path('existingclaims/', views.viewclaims, name='existingclaims'),
path('editclaims/<int:id>', views.editclaims, name='editclaims'),
path('update/<int:id>', views.updateclaims, name='updateclaims'),
]
It may not resolve all your problems but it will be more readable as answer.
When you get data from HTML then you create new object SaveClaimForm and it will have new ID and you will have the same object in two rows.
You have to get original Claim from database and update values in this object and save it - and then it will save it with original ID and you will have only one `object in database
def editclaims(request,id):
context = initialize_context(request)
user = context['user']
# get original object
claims = SaveClaimForm.objects.get(id=id)
if request.method == 'POST':
# update original object
claims.name = request.POST['name']
claims.email = request.POST['email']
claims.claim = request.POST['claim']
claims.claimtype = request.POST.get('claimtype')
claims.description = request.POST['description']
claims.receipt = request.FILES['receipt']
claims.cheque = request.POST.get('Cheque')
# save it with original `ID`
claims.save()
return render(request, "Login/editclaims.html", {'claims':claims, 'user':user})
BTW:
Django has special class ModelForm to create forms in HTML. It may also have methods to check if data in HTML are correct - ie. if fields are not empty, if email is correctly constructed (name#domain.com), if phone has only numbers, etc. So using ModelForm can be more useful then writing all manually in code.
<form method = "POST">
{% csrf_token %}
<div class = "lookback"
<label for = "time"></label>
<select name = "time" id = "time">
<option value = "today">Today</option>
<option value = "yesterday">Yesterday</option>
<option value = "lastweek">Last Week</option>
<option value = "lastmonth">Last Month</option>
<option value = "lastyear">Last Year</option>
<option value = "forever">Forever</option>
</select>
<button><a type= "button" class = "Look" id = "look" href = "">Look Back?!</a></button>
</form>
** Above is the portion of HTML page i am using to get the select value so that I can use it in my views.py to filter out the data and output it on another page. **
def retro(request):
if request.method == "POST":
time = ThoughtForm(request.POST)
today = timezone.now().date()
if time == "Yesterday":
yesterday = timezone.now().date() - timedelta(days=1)
data = Thought.objects.filter(date__gte=yesterday, date__lt=today)
elif time == "Last Week":
week = timezone.now().date() - timedelta(days=7)
data = Thought.objects.filter(date__gte=week, date__lt=today)
elif time == "Last Month":
month = timezone.now().date() - timedelta(days=30)
data = Thought.objects.filter(date__gte=month, date__lt=today)
elif time == "Last Year":
year = timezone.now().date() - timedelta(days=365)
data = Thought.objects.filter(date__gte=year, date__lt=today)
elif time == "Forever":
data = Thought.objects.all
else:
data = Thought.objects.filter(date__gte=today, date__lt=today)
return render(request,'look.html', {'data' : data})
else:
return render(request, 'retro.html')
When I use the submit button of the retro.html (the one with the select input), it does direct me to the page I want to output my data to. However, it does not print the data in the page.
<div class = "results">
<form method = "GET" >
<h1>Till now, you thought of writing- </h1>
<table class="table">
<thead class = "thead-dark">
<tr>
<td>Thought</td>
<td>Date</td>
</tr>
</thead>
<tbody>
{% for obj in data%}
<tr>
<td scope="row">{{ obj.thought }}</td>
<td>{{ obj.date.date}}</td>
</tr>
{% endfor %}
Directly above is the html for the page I am trying to output my data in.
from django.db import models
from django.utils import timezone
# Create your models here.
class Thought(models.Model):
thought = models.CharField( max_length=300)
done = models.BooleanField(default = False)
#date = models.models.DateField()
date = models.DateTimeField(default=timezone.now)
def __str__(self):
return str(self.thought) + "- " + str(self.done)
Directly above is my models.py file.
from django import forms
from retrospection.models import Thought
class ThoughtForm(forms.ModelForm):
class Meta:
model = Thought
fields = ['thought', 'done', 'date']
Directly above is my form.py file.
After trying what Blackeagle52 suggested and by printing(form.is_valid()), I found out that my form is failing the is_valid test with an error -
<ul class="errorlist"><li>thought<ul class="errorlist"><li>This field is required.</li></ul></li><li>date<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
My form contains a select item with a few options and a submit button. I am not sure how the field can be empty. Please help me.
def retro(request):
if request.method == "POST":
form = ThoughtForm(request.POST)
if form.is_valid():
time = form.cleaned_data['time']
today = timezone.now().date()
if time == "Yesterday":
yesterday = timezone.now().date() - timedelta(days=1)
data = Thought.objects.filter(date__gte=yesterday, date__lt=today)
elif time == "Last Week":
week = timezone.now().date() - timedelta(days=7)
data = Thought.objects.filter(date__gte=week, date__lt=today)
elif time == "Last Month":
month = timezone.now().date() - timedelta(days=30)
data = Thought.objects.filter(date__gte=month, date__lt=today)
elif time == "Last Year":
year = timezone.now().date() - timedelta(days=365)
data = Thought.objects.filter(date__gte=year, date__lt=today)
elif time == "Forever":
data = Thought.objects.all
else:
data = Thought.objects.filter(date__gte=today, date__lt=today)
return render(request,'look.html', {'data' : data})
# TODO Do something when form is invalid.
# Maybe just removing the else below this, so you'll get retro.html
else:
return render(request, 'retro.html')
To explain my solution, in your code time was not a value, but a Form.
To retrieve the data from the form, you first need to check whether the form is valid (is_valid()) and than retrieve the value from form.cleaned_data.
There are some other minor things you could improve. After a POST, redirect to another view, as now this look.html is only visible on a POST. If you want to refresh that page or something, you'll be back at retro.html.
With the Forever-case, you are also missing () on your .all() call.
I am developing an angularjs app on google app engine however I have an issue displaying one of my KeyProperty fields client.
This is how the KeyProperty field is displaying (The other non KeyProperty elements are showing up fine);
NB: Post is 200 OK
model.py
class Project(ndb.Model):
projectID = ndb.IntegerProperty(required=True)
title = ndb.StringProperty(required=True)
description = ndb.StringProperty(required=True)
startAt = ndb.DateTimeProperty(indexed=True)
endAt = ndb.DateTimeProperty()
client = ndb.KeyProperty(kind='Client')
class Client(ndb.Model):
name = ndb.StringProperty()
home.html element
<div class="form-group">
<label>Client : </label>
<input type="text" ng-model="Project.Client" class="form-control"/>
</div>
In handler.py
def post(self):
r = json.loads(self.request.body)
print str(r)
cl = Client(name=r['Client']).put()
client_key = cl
g = Project(projectID=int(r['ProjectID']),
client=client_key,
description=r['Description'],
title=r['Title'],
startAt=datetime.strptime(r['StartAt'], '%Y-%m-%dT%H:%M:%S.%fZ'),
endAt=datetime.strptime(r['EndAt'], '%Y-%m-%dT%H:%M:%S.%fZ'))
project_key = g.put()
angular
eventClick: function (project) {
$scope.showSelected = true;
var fromDate = moment(project.start).format('DD/MM/YYYY LT');
var endDate = moment(project.end).format('DD/MM/YYYY LT');
$scope.Project = {
ProjectID : project.projectID,
Client : project.client,
Title : project.title,
Description: project.description,
Employees: project.employees,
StartAt : fromDate,
EndAt : endDate,
IsFullDay : false
}
$scope.ShowModal()
},
I am still learning my way to handling KeyProperties. Thanks for the help
UPDATE
This is the error I am getting when I type 'qwert' in the Client field;
I am talking about in your Python code. The error log should tell you what line of your code was expecting a string. That is where you need the .name
I'm currently working through the O'Reilly book, Programming Python. Below is code that reads a shelve and creates a web interface allowing you to access those values from the shelf. You can fetch and update the values
'''
Implement a web-based interface for viewing and updating class instances
stored in a shelve; the shelve lives on server (same machine if localhost)
'''
import cgi, sys, os
import shelve, html
shelvename = 'class-shelve'
fieldnames = ('name', 'age', 'job', 'pay')
form = cgi.FieldStorage()
print('Content-type: text/html')
sys.path.insert(0, os.getcwd())
# main html template
replyhtml = """
<html>
<title>People Input Form</title>
<body>
<form method=POST action=peoplecgi.py>
<table>
<tr><th>key<td><input type=text name=key value="%(key)">
$ROWS$
</table>
<p>
<input type=submit value="Fetch", name=action>
<input type=submit value="Update", name=action>
</form>
</body></html>
"""
# insert html for data rows at $ROWS$
rowhtml = '<tr><th>%s<td><input type=text name=%s value="%%(%s)s">\n'
rowshtml = ""
for fieldname in fieldnames:
rowshtml += (rowhtml % ((fieldname,)*3))
replyhtml = replyhtml.replace('$ROWS$', rowshtml)
def htmlize(adict):
new = adict.copy()
for field in fieldnames:
value = new[field]
new[field] = html.escape(repr(value))
return new
def fetchRecord(db, form):
try:
key = form['key'].value
record = db[key]
fields = record.__dict__
fields['key'] = key
except:
fields = dict.fromkeys(fieldnames, '?')
fields['key'] = 'Missing or invalid key!'
return fields
def updateRecord(db, form):
if not 'key' in form:
fields = dict.fromkeys(fieldnames, '?')
fields['key'] = 'Missing key input!'
else:
key = form['key'].value
if key in db:
record = db[key]
else:
from person_start import Person
record = Person(name='?', age='?')
for field in fieldnames:
setattr(record, field, eval(form[field].value))
db[key] = record
fields = record.__dict__
fields['key'] = key
return fields
db = shelve.open(shelvename)
action = form['action'].value if 'action' in form else None
if action == 'Fetch':
fields = fetchRecord(db, form)
elif action == 'Update':
fields = updateRecord(db, form)
else:
fields = dict.fromkeys(fieldnames, '?')
fields['key'] = 'Missing or invalid action!'
db.close()
print(replyhtml % htmlize(fields))
However, for some reason, printing is continually failing. I've tried many times to remove the "" the error is stating, but to no avail.
Does anyone see why this is failing to print the form?
After checking the complete code , I believe the issue is in the replyhtml , in line -
<tr><th>key<td><input type=text name=key value="%(key)">
The issue is in the format - "%(key)" . You need to specify the type of the element like s or d etc , I believe you may need s (for string). Example -
<tr><th>key<td><input type=text name=key value="%(key)s">