Flask: updating existing data from database - python

For the following model and route, how can I get the page to display the existing data in the database field, in an editable box, with a ‘save changes’ button.
# MODEL
class Task(db.Model): #inherits from db>Model
__tablename__ = "Tasks"
id = db.Column(db.Integer, primary_key=True)
# datetime, need to work this out
Title = db.Column(db.String(4096))
Status = db.Column(db.String(4096))
Description = db.Column(db.String(4096))
Priority = db.Column(db.String(4096))
Assigned_To = db.Column(db.String(4096))
# ROUTE:
#app.route("/<int:task_id>/edit")
def _edit(task_id):
task = Task.query.get_or_404(task_id)
return render_template('update.html',task=task)
<—- update.html—->
<form method="POST" action="">
<fieldset>
{{ task.Title }}
{{ task.Description }}
</fieldset>
</form>
I would prefer not to define Forms if possible. I have an SQL Alchemy database configured and working.
The routes added to the end of this question are working fine - adding new tasks (rows) to the database and creating individual pages to view individual tasks (rows):
#app.route("/tasks", methods=['GET', 'POST'])
def new_post():
if request.method == "GET":
return render_template("tasks.html")
data = Task(
Title=request.form["title"],
Description=request.form["description"],
Status=request.form["status"],
Priority=request.form["priority"],
Assigned_To=request.form["assigned"],
)
db.session.add(data)
db.session.commit()
return redirect(url_for('index'))
#app.route("/<int:task_id>")
def qtsk(task_id):
task = Task.query.get_or_404(task_id)
return render_template('indtask.html',task=task)
I’m new to flask, I would greatly appreciate any help.

I was watching a Flask tutorial recently and this is how he deals with this situation:
#app.route("/post/<int:post_id>/update", methods=["GET", "POST"])
#login_required
def update_post(post_id):
post = Post.query.get_or_404(post_id)
if (post.author != current_user):
abort(403)
form = PostForm()
if form.validate_on_submit():
post.title = form.title.data
post.content = form.content.data
db.session.commit()
flash('Your post has been updated!', 'success')
return redirect(url_for('post', post_id=post_id))
elif request.method == 'GET':
form.title.data = post.title
form.content.data = post.content
return render_template('create_post.html', title="Update Post",
form=form, legend='Update Post')
What it does is it checks if the user is submitting the updated title or content using POST method. If the user is then just update the field and then commit it. Otherwise return the template with the title and content field in the form being filled with the current fields in the Post object.
Here's link for that tutorial (this part starts at 25:35): https://youtu.be/u0oDDZrDz9U
Hope it helps.

I was asking a simillar question.
I think you can update by using form. It doesnt have to be presented as a form to the user. You can write an html template that would not lokk like a form,but youneed a form to fetch data. Maybe you can use javascript and than you can skip the usage of form. I am on to remember javascrip an use it for my app.

Related

Django form errors appearing without error

I made a research here in Stack and my problem is the opposite of the majority, I saw some ways to make it appear, but my problem is that it's appearing when the user hits the "Register" button / Refresh the register page. So it's an annoying thing that appears wherever the user enter/refresh the page because the form is empty.
View.py
#unauthenticated_user
def register(request):
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if request.method == 'POST':
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if form_u.is_valid() and form_c.is_valid():
user = form_u.save()
group = Group.objects.get(name='func')
user.groups.add(group)
client = form_c.save(commit=False)
client.user = user
client.save()
return redirect('login')
else:
form_u = CreateUser()
form_c = CreateClient()
context = {'form_u': form_u, 'form_c': form_c}
return render(request, 'register.html', context)
HTML
<form method="POST" action="" id="ativa">
{% csrf_token %}
...
</form>
{{form_u.errors}}
{{form_c.errors}}
<div class="mt-4">
<div class="d-flex justify-content-center links">
Have an account ? Login
</div>
</div>
Print
P.S: The site is in portuguese, but I can share the form link in heroku
Your logic is opposite of what you want:
Initialize the forms with POST data regardless of whether the request is a POST or a GET request, which will result in the errors if there is no POST data.
Then you initialize empty forms when the form data is invalid.
Instead you'll want to pass POST data only if the request is a POST request, and you should initialize empty forms only if the request is not a POST request:
#unauthenticated_user
def register(request):
# If request is POST, validate forms and add objects.
if request.method == 'POST':
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if form_u.is_valid() and form_c.is_valid():
user = form_u.save()
group = Group.objects.get(name='func')
user.groups.add(group)
client = form_c.save(commit=False)
client.user = user
client.save()
return redirect('login')
# We can remove the else statement here,
# because the function either redirects or resumes
# normal flow and renders the template
# with the form errors.
else:
# Only initialize empty forms when no POST request was made.
form_u = CreateUser()
form_c = CreateClient()
context = {'form_u': form_u, 'form_c': form_c}
return render(request, 'register.html', context)

