how to name numerous dynamic input fields for post method - Django - html

In my online shop, I fetch all of the products and services from two different apps and list them for the user to make his wishlist.
Each product or service is displayed in a bootstrap card that contains an input field for the count of products.
#views.py
def my_products(request):
ip_sensor = Ip_sensor.objects.all().order_by('title')
control_valves = ControlValves.objects.all().order_by('title')
context = {
'ip_sensor': ip_sensor,
'control_valves': control_valves,
}
return render(request, 'users/basket/my_products.html', context)
then in the template, they are being displayed in this way:
<form method="post" id="add_to_wishlist" data-url="{% url 'my_products' %}">
{% csrf_token %}
{% if ip_sensor %}
{% for item in ip_sensor %}
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-12">
<p class="card-text text-center text-capitalize">{{ item.title }}</p>
</div>
<div class="col-12">
<div class="form-group">
<input type="hidden" name="tag" value="{{ item.type }}"> <!-- what to put in name field of this line -->
<input type="hidden" name="item_id" value="{{ item.id }}"> <!-- what to put in name field of this line -->
<label for="count" class="control-label">count</label>
<input type="text"
id="count"
name="count" <!-- what to put in name field of this line -->
placeholder="Count"
class="form-control"
autofocus/>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
{% endif %}
</form>
Question:
When I return the count of each product or service through a POST method back to my views.py to save to his wishlist, I don't know how to distinguish between the returned values??
Since items are being displayed in a for loop and I want to save each of the selected items separately in a WishListItem object (model), I need to name each card's input fields separately but I don't know how to do it.
I can save each item in this way:
if request.method == 'POST':
owner = request.user
count = request.POST.get('count')
tag = request.POST.get('tag')
object_id = request.POST.get('item_id')
wishlist = WishListItem(owner=owner,
content_type=class_types[tag],
object_id=object_id,
tag=tag,
count=count)
wishlist.save()
return redirect('my_products')

When there are multiple inputs with same name, request.POST has list of all those input element values. So, you can get all item ids using request.POST.getlist('item_id') and it will return list containing all ids. In your html you can name all other inputs using id as part of the name attribute, like this:
...
<input type="hidden" name="tag_{{ item.id }}" value="{{ item.type }}"> <!-- item id has become part of the input name -->
<input type="hidden" name="item_id" value="{{ item.id }}"> <!-- all item ids will be accessed as list in view -->
<label for="count" class="control-label">count</label>
<input type="text"
id="count"
name="count_{{ item.id }}" <!-- item id has become part of the input name -->
placeholder="Count"
class="form-control"
autofocus/>
...
And in your view you can access all values like this:
...
for object_id in request.POST.getlist('item_id'): #this will contain a list with all item ids in it
count = request.POST.get('count_%s'%object_id) #as html inputs are named with ids as part of name you can access them
tag = request.POST.get('tag_%s'%object_id) #as html inputs are named with ids as part of name you can access them
wishlist = WishListItem(owner=owner,
content_type=class_types[tag],
object_id=object_id,
tag=tag,
count=count)
wishlist.save()
...

I found the answer based on #datosula's answer.
since the product id may be repetitive because products are loaded from various tables, then a unique tag is required to distinguish returned values in views.py. I mixed up product's title and products type to achieve this:
<div class="form-group">
<input type="hidden" name="title_type" value="{{ item.title }}{{ item.type }}">
<input type="hidden" name="item_id_{{ item.title }}{{ item.type }}" value="{{ item.id }}">
<input type="hidden" name="tag_{{ item.title }}{{ item.type }}" value="{{ item.type }}">
<input type="hidden" name="title_{{ item.title }}{{ item.type }}" value="{{ item.title }}">
<div class="text-center">
<input type="number"
id="count"
name="count_{{ item.title }}{{ item.type }}"
placeholder="count"
class="form-control"
value="0"
min="0"
max="9999"
autofocus/>
</div>
</div>
in views.py I got the values and created the object like the following:
....
for item in request.POST.getlist('title_type'):
object_id = request.POST.get('item_id_%s'%item)
tag = request.POST.get('tag_%s'%item)
print('\ntag: ', tag )
print('\nclass_types[tag]: ', class_types[tag])
count = request.POST.get('count_%s'%item)
title = request.POST.get('title_%s'%item)
if count != '0': # only save those products that their count is not 0
wishlist = WishListItem(owner=owner,
content_type=class_types[tag],
object_id=object_id,
tag=tag,
count=count,
title=title)
wishlist.save()
....
wish it helps somebody

Related

How to handle await self.request.post()['users'] with multiple variables from HTML select tag for the same parameter in Aiohttp?

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!

shopify liquid add to cart button

