sending data from a "form action" to a views function in django - html

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 %}

Related

Formset not showing label in django

I am developing a domestic worker booking app in django
When I try to pass the formset, I am not geting the label of that field. I am only getting the field in html.
{% for formset in formsets %}
<form method="post">
{% for form in formset %}
{% for field in form %}
<div>
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
{% for error in field.errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endfor %}
{% endfor %}
<input type="submit" value="Submit">
</form>
{% endfor %}
This the html code
def staffApply(request,pk):
if request.method == 'POST':
selected_domestic_works = request.POST.getlist('domestic_works')
formsets = []
if 'cook' in selected_domestic_works:
formsets.append(CookingForm(request.POST,prefix='cook'))
if 'driver' in selected_domestic_works:
formsets.append(DriverForm(request.POST,prefix='driver'))
print(formsets)
return render(request, 'staffApply2.html', {'formsets': formsets})
return render(request,'staffapply.html',{'uid':pk})
enter code here
This is my views.py
class CookingForm(ModelForm):
food_cooked=(('veg','veg'),
('non-veg','non-veg'),
('both','both')
)
class Meta:
model = Cook
fields = '__all__'
exclude=['user']
widgets={
'food_cooked':forms.widgets.RadioSelect(),
'type_of_cuisine':forms.widgets.CheckboxSelectMultiple()
}
This is my forms.py
I am getting the fields to type. But I am not getting hte label for those fields. Please help me fix this.
class Cook(models.Model):
food_cooked=(('veg','veg'),
('non-veg','non-veg'),
('both','both')
)
type_of_cuisine=(('NorthIndian','NorthIndian'),
('SouthIndian','SouthIndian'),
('Chettinadu','Chettinadu'),
('Chinese','Chinese'),
)
user=models.ForeignKey(User,on_delete=models.CASCADE)
food_cooked=models.CharField(choices=food_cooked,max_length=30)
type_of_cuisine=models.CharField(choices=type_of_cuisine,max_length=30)
experience=models.IntegerField()
wages_expected=models.IntegerField()
dishwashing_flag=models.BooleanField()
wages_for_dishwashing=models.IntegerField(null=True)
desc=models.TextField(max_length=200)
This is my models.py
You have one extra loop, with the wrong naming so you cannot access {{ field.label }} on your loops its like you are trying something like {{ form.field.attribute.label }}, the correct way would be the following:
{% for form in formsets %}
<form method="post">
{% for field in form %}
<div>
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
{% for error in field.errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endfor %}
<input type="submit" value="Submit">
</form>
{% endfor %}
That being said, you can also use Django form rendering options, instead of doing it manually.
{% for form in formsets %}
<form method="post">
{{form.as_p}}
<input type="submit" value="Submit">
</form>
{% endfor %}

Action attribute in html form tag is not sending the data in django

I want to send song.id from each song in an album model but every time I send it, I get A KeyError
This is what I wrote in details.html
{% if error_message %}
<p><strong>{{ error_message }}</strong></p>
{% endif %}
<form action="{% url 'music:favorite' album.id %}">
{% csrf_token %}
{% for song in album.song_set.all %}
<input type="radio" id="song{{ song.id }}" name="song" value="{{ song.id }}">
<label for="song{{ song.id }}">
{{ song.song_title }}
{% if song.is_favorite %}
<img src="https://png.pngtree.com/png-vector/20190726/ourmid/pngtree-cute-light-star-with-black-frame-png-image_1633374.jpg">
{% endif %}
</label>
<br>
{% endfor %}
<input type="submit" value="Favorite">
</form>
This is my views.py
def favorite(request, album_id):
album = get_object_or_404(Album, pk=album_id)
try:
selected_song = album.song_set.get(pk=request.POST['song'])
except (KeyError, Song.DoesNotExist):
return render(request, 'music/detail.html', {
'album':album,
'error_message':"You did not select a valid song",
})
else:
selected_song.is_favorite = True
selected_song.save()
return render(request, 'music/detail.html', {'album':album})
I am answering my own question. So in the form tag, I had to include the method attribute and set it to POST.
Change
<form action="{% url 'music:favorite' album.id %}">
To
<form action="{% url 'music:favorite' album.id %}" method="POST">

Hide/Show form on button click in Flask