Calling a Django-Rest API from a Django Form

I built a Django-Rest API with an APIView that uploads a file to a folder of a web server.
This API is working with Postman as shown in the pictures below:
Now, I am working on calling this API from the below HTML form:
Issue I am facing: the file sent via the form returns the following error:
"file": [
"No file was submitted."
]
Probably something related with the binding of the file as the file is uploaded in the form but not sent to the API.
Below the code of my application:
index.html
<form action="/file/upload/" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input id="audio_file" type="file"/>
<input type="submit" value="Upload File" name="submit"/>
</form>
views.py
class IndexView(TemplateView):
template_name = "index.html"
log = logging.getLogger(__name__)
log.debug("Debug testing")
def post(self, request): # TODO: fix, empty file error returned after calling post method
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
# https://docs.djangoproject.com/en/2.2/ref/forms/api/#binding-uploaded-files
form = FileForm(request.POST, request.FILES)
# check whether it's valid:
if form.is_valid():
instance = form.save(commit=False)
instance.save()
# redirect to the same URL:
return HttpResponseRedirect('/App/index/')
# if a GET (or any other method) we'll create a blank form
else:
form = FileForm()
return render(request, 'index.html', {'form': form})
class FileView(views.APIView):
parser_classes = (MultiPartParser, FormParser)
def post(self, request):
'''This method is used to Make POST requests to save a file in the media folder'''
file_serializer = FileSerializer(data=request.data)
if file_serializer.is_valid():
file_serializer.save()
return Response(file_serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
models.py
from django.db import models
class FileModel(models.Model):
file = models.FileField()
timestamp = models.DateTimeField(auto_now_add=True)
forms.py
from django.forms import ModelForm
from App.models import FileModel
class FileForm(ModelForm):
# Creating a form that maps to the model: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/
class Meta:
model = FileModel
fields = ['file']
Below the documentation I have already consulted without success:
https://docs.djangoproject.com/en/2.2/topics/http/file-uploads/
https://docs.djangoproject.com/en/2.2/topics/forms/
These are the stackoverflow questions I already read without finding a solution to the issue:
Django Contact Form Attachment showing 'This field is required.' What am I doing Wrong?
Django calling REST API from models or views?
Django HTML Form Send Attachment Emails
Post to django rest framework
Complete code repository: https://github.com/marcogdepinto/Django-Emotion-Classification-Ravdess-API .
EDIT: I changed the if statement inside IndexView.post as follows
if form.is_valid():
instance = form.save(commit=False)
instance.save()
Now the request is OK but the file passed is empty
HTTP 201 Created
Allow: POST, DELETE, OPTIONS
Content-Type: application/json
Vary: Accept
{
"file": null,
"timestamp": "2019-08-16T06:15:58.882905Z"
}

Django rendering correct view but incorrect URL

I'm creating a video sharing application. On my video page, I have allowed users to post comments and to delete comments.
def video_content(request, video_id):
video = get_object_or_404(Video, pk=video_id)
....
return render(
request,
'video-content.html',
context={
'video': video,
}
)
I'm obviously omitting a lot of things in the code.
I also have a comment handler function
def add_comment(request, video_id):
video = get_object_or_404(Video, pk=video_id)
if request.method == 'POST' and request.user.is_authenticated:
# Get comment and save it
return HttpResponse()
On my video page, I have a form:
<form action="/comment/add/{{video.id}}" method="post">
<input type="text"></input>
<button type="submit">Comment</button>
</form>
All of this works fine. When the user inputs a comment and submits the form, the add_comment function is successfully called just like its supposed to, and the comment is saved. The video page does not reload, which is what I want, but the URL on the top bar changes. How can I prevent that from happening?
All you need to do, is redirect the user to the view that you want.
def add_comment(request, video_id):
video = get_object_or_404(Video, pk=video_id)
if request.method == 'POST' and request.user.is_authenticated:
# Get comment and save it
return redirect("video_content",video.id)
# return HttpResponse() # this, is not correct
return render(request,"template_name.html",{})

Django Page not found (404) Request Method: No Sales matches the given query [duplicate]

This question already has answers here:
Django: get() returned more than one items -- it returned 3
(4 answers)
Closed 5 years ago.
My python code keeps returning the following error:
Page not found (404) Request Method: GET Request URL:
http://127.0.0.1:8000/geo_gas/edit_sale/ Raised by:
geo_gas.views.edit_sale No Sales matches the given query.
class Sales(models.Model):
gas_qty = models.CharField(max_length=20)
amount = models.CharField(max_length=20)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a string representation of the model."""
return self.gas_qty
class Meta:
verbose_name_plural = 'Sales'
View.py
def edit_sale(request):
"""Edit an existing sales record."""
entry = get_object_or_404(Sales, pk=1)
if request.method != 'POST':
form = SalesForm(instance=entry)
else:
form = SalesForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('geo_gas:sales'))
context = {'entry': entry, 'form': form}
return render(request, 'ht/edit_sale.html', context)
Urls.py
.......
# Page for editing a sale entry
path('edit_sale/', views.edit_sale, name='edit_sale'),
.......
ht/templates/ht/edit_sale.html
Edit entry:
<form action="{% url 'geo_gas:edit_entry' %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">save changes</button>
</form>
I have not been able to identify what part of the code is causing the error to occur.
The error is saying that no Sales instance with pk=1 exists, as the error is likely thrown by get_object_or_404(Sales, pk=1). So you might want to check if that's really the case.
You can try checking the pk of your instance by doing Sales.objects.first().pk to see.
Update: How to make it dynamic
Before going into that, it might be useful to understand what RESTful API endpoints are like. But briefly in this context you might want to have something like this
# urls.py
path('sale/', views.list_sale, name='list_sale'),
path('sale/<int:pk>/', views.retrieve_update_delete_sale, name='retrieve_update_delete_sale') # names of the view can be whatever you want
What happens here is that the pk argument is passed from the URL (<int:pk>) to the view as an argument in the function-based view.
Accessing the arguments passed from URLs in your view
def retrieve_update_delete_sale(request, pk): # <-- you will be able to access it here
"""Edit an existing sales record."""
entry = get_object_or_404(Sales, pk=pk) # this is now dynamic!
if request.method != 'POST':
form = SalesForm(instance=entry)
else:
form = SalesForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('geo_gas:sales'))
context = {'entry': entry, 'form': form}
return render(request, 'ht/edit_sale.html', context)
So now if you want to access the Sale instance with pk=1, all you need to do is visit the url with /sale/1/, and so on.

WTForms form displays invalid when using the back button

I am building a Flask app and I use WTF forms.
My main page uses the MainForm which has a select field to choose a 'Tenant', two date fields and one Submit button which redirects to another page. The MainForm looks like:
class MainForm(Form):
tenant = SelectField('Tenant', choices=get_tenants())
start_date = DateField('From',
[validators.DataRequired('Please enter a valid date'),
DateRange(min=datetime(2013, 04, 01).date(), max=datetime.now().date())],
format='%Y-%m-%d', default=datetime.now().date())
end_date = DateField('To',
[validators.DataRequired('Please enter a valid date'),
DateRange(min=datetime(2013, 04, 01).date(), max=datetime.now().date())],
format='%Y-%m-%d', default=datetime.now().date())
room_stats = SubmitField('Room Stats')
In the new page /room-stats there is another form which has only one 'Back' button which redirects to the main page. It looks like:
class StatsForm(Form):
back = SubmitField('Back')
The view functions look like:
#main.route('/', methods=['GET', 'POST'])
#login_required
def index():
form = MainForm()
if form.validate_on_submit():
session['start_date'] = str(form.start_date.data)
session['end_date'] = str(form.end_date.data)
if current_user.is_administrator():
session['username'] = str(form.tenant.data)
else:
session['username'] = str(current_user.username)
if form.room_stats.data:
return redirect(url_for('main.room_stats'))
return render_template('index.html', form=form)
and
#main.route('/room-stats')
#login_required
def room_stats():
form = StatsForm()
[MORE_CODE_HERE]
if form.validate_on_submit():
if form.back.data:
return redirect(url_for(index))
return render_template('stats/room_stats.html', form=form)
Finally, the room_stats.html file has a form like:
<form action="/" method="post">
{{ form.hidden_tag() }}
{{ wtf.quick_form(form) }}
</form>
The app generally works fine but there is a problem about validation. Every time I click the Back button and I go back to my main form the Tenant field is highlighted in red and there a message
Not a valid choice
even though the field has a value and it is not blank.
I assume something is going wrong with the validation, but I cannot figure out what.
It is also strange that the Tenant field has no validators, so why is there this error message?

Categories

Resources