Django Page Does Not Save - django

I'm working on a simple app that act similar to wikipedia using the tutorial "create a wiki in 20 minutes " from showmedo .
The app works by you create a page e.g dog if it doesn't exist then you add all the info about dogs and it display the dog and details of the dog.
When I try to edit the same page . the page doesn't get updated on the main page either does it get updated on my admin page.
I think the problem is with my save_page function and edit_page function.
So when I create a page , it works
but when I try to retrieve the page and save it . It doesn't save .
The only method that works is editing the page by admin.
I think the problem is here
def save_page(request, page_name):
content = request.POST.get('content', 'this is the default')
try:
page = Page.objects.get(pk=page_name)
page.content = content
This is the website
http://tafe.pythonanywhere.com/wikicamp/Dogs/
My views.py
from wiki.models import Page
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.template import RequestContext
def view_page(request,page_name):
try:
page = Page.objects.get(pk=page_name)
except Page.DoesNotExist:
return render_to_response("create.html",{"page_name":page_name})
content = page.content
return render_to_response("view.html",{"page_name":page_name , "content":content}, context_instance=RequestContext(request))
def edit_page(request,page_name):
try:
page = Page.objects.get(pk=page_name)
content = page.content
except Page.DoesNotExist:
content = ""
return render_to_response("edit.html",{"page_name":page_name, "content":content}, context_instance=RequestContext(request))
def save_page(request, page_name):
content = request.POST.get('content', 'this is the default')
try:
page = Page.objects.get(pk=page_name)
page.content = content
except Page.DoesNotExist:
page = Page(name=page_name, content=content)
page.save()
return HttpResponseRedirect("/wikicamp/" + page_name + "/")
My create.html
<html>
<head>
<title>{{page.name}} - Create </title>
</head>
<body>
<h1>{{page_name}} </h1>
This page does not exist. Create?
</body>
</html>
My edit.html
<html>
<head>
<title>{{page_name - Editing</title>
</head>
<body>
<h1>Editing {{page_name}} </h1>
<form method = "post" action="{% url wiki:save page_name %}"> {% csrf_token %}
<textarea name="content" rows="20" cols="60"> {{content}}
</textarea><br/>
<input type="submit" value="Save Page"/>
</form>
</body>
</html>
My view.html
<html>
<head>
<title>{{page_name}}</title>
</head>
<body>
<h1>{{page_name}} </h1>
{{content}}
<hr/>
Edit this page ?
</body>
</html>

try:
page = Page.objects.get(pk=page_name)
page.content = content
except Page.DoesNotExist:
page = Page(name=page_name, content=content)
page.save()
Two problems I see here. First, you are trying to retrieve the page by the page_name as the primary key, when you should be searching on the name attribute, and secondly after you have fetched the page successfully and updated its content, you forget to save it.
Since this is a common pattern, there is a shortcut in django get_or_create, it works like this:
page, created = Page.objects.get_or_create(name=page_name)
if created:
# new page was created
else:
# existing page was retrieved
In your scenario, you just want to fetch and update the contents in either scenario. So we don't need to use the created variable:
page, created = Page.objects.get_or_create(name=page_name)
page.content = content
page.save()

You're not saving the page in save_page, only when it doesn't exist. Try something like:
def save_page(request, page_name):
content = request.POST.get('content', 'this is the default')
try:
page = Page.objects.get(pk=page_name)
page.content = content
page.save()
except Page.DoesNotExist:
page = Page(name=page_name, content=content)
page.save()
return HttpResponseRedirect("/wikicamp/" + page_name + "/")
This is a quick ugly fix, I recommend looking into forms and class based views.

Two things I noticed with this:
try:
page = Page.objects.get(pk=page_name)
page.content = content
except Page.DoesNotExist:
page = Page(name=page_name, content=content)
page.save()
1) in the try block you are querying by pk and in the except you are setting the name.
2) you are not saving in the try block.
try this:
try:
page = Page.objects.get(name=page_name)
except Page.DoesNotExist:
page = Page(name=page_name)
page.content = content
page.save()

