I'm building a Django application that will be used to schedule downloads. I've cleaned some government data that's pretty large, so people won't necessarily want to download all of it at the same time. They might also want to have it delivered via S3 buckets, etc. So I figured the easiest way to get the data to them would be to have a series of check boxes that iterate through what I have available and then let them press a button that would populate a database and have the server where the data they are stored on do the heavy lifting of uploading to a bucket and providing them the link, etc.
Where I'm having an issue is I'm having a heck of a time figuring out how to get data from the checkboxes, of all things.
Here's what I have tried so far. I've included the snippets from the files I believe to be pertinent:
models.py
class UserDownload(m.Model):
user = m.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=m.CASCADE
)
download_source = m.TextField(max_length=200)
download_configuration = JSONField()
download_queued_utc = m.DateTimeField()
download_link_generated = m.DateTimeField()
download_link = m.TextField()
bls-download.html
{% extends "base.html" %}
{% load static i18n %}
{% block title %}Data Sources List {% endblock title %}
...
{% for source in row_list %}
<div class="block">
{{ source.0 }} - {{ source.1.database_description }}
</div>
<table>
{% for file in source.1.file_list %}
<tr>
<td style="color:#0000ff"><input type="checkbox" id="{{ file.file_name }}" name="{{ file.file_name}}" value=
"{{ file.file_name }}">
<label for="{{ file.file_name }}"> {{ file.file_name }}</label></td>
<td style="color:#0000ff">{{ file.description }}</td>
</tr>
{% endfor %}
</table>
<br>
{% endfor %}
</div>
<h2 style="margin-top: 20px;">Please indicate your desired delivery method</h2>
<div class="block">
<input type="radio" id="SQLite" name="ExportAs" value="SQLite">
<label for="ExportAs">SQLite</label><br>
<input type="radio" id="CSV" name="ExportAs" value="CSV">
<label for="ExportAs">Excel</label><br>
<input type="radio" id="S3" name="ExportAs" value="S3">
<label for="ExportAs">CSV</label><br>
<input type="radio" id="SFTP" name="ExportAs" value="SFTP">
<label for="ExportAs">SFTP</label><br>
</div>
<form action="post">
<button type="button" class="btn btn-primary btn-block submit col-md-2">Schedule Download</button>
</form></div>
</div>...
{% endblock content %}
views.py
class BLSDownloadView(CreateView):
template_name = "data/bls-download.html"
model = UserDownload
def post(self, request):
if request.method == 'POST':
print(request)
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
row_list = [
{
"database_id": "ap",
"file_name": "All Files",
"description": None,
"database_description": "Average Price Data",
},
{
"database_id": "ap",
"file_name": "ap.data.0.Current",
"description": "All current year-to-date data",
"database_description": "Average Price Data",
},
{
"database_id": "ap",
"file_name": "ap.data.1.HouseholdFuels",
"description": "All household fuels data",
"database_description": "Average Price Data",
},
{
"database_id": "ap",
"file_name": "ap.data.2.Gasoline",
"description": "All gasoline data",
"database_description": "Average Price Data",
},
{
"database_id": "ap",
"file_name": "ap.data.3.Food",
"description": "All food data",
"database_description": "Average Price Data",
},
...
]
data['row_list'] = list()
database_dict = dict()
for row in row_list:
if not database_dict.get(row.get('database_id').upper()):
database_dict[row.get('database_id').upper()] = {
'database_description':row.get('database_description'),
'file_list': list()
}
file_cols = {'file_name': row.get('file_name'),
'description': row.get('description')}
database_dict[row.get('database_id').upper()]['file_list'].append(
file_cols
)
for k, v in database_dict.items():
data['row_list'].append([k, v])
return data
I feel like there's something incredibly obvious that I must be missing because I don't get anything when I click the Schedule Download button. I'm assuming that either/both:
I'm incorrectly capturing the post data. (Ultimately, I'd capture it and convert it to JSON and then insert into the table)
The HTML form isn't appropriately getting the data from the forms.
Ideally, I'd like it so that I don't have to specify which checkboxes I'm pulling in because I'll want to use this for other pages too, so if I can make the submit download button simply collect all of the form data, it would be re-useable and I won't have to make say, a boolean value for every possible checkbox I want.
EDIT:
Also tried this variation of the bls-download.html, which puts the form tag above the loop so all the tickboxes are in the same form:
{% extends "base.html" %}
{% load static i18n %}
{% block title %}Data Sources List {% endblock title %}
...
<form action="post">
{% for source in row_list %}
<div class="block">
{{ source.0 }} - {{ source.1.database_description }}
</div>
<table>
{% for file in source.1.file_list %}
<tr>
<td style="color:#0000ff"><input type="checkbox" id="{{ file.file_name }}" name="{{ file.file_name}}" value=
"{{ file.file_name }}">
<label for="{{ file.file_name }}"> {{ file.file_name }}</label></td>
<td style="color:#0000ff">{{ file.description }}</td>
</tr>
{% endfor %}
</table>
<br>
{% endfor %}
</div>
<h2 style="margin-top: 20px;">Please indicate your desired delivery method</h2>
<div class="block">
<input type="radio" id="SQLite" name="ExportAs" value="SQLite">
<label for="ExportAs">SQLite</label><br>
<input type="radio" id="CSV" name="ExportAs" value="CSV">
<label for="ExportAs">Excel</label><br>
<input type="radio" id="S3" name="ExportAs" value="S3">
<label for="ExportAs">CSV</label><br>
<input type="radio" id="SFTP" name="ExportAs" value="SFTP">
<label for="ExportAs">SFTP</label><br>
</div>
<button type="button" class="btn btn-primary btn-block submit col-md-2">Schedule Download</button>
</form></div>
</div>...
{% endblock content %}
The first thing I see is that you should put your <input> elements inside your <form> tag. When you submit a form, it only submits its own inputs - in case you have multiple different forms in a document, for instance.
Related
How are you community, I'm a little confused between my newbies and lack of knowledge, I'm working on a small project in Django and I'm also trying to send data from a form action in the html to another view function but I'm not understanding it well How does this work and on top of that I have to send several data not just one and it confuses me even more, I have the following HTML:
{% extends "base.html" %}
{% block content %}
<main class="container">
<div class="row">
<div class="col-md-10 offset-md-1 mt-5">
<form action="/interface/" method="POST" class="card card-body">
<h1>Interface</h1>
<h4>{{ error }}</h4>
<select name="dv">
<option selected disabled="True">Select Device</option>
{% for device in devicess %}
<option>{{ device.id }} - {{ device.name }}</option>
{% endfor %}
</select>
<br>
{% csrf_token %}
<br>
<button type="submit" class="btn btn-primary">Send</button>
</form>
<br>
{% for interface in interfaces %}
<section class="card card-body">
<h2>{{interface.Interface}}</h2>
{% if interface.Description == "" %}
<p class="text-secondary">none description</p>
{% else %}
<P class="text-secondary">{{interface.Description}}</P>
{% endif %}
<form action= "{% url 'send_description' %}"method="POST">
{% csrf_token %}
<input type="text" name="command" class="form-control" placeholder="Change description">
<br>
<button type="submit" class="btn btn-primary align-content-lg-center">Send change</button>
</form>
<br>
{% if interface.Status == "up" %}
<p class="text-secondary">Interface State: 🟢 Free</p>
{% else %}
<p class="text-secondary">Interface State: 🔴 Used</p>
{% endif %}
</section>
<br>
{% endfor %}
</div>
</div>
</main>
{% endblock %}
and aesthetically to better understand the first POST executed like this:
So far everything is perfect, if I press the "Send change" button it redirects me perfectly, the problem is that I need to send various data such as device.id, interface to that function that I am executing in the action= "{% url 'send_description' %} .Interface and also the content of the input that is inside the same form. Could you give me a hand or a guide on where to find the best way?
regards!
Let me start by saying that this would work way better with JS and AJAX. But, to answer your question, data is passed via Django http request object, in your case, since you have several different forms, it is possible to pass this data by adding a hidden field inside each form with the desired value:
<input type="hidden" name="interface" value="{{ interface.id }}">
And fetch this value form the request object in the view:
interface = request.POST.get('interface')
A full example:
models.py
class Device(models.Model):
name = models.CharField(max_length=100)
class Interface(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=100, default='interface description field')
status = models.BooleanField(default=False)
device = models.ForeignKey(Device, on_delete=models.CASCADE, related_name='interfaces')
views.py
from django.core.exceptions import ObjectDoesNotExist
def list_interfaces(request):
devices = Device.objects.all()
interfaces = None
try:
selected_device = Device.objects.get(id=request.POST.get('dv'))
interfaces = selected_device.interfaces.all()
except ObjectDoesNotExist:
selected_device = Device.objects.all().first()
if selected_device:
interfaces = selected_device.interfaces.all()
else:
selected_device = None
context = {
'devices': devices,
'selected_device': selected_device,
'interfaces': interfaces
}
return render(request, 'list_device_interfaces.html', context)
def send_description(request):
command = request.POST.get('command')
device = request.POST.get('seleted_device')
interface = request.POST.get('interface')
print(f'command: {command}')
print(f'device_id: {device}')
print(f'device_id: {interface}')
return redirect('core:list-device-interfaces')
urls.py
from core import views
from django.urls import path
app_name = 'core'
urlpatterns = [
path("list/device/interfaces/" , views.list_interfaces, name="list-device-interfaces"),
path("send/description/" , views.send_description, name="send-description"),
]
list_device_interfaces.html
{% extends "base.html" %}
{% block content %}
<main class="container">
<div class="row">
<div class="col-md-10 offset-md-1 mt-5">
<form action="{% url 'core:list-device-interfaces' %}" method="POST" class="card card-body">
{% csrf_token %}
<h1>Device</h1>
<h4>{{ error }}</h4>
<select name="dv">
<option selected disabled="True">Select Device</option>
{% for device in devices %}
<option value="{{ device.id }}" {% if device.id == selected_device.id %} selected {% endif %}>{{ device.id }} - {{ device.name }}</option>
{% endfor %}
</select>
<br>
<br>
<button type="submit" class="btn btn-primary">Send</button>
</form>
<br>
<hr>
<h2>Interfaces</h2>
{% for interface in interfaces %}
<section class="card card-body">
<h2>{{interface.name}}</h2>
{% if interface.description == "" %}
<p class="text-secondary">none description</p>
{% else %}
<P class="text-secondary">{{interface.description}}</P>
{% endif %}
<form action= "{% url 'core:send-description' %}"method="POST">
{% csrf_token %}
<input type="text" name="command" class="form-control" placeholder="Change description">
<input type="hidden" name="seleted_device" value="{{ selected_device.id }}">
<input type="hidden" name="interface" value="{{ interface.id }}">
<br>
<button type="submit" class="btn btn-primary align-content-lg-center">Send change</button>
</form>
<br>
{% if interface.status %}
<p class="text-secondary">Interface State: 🟢 Free</p>
{% else %}
<p class="text-secondary">Interface State: 🔴 Used</p>
{% endif %}
</section>
<br>
{% endfor %}
</div>
</div>
</main>
{% endblock %}
I want to receive users that a user has chosen to create a group chat. In an Aiohttp to get variable from HTML select tag, I use self.request.post()['variablename']:
#login_required
async def post(self):
data = await self.request.post()
chat_topic = data.get('chat_topic', '').lower()
users = data['users']
await Chat.create(topic=chat_topic, users=group_users)
This is my rooms.html:
{% extends 'base.html' %}
{% block content %}
<form class="form-signin" method="POST" action="{{ app.router['create_chat'].url_for() }}">
<h2 class="form-signin-heading">Create new chat</h2>
<label for="chat_topic" class="sr-only">chat topic</label>
<input name="chat_topic" type="text" id="chat_topic" class="form-control" maxlength="32" placeholder="chat topic" required autofocus>
<label for="users"> Choose users: </label>
<select name="users" id="users" multiple="multiple">
{% for user in users %}
<option value="{{ user.id }}">
{{ user.username }}
</option>
{% endfor %}
</select>
<button class="btn btn-lg btn-primary btn-block" type="submit">create chat</button>
</form>
{% endblock %}
Unfortunately, I receive only the last selected user as a string, not all of selected users. How can I get an array of selected users in aiohttp from an HTML select tag?
I know that in Django, I could do something like this:
users = request.POST.getlist('users')
I will appreciate any help!
I have created an edit view for my birthday reminder application and having previously worked, whenever I link to this edit view I receive a key error. I've checked the object being returned by the get_Gift function and the key field is present. Also, the HTML form created with multiple fields contains the field indicated in the error.
The error being received is:
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'giftName'
I'm sure I'm missing something really obvious, but I currently cannot see it. The HTML should be rendered and populated with the values retrieved from the respective MongoDB document.
Any assistance would be appreciated.
Python Script
def get_gift(gid):
query = {"giftId":gid}
gift = Gift.viewGift(query)
if gift is None:
abort(404, "Gift id {0} doesn't exist.".format(gid))
return gift
#ct.route('/<giftId>/editGifts', methods=('GET','POST'))
def editGifts(giftId):
gft = get_gift(giftId)
if request.method == 'POST':
giftId = giftId
giftName = request.form['giftName']
giftDate = datetime.strptime(request.form['giftDate'],'%Y-%m-%d')
giftDesc = request.form['giftDesc']
giftType = request.form['giftType']
giftCategory = request.form['giftCategory']
giftSentiment = request.form['giftSentiment']
giftRelInterest = list(str(request.form['giftRelInterest']).split(','))
error = None
gft = Gift(giftName,giftDate,giftDesc,giftType,giftCategory,giftSentiment,giftRelInterest,giftId)
if giftName is None:
error = 'Please provide a gift name'
if giftDate is None:
error = 'Please enter the date of gift purchase'
if giftType is None:
error = 'Please select a gift type'
if giftCategory is None:
error = 'Please choose a gift category'
if error is not None:
flash(error)
else:
query = {"giftId":giftId}
giftData = {"$set": {
"giftName": giftName,
"giftDate": giftDate,
"giftDesc": giftDesc,
"giftType": giftType,
"giftCategory": giftCategory,
"giftSentiment": giftSentiment,
"giftRelInterest": giftRelInterest
}}
Gift.updateGift_one(query,giftData)
return redirect(url_for('contact.index'))
gCategories = [{'name':'Books'},{'name':'Electronics/Computers'},{'name':'Home & Garden'},{'name':'Sports'},{'name':'Travel/Hospitality'},{'name':'Film/Theatre'},{'name':''},{'name':''}]
gTypes = [{'name':'Activity/Experience'},{'name':'Alcohol/Beverage'},{'name':'Virtual'},{'name':''},{'name':''},{'name':''},{'name':''},{'name':''}]
gSentiment = [{'name':'Positive'},{'name':'Neutral'},{'name':'Negative'},{'name':'Unknown'}]
gRelInt = [{'name':'Sports'},{'name':'Reading'},{'name':'Travel'},{'name':'Cooking'},{'name':'Art'},{'name':'Gardening'},{'name':'Gym'},{'name':'Film/Theatre'},{'name':'Music'},{'name':'DIY'}]
return render_template('gifts/editGifts.html', gft=gft, gCategories=gCategories, gTypes=gTypes, gSentiment=gSentiment, gRelInt=gRelInt)
else:
return render_template('gifts/viewGifts.html')
HTML Form
{% extends 'base.html' %}
{% block header %}
<div>
<h1>{% block title %}Edit Gift{% endblock %}</h1>
</div>
{% endblock %}
{% block content %}
<div class="ui form">
<fieldset>
<form method = "post">
<p>
<label for="giftName">Gift Name *</label>
<input name="giftName" id="giftName" value="{{ request.form['giftName'] or gft['giftName'] }}" required>
</p>
<p>
<label for="giftDate">Purchase Date *</label>
<input type="date" name="giftDate" id="giftDate" value="{{ request.form['giftDate'] or gft['giftDate'] }}" required>
</p>
<p>
<label for="giftDesc">Description</label>
<textarea name="giftDesc" id="giftDesc" rows="4" cols="50" value="{{ request.form['giftDesc'] or gft['giftDesc'] }}"></textarea>
</p>
<p>
<label for="giftType">Type *</label>
<select name="giftType" id="giftType" required>
{% for gTy in gTypes %}
<option selected="" value="{{ gTy.name }}">{{ gTy.name or gft['giftType'] }}</option>
{% endfor %}
</select>
</p>
<p>
<label for="giftCategory">Category *</label>
<select name="giftCategory" id="giftCategory" required>
{% for gC in gCategories %}
<option selected="Unknown" value="{{ gC.name }}">{{ gC.name or gft['giftCategory']}}</option>
{% endfor %}
</select>
</p>
<p>
<label for="giftSentiment">Sentiment</label>
<select name="giftSentiment" id="giftSentiment">
{% for gSen in gSentiment %}
<option selected="Unknown" value="{{ gSen.name }}">{{ gSen.name or gft['giftSentiment'] }}</option>
{% endfor %}
</select>
</p>
<p>
<label for="giftRelInterest">Related Interests</label>
<select name="giftRelInterest" id="giftRelInterest" size=5 multiple>
{% for gRI in gRelInt %}
<option value="{{ gRI.name }}">{{ gRI.name or gft['giftRelInterest'] }}</option>
{% endfor %}
</select>
</p>
<hr>
<p><tr>
<td><input type="submit" value="Save"></td>
<td><input type="reset" value="Reset"></td>
<td><input type="submit" value="Cancel" onclick="history.back()"></td>
</tr>
</p>
<hr>
</fieldset>
</form>
</div>
{% endblock %}
When you get a form value with request.form['key'], if the key doesn't exist in the request.form, Flask will raise a werkzeug.exceptions.BadRequestKeyError exception, just like what you got:
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand. KeyError: 'giftName'
It just tells you that the key giftName doesn't exist in request.form. Like what you do with dict, you can replace all the request.form[key] to request.form.get('key'), then you will get None as default value if the key doesn't exist.
Nothing suspicious to me... That error comes with the whole trace that should point out where it is originated exactly.
Also if it's actually GET request - viewGifts.html seems strange as it has no arguments passed.
I would like to write a one-template view, which depending on the url path will show different field of the model. So for instance if path is
http://127.0.0.1:8000/trip/2/1/
I will get second trip from my db (that works), and 1 should give (as html is written) a description field. I don't know how to process this to context_processor in my view. Do you have any ideas?
views.py
class TripDescriptionCreate(LoginRequiredMixin, UpdateView):
model = Trip
template_name = 'tripplanner/trip_arguments.html'
fields = ["description", "city", "country", "climate", "currency", "food", "cost", "comment", "accomodation",
"car", "flight", "visa", "insurance", "remarks"]
context_object_name = 'trips'
success_url = '/'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
trip_arguments.html
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">{{ trips.tripName }}</legend>
{% if field_id == 1 %}
{{ form.description|as_crispy_field }}
{% elif field_id == 2 %}
{{ form.city|as_crispy_field }}
{% endif %}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Update</button>
</div>
</form>
urls.py
path('trip/<int:pk>/<int:field_id>', TripDescriptionCreate.as_view(), name='trip-fullfill'),
So I came up with this idea. In my html I added these lines:
{% url 'trip-fullfill-description' pk=trip.pk as description_url %}
{% url 'trip-fullfill-city' pk=trip.pk as city_url %}
{% if request.get_full_path == description_url %}
{{ form.description|as_crispy_field }}
{% elif request.get_full_path == city_url %}
{{ form.city|as_crispy_field }}
I want to iterate over fields list in Django so as to create a generalized template for major of my forms.
The problem I face is that my form is not considered as valid when I'm using the input fields.
I want to stick to input fields as I'm using materialize css .
Below is my
form_template.html
<div class="row ">
{% for field in form %}
<div class="form-group">
{% ifequal field.name "password" %}
<div class="row">
<div class="input-field col s3 xl12">
<input id="{{ field.name }}" type="password" class="{{
field.name }}">
<label for="{{ field.name }}">{{ field.label }}</label>
</div>
</div>
{% endifequal %}
{% ifnotequal field.name "password" %}
{% ifequal field.name "email" %}
<div class="row">
<div class="input-field col s3 xl12">
<input id="{{ field.name }}" type="{{ field.name }}" class="validate">{{ form.field }}
<label for="{{ field.name }}" data-error="Not a valid email"
data-success="Valid Email">{{ field.label }}</label>
</div>
</div>
{% endifequal %}
<br>
{% ifnotequal field.name "email" %}
{% ifequal field.name "album_logo" %}
<div class="file-field input-field col s3 xl12">
<div class="btn">
<span>File</span>
<input type="file" multiple>
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text" placeholder="Upload an album cover">
</div>
{% endifequal %}
{% ifnotequal field.name "album_logo" %}
{% ifequal field.name "date_joined" %}
<div class="row">
<div class="input-field col s3 xl12">
<input id="{{ field.name }}" type="date" class="datepicker">{{ form.field }}
<label for="{{ field.name }}">{{ field.label }}</label>
</div>
</div>
{% endifequal %}
{% ifnotequal field.name "date_joined" %}
<div class="row">
<div class="input-field col s3 xl12">
<input id="{{ field.name }}" type="text">
<label for="{{ field.name }}">{{ field.label }}
</label>
</div>
</div>
{% endifnotequal %}
{% endifnotequal %}
{% endifnotequal %}
{% endifnotequal %}
</div>
{% endfor %}
</div>
and UserFormView Class in views.py
class UserFormView(View):
form_class = UserForm
template_name = "music/registration_form.html"
# Display a blank form for a new user
def get(self, request):
form = self.form_class(None)
return render(request, self.template_name, {'form': form})
# Process form Data here
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
# Cleaned (Normalized or Formatted) Data
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user.set_password(password)
user.save()
# Returns User Objects if credentials are correct
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request,user)
return HttpResponseRedirect('music:index')
else:
return render(request, self.template_name, {'form': form})
Would really appreciate some help, thanks.
When you want to style your form I would suggest to use Widget Tweaks. When you install it correctly you can use it in your template like:
Css:
.inputStyle{
width: 500px;
border: 1px solid black;
border-radius: 5px;
}
.slide{
...
}
HTML:
<form method='POST' action="/" enctype='multipart/form-data'>
{% load widget_tweaks %}
{% csrf_token %}
<span class="">Post the Image via Url: {{form.image_url|add_class:"inputStyle" }}</span>
<span class="" >Please select an option {{ form.Options|add_class:"slide" }}</span>
</form>
Another way to style your forms is to install Widgets in the forms.py
You can install things like Django Select and add it into the form like:
class PostForm(forms.ModelForm):
language = forms.MultipleChoiceField(widget=Select2MultipleWidget(attrs={'data-placeholder': 'Language'}),choices=settings.LANGUAGES)
class Meta:
model = Post
fields=[
'title',
'content',
'image_url',
'language',
.....
]
don't forget to use {{ form.media.js }}in the form.
I hope you get that going. The way you do it now is not the best way ;)
p.s. forgot to mention Django Crispy Form. They are fast to install and easy to handle but I would suggest using widget tweaks since you can style everything with CSS. Crispy can be tricky sometimes and you have to read into the docs...