I am working on a Django wiki app. The user can enter markdown text in a textarea to either create or edit an entry. Whenever this happens though, the number of newlines between text are doubled. For example if the user entered 4 newlines in the textarea, the saved markdown file will have 8 newlines.
'''
# in views.py
class ContentForm(forms.Form):
content = forms.CharField(
required=True,
widget=forms.Textarea,
label="Contents")
def edit(request, title):
if request.method == 'POST':
# save_entry saves the markdown text to the given title.md
save_entry(title, request.POST['content'])
# redirect the user to the updated wiki page
return HttpResponseRedirect(reverse('entry', args=(title,)))
else:
# get_entry returns markdown text for a title
content = get_entry(title)
form = ContentForm(request.POST or None, initial={'content': content})
return render(request, "encyclopedia/edit.html", {
"title": title,
"content": content,
"form": form
})
# in edit.html
<h1>Edit {{ title }}</h1>
<form action="{% url 'edit' title %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Save Changes">
</form>
# save_entry and get_entry definitions
def save_entry(title, content):
"""
Saves an encyclopedia entry, given its title and Markdown
content. If an existing entry with the same title already exists,
it is replaced.
"""
filename = f"entries/{title}.md"
if default_storage.exists(filename):
default_storage.delete(filename)
default_storage.save(filename, ContentFile(content))
def get_entry(title):
"""
Retrieves an encyclopedia entry by its title. If no such
entry exists, the function returns None.
"""
try:
f = default_storage.open(f"entries/{title}.md")
return f.read().decode("utf-8")
except FileNotFoundError:
return None
'''
In this case I used a textarea widget, but before this I had just used the textarea html tag and that was not working either. To create a new page as well, I am not using a widget and that is doing the same thing too. I've been trying to fix this for many hours. What could be going wrong?
I fixed this by using bytes(content, 'utf8') before passing it to the save function.
See: Why did bytes() solve my newlines problem?
if request.method == 'POST':
save_entry(title, bytes(request.POST['new_content'], 'utf8'))
return HttpResponseRedirect(reverse('encyclopedia:entry', args=(title,)))
Related
i am trying to build a django search functionality for my app but the input form keeps returning a none
views.py
def search(request):
if request.method == 'POST':
query = request.POST.get('text')
houses = Product.objects.filter(name__contains='query')
context = {
'houses':houses,
}
return render (request, 'searchresult.html', context)
search.html
<form>
<input type='text' placeholder='search houses>
<button type='submit'>Search</button>
</form>
First off, your python indentation is invalid, and your HTML is also invalid on the input line. I will assume this is a typo in the question, but if not, you have issues there.
Your main problem is the filter for houses:
houses = Product.objects.filter(name__contains='query')
is looking for a name containing the string "query". You need the variable you've just defined.
houses = Product.objects.filter(name__contains=query)
You have an indentation issue in the code you have posted.
You need to add action and method in your Form.
<form action="/url_of_search/" method="post">
Missing quote in input line.
<input type='text' placeholder='search houses'>
You need to use query instead of 'query' in the filter.
Product.objects.filter(name__contains=query)
Things missing in html code:
form action attribute
form method attribute
input field name attribute
<!-- add form attributes method and action -->
<form method="POST" action="{% url '<url_name>' %}">
<!-- add input attribute name to identify the field and pass the value in request body -->
<input type='text' placeholder='search houses' name='search_text'>
<button type='submit'>Search</button>
</form>
update views for search
def search(request):
if request.method == 'POST':
# use input field name to get the search text
query = request.POST.get('search_text')
houses = Product.objects.filter(name__contains=query)
context = {
'houses':houses,
}
return render (request, 'searchresult.html', context)
First, I'm relatively new to Django. I've seen my question addressed here with answers that I've tried to implement without success. Using a date picker that formats the date differently then how its stored and returned on the form initially.
forms.py
....
start_date = forms.Datefield(widget=forms.DateInput(format='%m/%d/%Y'), input_formats=['%m/%d/%Y'])
queries.html
....
<div class="col-4">
<label for="start_date" style="font-weight: bold;">Start Date</label>
<div class="input-group date" data-provide="datepicker">
<input type="text" name="start_date" id="start_date" value="{{queryform.start_date.value}}" class="form-control">
<div class="input-group-addon"><span class="glyphicon glyphicon-th"></span></div>
</div>
</div>
....
<form method="post">
{% csrf_token %}
<script>
$(function() {
$( ".datepicker" ).datepicker({
changeMonth: true,
dateFormat: 'mm/dd/yyyy',
changeYear: true,
yearRange: "2010:2025"
});
});
</script>
url.py
path('editqueries/<id>', views.editqueries, name='editqueries'),
views.py
def editqueries(request, id):
query_data = get_object_or_404(Query, pk=id)
if request.method == "POST":
query_form = QueryForm(request.POST, instance=query_data)
if query_form.is_valid():
the_query_name = query_form.cleaned_data["query_name"]
# get the hidden field from the html page (but not on the
# Django form)
current_query_name = request.POST["currentqueryname"]
# test to be sure if the names are not the same that changing
# the name doesn't create a duplicate query_name
if not the_query_name == current_query_name:
try:
test_query =
Query.objects.get(query_name=the_query_name)
except Query.DoesNotExist:
# this is allowed. Named changed does not create a
# duplicate
query_form.save()
query = Query.objects.all()
query_flag = "None"
context = {'queries': query, 'query_flag': query_flag}
return render(request, 'seakerUI/queries.html',
context)
# successful query means this name is in use.
# Stop the renaming of the query.
return HttpResponse("ERROR: Query Name '" +
the_query_name + "' Already exist!")
query_form.save()
query = Query.objects.all()
query_flag = "None"
context = {'queries': query, 'query_flag': query_flag}
return render(request, 'seakerUI/queries.html', context)
else:
return HttpResponse("Form is invalid.. errors:" +
str(query_form.errors))
else:
query_form = QueryForm(instance=query_data)
# tell the user the query is ready to be updated.
query_flag = "Edit"
context = {'queryform': query_form, 'query_flag': query_flag}
return render(request, 'seakerUI/queries.html', context)
queries.html
see code above
So when attempting to edit a query, the page is formatted with the date like "Aug. 2, 2019". However, if one submits the form without changing the date, the form is invalid and the form.error is date is invalid.
I've set the following line in settings.py
DATE_INPUT_FORMATS = ['%m/%d/$Y']
I've had 2 other formats in this definition but none seem to work.
I also executed
python manage.py diffsettings
and though it shows in the output the impact is negligible.
I've attempted using many examples of structuring the forms.py file using a widget function and without it without success. The problem does not appear to be with the javascript on the hmtl page.
NOTE: If I change the date when the edit query page presents it then the form validates. However, if one doesn't change the date and the form is submitted it is not valid and an error occurs. I shouldn't have to change the date to get the form to validate.
Suggestions?
You can try with html5 and WTF forms.
Html5 and WTFforms together can be used to select and date/month/year and process.
In form.py:
from wtforms.fields.html5 import DateField
Accept the inputs as shown :
dob= DateField('Password - Your Date of Birth', validators=[DataRequired()], format='%Y-%m-%d')
In html
form.dob(class="form-control form-control-lg")
I trying to propose to the users of my site to download a document in either pdf or odt version through radio buttons. How can I get and use the value of the radio button chosen by the user to serve the appropriate file. So far, I can only serve one at a time.
My current work:
models.py
class File(models.Model):
name = models.CharField(max_length=200)
pdf_version = models.FileField()
odt_version = models.FileField()
def __str__(self):
'''String name represents class File'''
return self.name
urls.py
path('files_page/', views.files_page, name='files_page'),
path('download_file/<int:file_id>/', views.download_file, name='download_file'),
views.py
def files_page(request):
files = File.objects.all()
context = {'files':files}
return render (request, 'walk/files_page.html', context)
def download_file(request, file_id):
#No post request; do nothing
if request.method != 'POST':
pass
else:
#fetch the file to download
#file = File.objects.get(id=file_id)
response = FileResponse(open('/home/me/Desktop/super/media_cdn/tog.pdf', 'rb'))
response['Content-Disposition'] = 'attachment; filename="tog.pdf"'
return response
template
{%block content%}
{%for file in files %}
<p>{{file.name}}</p>
<p>{{file.pdf_version}}</p>
<p>{{file.csv_version}}</p>
<form action="{%url 'walk:download_file' file.id%}" method="POST">
{%csrf_token%}
<input type="radio" name="format" value="pdf" checked> pdf
<input type="radio" name="format" value="csv"> csv
<button name="submit">download</button>
</form>
{%endfor%}
{%endblock content%}
Let's start with using forms. Yes, you use django forms in django instead re-implementing everything yourself.
Create forms.py:
from django import forms
FILE_FORMAT_CHOICES = [("csv", "Download PDF"), ("csv", "Download CSV")]
class FileFormatForm(forms.Form):
file_format = forms.ChoiceField(choices=FILE_FORMAT_CHOICES, widget=forms.RadioSelect())
Inside of the template used by files_page (just let django render the fields, don't do it yourself):
<form action="{%url 'walk:download_file' file.id%}" method="POST">
{% csrf_token %}
{{ form }}
<input type="submit" value="Download">
</form>
And finally adjust the views.py:
def files_page(request):
...
context = {
'files': files,
'form': FileFormatForm() # empty / without POST
}
...
def download_file(request, file_id):
assert request.method == 'POST', "users should only come here with POST now"
form = FileFormatForm(request.POST) # populate from POST
if form.data['file_format'] == 'pdf':
return "return PDF file response here"
else:
return "return CSV file response here"
Note: you don't use tab in Python. Use 4x whitespaces instead.
Another Note: Class Based Views to further reduce the amount of boilerplate.
Here's the goal: when the user submits the form, use one view to send the submitted data to the database, then redirect back to the form, but with the data pre-populated. This is mostly working, but something about my implementation is wrapping extra quotes around the string. For now, I'm just using a super-simple form, btw. I enter Billy, and the pre-pop is: "Billy", if I click submit again, it comes back as: "\"Billy\"", then "\"\\\"Billy\\\"\"", and so on (as far as I have tested, anyways.
relevant views are:
def editUsers(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = usersForm(request.POST)
# check whether it's valid:
# process the data in form.cleaned_data as required
# redirect to a new URL:
name = json.dumps(form.data['user_name'])
request.session['editUserName'] = name
# call out to limboLogic.py to update values
test = name
return redirect('../users')
# if a GET (or any other method) we'll create a blank form
else:
return redirect('../users')
from .forms import *
def users(request):
form = None
if 'editUserName' not in request.session:
# create a blank form
form = usersForm()
else:
# form = equipmentForm(initial='jim') - used to make sure I was branching the if/else correctly
form = usersForm(initial={'user_name':request.session['editUserName']}, auto_id=False) #limboLogic.GetUserInfo(name))
return render(request, 'limboHtml/UserManagement.html', {'form': form})
form is simply:
class usersForm(forms.Form):
user_name = forms.CharField(label='New User\'s name', max_length=100)
and the template is:
{% extends "base.html" %}
{% block content %}
<div class="row">
<p>This is the user management page</p><br>
<form action="/edit/users.html" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="OK">
</form>
<br><p class="bold">This is below the form</p>
</div>
{% endblock %}
thoughts?
I can't quite say what the intracies are here, but the problem involves the fact that I was using a json class. I used this site as a guide and managed to fix the problem. note that the key aspect is inside the second if:
name = form.cleaned_data['user_name'] works fine,
name = json.dumps(form.data['user_name']) does not
the whole function as it now stands:
def editUsers(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = usersForm(request.POST)
# check whether it's valid:
# process the data in form.cleaned_data as required
# redirect to a new URL:
if form.is_valid():
name = form.cleaned_data['user_name']
# name = json.dumps(form.data['user_name'])
request.session['editUserName'] = name
# call out to limboLogic.py to update values
test = name
return redirect('../users')
# if a GET (or any other method) we'll create a blank form
return redirect('../users')
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.