you forgot to put save()
def save_page(request, page_name):
content = request.POST.get('content', 'this is the default')
try:
page = Page.objects.get(pk=page_name)
page.content = content
page.save()
except Page.DoesNotExist:
page = Page(name=page_name, content=content)
page.save()
return HttpResponseRedirect("/wikicamp/" + page_name + "/")

Related

Variable 'html' referenced before assigned: UnboundLocalError

This code previously worked and outputed what I wanted on the website, but then this error happened
from django.shortcuts import render
import json
def get_html_content(fplid):
import requests
API_KEY = "eb9f22abb3158b83c5b1b7f03c325c65"
url = 'https://fantasy.premierleague.com/api/entry/{fplid}/event/30/picks/'
payload = {'api_key': API_KEY, 'url': url}
for _ in range(3):
try:
response = requests.get('http://api.scraperapi.com/', params= payload)
if response.status_code in [200, 404]:
break
except requests.exceptions.ConnectionError:
response = ''
#userdata = json.loads(response.text)
return response.text
def home(request):
if 'fplid' in request.GET:
fplid = request.GET.get('fplid')
html = get_html_content(fplid)
return render(request, 'scrape/home.html', {'fpldata': html})
here is my views.py file. I think I assigned html before, but I'm not sure, how is it referenced before it renders. I added scraperapi for many ip addresses, as I thought maybe I was banned from the api. I am unsure what is going on.
<body>
<h1>Enter Your FPL id </h1>
<form method="GET">
<label for="fplid"> </label>
<input type="text", name="fplid", id="fplid"> <br>
<input type="submit" value="Submit" />
</form>
<h3> {{fpldata}}</h3>
</body>
This is a part of the home.html file if it is relevant
When you initially load the page there probably wont'be an initialized ?fplid=xx. When this isn't present the variable is not assigned a value.
You could initialize the variable with html = None or this:
def home(request):
if 'fplid' in request.GET: # <- when this isnt true
fplid = request.GET.get('fplid')
html = get_html_content(fplid)
return render(request, 'scrape/home.html', {'fpldata': html})
return render(request, 'scrape/home.html')

Flask WTForms: Field Values Not Being Sent

I am giving my first go at bringing up a small website with flask, bootstrap, and wtforms. I am running into an issue where my wtforms fields are not sending values when submitted. I have a very basic wtform defined as follows:
class GeneralForm(Form):
boolean_val = BooleanField('Boolean')
a_float = FloatField('Severity')
submit = SubmitField('Submit')
I also have an html template which I render the form in:
{% block content %}
<div class="col-md-12">
{{form|render_form()}}
</div>
{%- endblock %}
Everything renders fine. When the form is submitted, I check it like so:
#app.route('/form', methods=['GET', 'POST'])
def do_form():
general_form = GeneralForm()
if general_form.validate_on_submit():
return "Value {}".format(general_form.boolean_val.data)
return render_template('symptomsform.html', form=general_form)
What I find is that the value for the boolean field is always the default value (false). I also notice that only a default value is provided when I check the float field. I checked the html for the page, I found that the input fields looked like:
<label for="boolean_val">
<input type="checkbox">Boolean
</label>
What stood out to me is the input field was missing a name in its tag. So, I manually stuck the name in and my test app was receiving the actual value of the checkbox.
My question is: what am I doing wrong with creating the input fields such that the values of the fields are not being sent with the form submission? I suspect the input fields should have names. So, why are names not being generated on the input fields?
Below is a sample script with the fixes,
app.py
from flask import Flask, render_template
from flask_wtf import Form
from wtforms import SubmitField, BooleanField, FloatField
from flask import request
from jinja2 import filters
app = Flask(__name__)
app.config['SECRET_KEY'] = 'catchmeifyoucan'
class GeneralForm(Form):
boolean_val = BooleanField('Boolean')
a_float = FloatField('Severity')
submit = SubmitField('Submit')
#app.route('/wtforms', methods=['GET', 'POST'])
def debug_wtforms():
form = GeneralForm()
if request.method == 'POST' and form.validate_on_submit():
print(form.boolean_val.data)
print(form.a_float.data)
return render_template('index.html', form=form)
# This is a jinja2 custom filter for rendering a form
#app.template_filter()
def render_form(form, action='/', method='post'):
temp = ''
start_form = "<form action=" + action + " method=" + method + ">"
end_form = "</form>"
temp += start_form
for el in form:
temp += str(el())
temp += end_form
return filters.do_mark_safe(temp)
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Wtforms debug</title>
</head>
<body>
{{ form | render_form(action=url_for('debug_wtforms')) }}
</body>
</html>
The custom jinja2 filter given below helps you to render the form with the name attribute,
# This is a jinja2 custom filter for rendering a form
#app.template_filter()
def render_form(form, action='/', method='post'):
temp = ''
start_form = "<form action=" + action + " method=" + method + ">"
end_form = "</form>"
temp += start_form
for el in form:
temp += str(el())
temp += end_form
return filters.do_mark_safe(temp)
This filter has two default arguments, action and method which could be passed if you want to modify the form method and action.
The current filter won't display the form field label, but if you want to display form field label, you can access it using str(el.label()) and append it to the temp variable in the custom filter.
Note : You can make necessary tweaks to the custom filter to modify how the form must be displayed
I hope this helps.

