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.
Related
i have following code:
<div class="quantity">
<input type="number" name="updates[]" id="updates_{{ item.key }}" value="{{ item.quantity }}" min="0">
</div>
which increases input field value, but not product quantity in cart, how to fix it?
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
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 }}">
...
I want to style my checkboxes to look like buttons using the bootstrap class <div class="btn-group-toggle" data-toggle="buttons">
but it does not let me retrieve checkbox values in Flask like it usually did when I did not use this bootstrap class.
I have created a page with dynamically generated checkboxes using the code below. I use the following Flask code to retrieve checkbox values checkboxValues = request.form.getlist('checkbox'). How can I retrieve these checkbox values whilst having it nicely styled as buttons?
<form action="start" method="post">
{% for text in slideText %}
{% set count = namespace(a=0) %}
{% set slideNumber = namespace(a=loop.index-1) %}
<h2>Slide {{ loop.index }}</h2>
<hr>
<div class="btn-group-toggle" data-toggle="buttons">
{% for innerText in text %}
<label class="btn btn-primary active">
<input type="checkbox" name="checkbox" autocomplete="off" value="{{ slideNumber.a }}.{{ count.a }}"> {{ innerText }}
</label>
{% set count.a = count.a + 1 %}
{% endfor %}
</div>
{% endfor %}
<br>
<input class="btn btn-block btn-outline-success" type="submit" value="Submit">
</form>
I am trying to put {{field.id}} in the the HTML input attributes but it doesn't work. What is the correct way to do this?
<input type="text" class="form-control" id="{{field.id}}">
I also tried:
<input type="text" class="form-control" id={{field.id}}>
Heres my full code:
{% for field in wizard.form %}
<div class="row">
<label class="col-xs-4" for={{field.id_for_label}}>{{ field.label }}</label>
<div class="col-xs-2">
<div class="form-group">
<input type="text" class="form-control" id="{{field.id}}">
{{field.id}}
</div>
</div>
</div>
{% endfor %}
You can get the generated id of a form field like so:
{{ field.auto_id }}
This is a duplicate of https://stackoverflow.com/a/3765016/1637351
Edit:
Though I don't understand why you're trying to put the id value as the name attribute. That will result in the rendered html being
<input name="id_field_name" />
I would be taken care of automatically if using the field template tag.
Instead, I'd try a different approach than you're using, using django_widget_tweaks to programmatically add attributes.
For example, in your template...
{% for field in wizard.form %}
<div class="row">
<label class="col-xs-4" for={{field.auto_id}}>{{ field.label }}</label>
<div class="col-xs-2">
<div class="form-group">
{{field|attr:'class:form-control}}
</div>
</div>
</div>
{% endfor %}
This will automatically generate the input for each field with class='form-control' as part of the tag. The id/name will automatically be taken care of by rendering field.