I was learning to create a wtf Flask web form which was:
class Update(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
email = StringField('Email', validators=[DataRequired(), Email()])
pic = FileField('Update Profile Pic', validators=[FileAllowed(['jpg','png'])])
submit = SubmitField('Update')
What I wanted to do was that the form would load on the same page on a button click without making a seperate html page for the form. How can it be done either by using Flask or HTML? If any changes to route have to be made, please mention that as well.
HTML Code:
<div class="content-section">
<form method="POST" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Account Info</legend>
<div class="form-group">
{{ form.username.label(class="form-control-label") }}
{% if form.username.errors %}
{{ form.username(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.username.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.username(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.email.label(class="form-control-label") }}
{% if form.email.errors %}
{{ form.email(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.email.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.email(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ form.pic.label() }}
{{ form.pic(class="form-control-file") }}
{% if form.pic.errors %}
{% for error in form.picture.errors %}
<span class="text-danger">{{error}}</span><br>
{% endfor %}
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
You can load your form normally, and set it's visibility to hidden. If someone clicks on the button, just change visibility of a form to visible.
Example below:
function toggle_display(){
el = document.querySelector('.content_section');
if(el.style.visibility == 'hidden'){
el.style.visibility = 'visible'
}else{
el.style.visibility = 'hidden'
}
}
<button onclick="toggle_display()">Toggle display</button>
<div class="content_section">See me no more</div>
EDIT:
One more thing to mention, visibility property when set to hidden takes up space, even if it's hidden. To completly remove space that form will take and hide form, set display property to none. To show it again, set display to block.

How to call a function from a html button - Django

What I'm trying to do is execute a function when the user clicks the "add to cart" button, the product displayed on the screen will be added to the cart model. So far i've figured out how to add objects to the cart from the shell. What i don't know is how to call a python function with html and how to pass the object to it. Thank you in advance.
my template.html:
{% extends 'templates/header.html' %}
<title>{% block head_title %}{{ object.name }} - {{ block.super }}{% endblock head_title %}</title>
{% block head %}
{{ block.super }}
{% load staticfiles %}
<link rel = "stylesheet" href = "{% static 'css/products_detail.style.css' %}" type = "text/css"/>
{% endblock head %}
{% block content %}
<div id='Product-Page'>
<p>{{ object.name }}</p>
<hr>
<div id='Product-Image'>
<img src='{{ object.image.url }}' alt='{{ object.image.name }}'/>
</div>
<div id='Product-Details'>
<p id='Product-Name'>{{ object.name }}</p>
<p id='Product-Description'><small>{{ object.description }}</small></p>
</div>
<div id='Product-Buy'>
<p id='Product-Price'>{{ object.price }} лв.</p>
<p id='Product-Quantity'>{{ object.quantity }} </p>
<form class='Product-Form' method='post' action='#'>
{% csrf_token %}
<input class='Product-Button' type='submit' value='Buy Now'>
</form>
<form class='Product-Form' method='get' action=''>
{% csrf_token %}
<input class='Product-Button' type='submit' value='Add to cart'>
</form>
</div>
<hr>
</div>
{% endblock content%}
my view:
class ProductDetailView(DetailView):
template_name = 'products/products_detail.html'
def get_object(self, *args, **kwargs):
slug = self.kwargs.get('slug')
return get_object_or_404(Product, slug=slug)
You can add hidden inputs to each form (both of which should be POST methods, not GET) then add a post method to your view. Something like:
<form class='Product-Form' method='post'>
{% csrf_token %}
<input name="buy-now" hidden>
<input name="pk" value="{{ object.pk }}" hidden>
<button type="submit" class="btn">Buy Now</button>
</form>
<form class='Product-Form' method='post'>
{% csrf_token %}
<input name="add-to-cart" hidden>
<input name="pk" value="{{ object.pk }}" hidden>
<button type="submit" class="btn">Add to cart</button>
</form>
Then in your view:
class ProductDetailView(DetailView):
template_name = 'products/products_detail.html'
def get_object(self, *args, **kwargs):
slug = self.kwargs.get('slug')
return get_object_or_404(Product, slug=slug)
def post(self, request, *args, **kwargs):
name = request.POST.get("pk")
product = Product.objects.get(pk=pk)
if "buy-now" in request.POST:
#Do something to buy.
print('buy now ' + product.name)
elif "add-to-cart" in request.POST:
#Add to cart.
print('add to cart ' + product.name)
return redirect('home')
Or you can do it via AJAX if you don't want to reload the page.

Using html input tag while iterating over Django fields

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...