how to use variable data to use in template tag in Django template

In urls.py file
from articles.views import home
urlpatterns = patterns('',
url(r'^home/$',home.as_view(),name='home'),
)
In views.py file
class home(TemplateView):
template_name='article.html'
def get(self, request):
form = Homeform()
return render(request,self.template_name, {'form':form})
def post(self,request):
file_path = '/u/vinay/checking.py'
args={'file_path':file_path}
return render(request,self.template_name, args)
In article.html file
{% load static %}
<html>
<body>
Download plan</button>
<p>{{ file_path }} </p>
</body>
</html>
But i'm getting no file as output from GUI.
As i'm creating download link for that file in file_path location.So how do i render text from views to article.html
You dont neeed {{}} signs inside template tag. Try this:
"{% static file_path %}"
Check django docs for details.
Please refer to the documentation for template tags:
href="{% static file_path %}"
Ninja'ed..
Also, your view function is all messed up, I'm suprised it displays anything at all:
def vin(request):
return render(request,'article.html', {'file_path':'xyz.py'})
Try this:
class home(TemplateView):
template_name='article.html'
def get_context_data(self,*args,**kwargs):
context = super().get_context_data(*args,**kwargs)
context['file_path'] = '/u/vinay/checking.py'
return context
# END OF VIEW --- no get or post method, let the generic view handle that.

Django render is not refreshing page after a form submission