I am new to shopify theme development and I'm building a theme from scratch to gain experience. I'm having trouble with the 'add to cart' button on my product page. Even though I have various product options for a product, I am only displaying the 'size' options as radio buttons and then I take an input quantity and add it to the cart. The problem I am facing right now, is that the cart only adds 1 item at a time, So even if I input 3 or 4 as my quantity, the cart only adds 1 as the quantity.
here's my code:
{% form 'product', product %}
<div>
<p class="option-title">Size</p>
<div class="line"></div>
<div class="options">
{% for product_option in product.options_by_name['Size'].values %}
<input type="radio" id = "{{ product_option }}" name="size" value="{{ product_option }}" >
<label for="{{ product_option }}">{{ product_option }}</label>
{% endfor %}
</div>
</div>
<div class="line"></div>
<div class="quantity-add">
<div class="input-quantity">
<input class="input-quantity" type="number" min="1" placeholder="1">
<input type="hidden" name="id" data-productid="{{ product.variants[0].id }}" value="{{ product.variants[0].id }}" data-variant-title="{{ product.variants[0].title }}" />
</div>
<div class="cart-button">
<button class="cart-btn" type="submit" value="Add To Cart">ADD</button>
</div>
</div>
{% endform %}
Any help will be much appreciated. I am so lost regarding how I should fix it.
It should work fine if you add the proper name and value attribute to your input element:
<input name="quantity" value="3">
This would add the selected variant ID three times to the cart.

My form is not saving for some reason... did I made a mistake on my HTML?

I wanted to modify the way my forms is displayed using html and css.
Here is the <form> part of my HTML:
<form action="" method="post" enctype='multipart/form-data'>
{% csrf_token %}
<div>
<input type="text" name="post_title" placeholder="Forum title" id="id_post_title">
<textarea name="post_body" placeholder="Forum content" id="id_post_body"></textarea>
<div class="authors"> <!-- This class is to call my Users model -->
<select name="author" id="id_author">
<option value="">----Select Author----</option>
{% for author in authors %}
<option value="{{ author.first_name }} {{ author.last_name }}">{{ author.first_name }} {{ author.last_name }}</option>
{% endfor %}
</select>
</div>
<div>
<label>
<input type="file" accept="image/" name="forum_image" required id="id_forum_image" >Upload Image
</label>
</div>
<input type="submit" value="Save Post" class="save_post">
</div>
</form>
I tried form.as_p and it all worked just fine. Did I made a mistake in my HTML? Here is my forms.py:
class AddForum(forms.ModelForm):
class Meta:
model = Forum
fields = 'all'
labels = {
'post_title': 'Title of your post:',
'post_body': 'Content of your post:',
'author': 'Author:',
'forum_image': 'Attach image:',
}
def __init__(self, *args, **kwargs):
super(AddForum, self).__init__(*args, **kwargs)
self.fields['forum_image'].required = False
The problem lies with my <option value="{{ something }}". What value needs is {{ something }}'s id and not the name itself.
The code should be:
<option value="{{ author.id }}">{{author.first_name }} {{ author.last_name }}</option>

Insert into table from arbitrary number of checkboxes in Django

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.

Django generic editing views: Styled HTML form is not saved

I'm using Django's generic editing views CreateView, UpdateView, etc. together with the auto-generated HTML forms and it works fine:
# views.py
class TagCreate(CreateView):
template_name = 'app/tag_form.html'
model = Tag
fields = ['name', 'description', 'color']
class TagUpdate(UpdateView):
model = Tag
fields = ['name', 'description', 'color']
<!-- app/tag_form.html -->
{% extends 'app/base.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
{% endblock %}
Now, I want to customize the generated form {{ form.as_p }} with bootstrap:
{% extends 'app/base.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="nameInput">Name</label>
<input class="form-control" type="text" id="nameInput" placeholder="Name" value="{{ tag.name }}">
</div>
<div class="form-group">
<label for="descrInput">Description</label>
<input class="form-control" type="text" id="descrInput" placeholder="Description" value="{{ tag.description }}">
</div>
<div class="form-group">
<label for="colorInput">Color</label>
<input class="form-control" type="color" id="colorInput" placeholder="Color" value="{{ tag.color }}">
</div>
<input type="submit" value="Save">
</form>
{% endblock %}
The page renders nicely exactly how I want it to, but when I click the "Save" button, nothing happens and the data is no longer saved, nor am I forwarded to the detail view like I was before.
I tried following the Django documentation on how to render fields manually; again, it's rendered correctly, but the data isn't saved.
How can I properly customize forms and still use my generic editing views?
Edit: My full code his here.
Following the documentation you have to access the form inputs manually and django will populate them accordingly in the template.
{% extends 'app/base.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="nameInput">Name</label>
{{ form.name }}
</div>
<div class="form-group">
<label for="descrInput">Description</label>
{{ form.description }}
</div>
<div class="form-group">
<label for="colorInput">Color</label>
{{ form.color }}
</div>
<input type="submit" value="Save">
</form>
{% endblock %}
From there, to add classes, you will have to override the CreateView get_form in order to add in what we need:
class TagCreate(CreateView):
template_name = 'app/tag_form.html'
model = Tag
fields = ['name', 'description', 'color']
def get_form(self, form_class):
form = super(TagCreate, self).get_form(form_class)
form.fields['name'].widget = forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Name' })
form.fields['description'].widget = forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Description' })
form.fields['color'].widget = forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Color' })
return form
Although, this would be a lot cleaner if using ModelForms
in your form input tag set the "name" attribute, field name as value
something like this:
...
<input class="form-control" type="text" id="nameInput" placeholder="Name" name="name" value="{{ tag.name }}">
...
<input class="form-control" type="text" id="descrInput" placeholder="Description" name="description" value="{{ tag.description }}">
...
<input class="form-control" type="color" id="colorInput" placeholder="Color" name="color" value="{{ tag.color }}">
...