After almost a full day trying to make things work, I resign... I need some help because I don't understand where I'm doing things the wrong way.
I searched SO and stumbled on many answers about redirection and so on...
Let me give you some context :
I have a simple form where one can upload a file, so far, it's working well. I want the user to be redirected to another page after the upload is successful and this is where it fails :'(
My views.py :
# Create your views here.
def thank_you(request):
data = {'text': 'Thank you for your file'}
print('thank you blablabla')
print(data)
return render(request, 'app/thank_you.html', data)
def home(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
uploaded_file = request.FILES['file']
new_file = UploadFile(file=uploaded_file)
new_file.save()
return redirect(reverse(thank_you))
else:
form = UploadFileForm()
data = {'form': form}
return render(request, 'app/drop_file.html', data)
I do see the 2 prints in the 'thank_you' function, meaning that the redirect is working as expected. But the view doesn't refresh and I'm stuck.
And if I try to access the url directly (going to http://.../thank_you/ ) it does show correctly.
The thank_you.html :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload done</title>
</head>
<body>
<h1>Hello !</h1>
<h1>{{ text }} </h1>
</body>
</html>
The form looks like that :
<form id="my-dropzone" class="dropzone" action="{% url 'home' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
</form>
<button id="submit-all">
Submit all files
</button>
<script type="text/javascript">
Dropzone.options.myDropzone = {
// Prevents Dropzone from uploading dropped files immediately
autoProcessQueue : false,
init : function() {
var submitButton = document.querySelector("#submit-all")
myDropzone = this;
submitButton.addEventListener("click", function() {
myDropzone.processQueue();
// Tell Dropzone to process all queued files.
});
// You might want to show the submit button only when
// files are dropped here:
this.on("addedfile", function() {
// Show submit button here and/or inform user to click it.
});
this.on("queuecomplete", function() {
console.log('should we redirect ?');
});
}
};
</script>
And my urls.py file :
url(r'^$', views.home, name='home'),
url(r'thank_you/$', views.thank_you, name='thank_you'),
Nothing out of the ordinary I guess.
Even if I change the 'home' to :
if form.is_valid():
uploaded_file = request.FILES['file']
new_file = UploadFile(file=uploaded_file)
new_file.save()
data = {'text': 'Thank you for your file'}
print('thank you blablabla')
print(data)
return render(request, 'app/thank_you.html', data)
The view doesn't update, I do see the prints in the django console but the template does not render...
I would like the return render(request, 'app/thank_you.html', data) to actually refresh the page after the form is submitted but I can't achieve to do so :( Any help ?
Firebug show me this in the console :
console output

redirect from django admin action intermediate page to change form page

I am trying to build an admin action 'download_selected' which will download selected models. When the action is selected, I redirect to an intermediate page so that users can select a download format. When a user selects a download format and clicks on 'download', it downloads the file. But stays on the same intermediate page. How do I redirect it back to change form admin page? This redirection that I want is similar to django 'download selected file' default admin action. Thanks.
Here is my code.
admin.py
class SelectDownloadFormatForm(forms.Form):
DOWNLOAD_TYPE_CHOICES=[('csv','csv'),
('json', 'json'),
('xml','xml')]
_selected_action = forms.CharField(widget=forms.MultipleHiddenInput)
download_type = forms.ChoiceField(label=_('Select a Download type'), choices=DOWNLOAD_TYPE_CHOICES, widget=forms.RadioSelect())
def download_selected(self, request, queryset):
import csv
from django.http import HttpResponse, HttpResponseRedirect
import StringIO
form = None
if 'download' in request.POST:
form = self.SelectDownloadFormatForm(request.POST)
if form.is_valid():
dtype = form.cleaned_data['download_type']
print dtype
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="export.csv"'
writer = csv.writer(response)
writer.writerow(['id', 'name', 'qid' ,'label', 'name', 'field'])
count = 0
for s in queryset:
questions_query = ParentModel.objects.filter(parent_form_id = s.id)
for q in questions_query:
writer.writerow([s.id, s.name, q.id, q.label, q.name, q.field])
count += 1
plural = ''
if count != 1:
plural = 's'
self.message_user(request, "Successfully downloaded %d survey response%s in %s format" % (count, plural, dtype))
return response
if not form:
form = self.SelectDownloadFormatForm(initial={'_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME)})
return render(request,'admin/download_type.html', {'items': queryset,
'download_type_form': form,
})
download_selected.short_description = "Download selected forms"
download_type.html
{% extends "admin/base_site.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
{{ download_type_form }}
<p>Following survey will be downloaded with corresponding responses:</p>
<ul>{{ items|unordered_list }}</ul>
<input type="hidden" name="action" value="download_selected" />
<input type="submit" name="download" value="Download" />
</form>
{% endblock %}
I added an extra button to go back
Go Back
You'll need javascript for the redirect.
You can use jQuery File Download so you can do:
$.fileDownload('/url/to/download').done(function {
// redirect
})
Not sure if you can combine it with a form post.