Django not passing data to views on POST request - python

I've created a drop-down menu that is supposed to pass data to a view that'll help filter a queryset. However, it doesn't seem like the data is actually being passed to the view. Below is the relevant code I've written.
template.html
<!-- Query based content for dropdown menu -->
<form method="POST" action="{% url 'property-selected' %}" id="property-select">
{% csrf_token %}
<select class="dropdown-content" onchange="this.form.submit()" name="property-select">
{% if current_user_meters %}
<option disabled selected> -- select an option -- </option>
{% for meter in current_user_meters %}
<option class="dropdown-menu-option" value="{{meter.id}}">{{meter.name}}</option>
{% endfor %}
{% else %}
<option>You don't have any meters</option>
{% endif %}
</select>
</form>
views.py
def property_selected(request):
if request.method == 'POST':
selection = request.POST.get('property-select')
current_user_groups = Group.objects.filter(
id__in=request.user.groups.all()
)
current_user_properties = Property.objects.filter(
groups__in=current_user_groups
)
current_user_meters = Meter.objects.filter(
meter_id__in=current_user_properties
)
selected_meters = Meter.objects.filter(name=selection)
selected_meter_data = MeterData.objects.filter(
name=selection
).order_by('date')
return render(request, 'properties/property-selected.html', {
'current_user_meters': current_user_meters,
'selection': selection,
'selectected_meters': selected_meters,
'selected_meter_data': selected_meter_data,
})
For the querysets in the views file, the selection variable doesn't seem to be getting anything, which is where I want the data from the POST request to go. I want the data from the POST request to go there so my selected_meters and selected_meter_data queries will work as intended.

The values in the property-select options are IDs, but you are trying to filter MeterData by name with those values. Either filter by id, or use the name attribute as the option values.

You need to either use a form to ingest your data (thereby providing cleaning and validation) or access request.body instead of request.post, and then parse it for yourself.
From the documentation, emphasis mine (https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.POST):
HttpRequest.POST¶ A dictionary-like object containing all given HTTP
POST parameters, providing that the request contains form data. See
the QueryDict documentation below. If you need to access raw or
non-form data posted in the request, access this through the
HttpRequest.body attribute instead.
It’s possible that a request can come in via POST with an empty POST
dictionary – if, say, a form is requested via the POST HTTP method but
does not include form data. Therefore, you shouldn’t use if
request.POST to check for use of the POST method; instead, use if
request.method == "POST" (see HttpRequest.method).
I'd recommend passing the request into a form. It gives you some nice functionality down the line, and lets you avoid parsing request.body for yourself, even if it's an extra step.

Related

How to Prefill Form with URL Parameters (Django)

I would like to prefill a form with URL parameters, but I am unsure as to how I should configure my URLs. I need to fill multiple fields, so is using URL parameters still the best method? In the tutorials I have been reviewing, most cases only use 1 or 2 parameters from the GET request. In my view, I am only handling one field currently as I am having trouble with just one parameter. You can see in the form model the other fields I would like to fill. Any help is greatly appreciated!
views.py
def new_opportunity_confirm(request):
form_class = OpportunityForm
account_manager = request.GET.get('account_manager')
form = form_class(initial={'account_manager': account_manager})
return render(request, 'website/new_opportunity_confirm.html', {'form': form})
urls.py
re_path(r'new_opportunity/new_opportunity_confirm/(?P<account_manager>\w+)/$', view=views.new_opportunity_confirm,
name='new_opportunity_confirm'),
new_opportunity_confirm.html
<form action="" method="post" name="newOpportunityForm" id="newOpportunityForm">
{% csrf_token %}
<div class="field">
<label class="label">Account Manager:</label>
<div class="select">
<select name="account_manager" id="account_manager" required>
<option value="{{ form }}">{{ form }}</option>
</select>
</div>
</div>
It depend if you want your parameters to be part of the url or not, and in your case I would suggest not, but let's see both method.
For GET parameters (url?var1=poney&var2=unicorn):
You do not need to configure your url. Django will do the work for you, you just have to configure what is before the interrogation point.
You can then access those with request.GET.get("var1"), or request.GET.get("var1", "default") if you want a default value in case it's not found.
In your template, you can access it with {{ request.GET.var1 }}.
For parameters in the url (url/poney/unicorn):
You need to configure the url to capture the part you want, and you need to have a parameter in the receiving view to get the one in the URL:
def new_opportunity_confirm(request, account_manager):
You can then access it like any other variable, and send it to your template if you want to have access to it there.
Again, that second way does not seem fitting to what you want to achieve.
You were halfway there, you just mixed a little bit of both methods.

Compare url in django

I am trying to hide a content when a user is visiting his own profile. I tried the code below but it didn't work. What could be wrong.
{% if request.path == "/account/users/{{ request.user.username }}/" %}
{% else %}
<img src="https://tyllplus.com/static/arrow-orange-down-vector-1.png" width="30" height="30">
{% endif %}
(Advanced) string processing should not be done in the template. Especially not with URLs, since you might later want to change the view. Even if you manage to get it work, if you later have a prefixed path, it can start failing. This method would also heavily depend on the format of the URL: if you later specify a URL where you use the id instead of the username, then you will need to look for all all URL processing that depends on this format. This is not elegant design.
Of course simple processing is no problem. For example adding comma separators to a number, etc. is typically handled by template tags. But URLs - in my opinion - do not really fit in that category.
You better encode this logic in the view (or make sure that you easily can detect it with elements from the view).
For example for a DetailView:
from django.views.generic.detail import DetailView
from django.contrib.auth.models import User
class UserDetailView(DetailView):
model = User
context_object_name = 'my_user'
template = 'user_detail.html'
We know that the my_user variable will carry the User object ot display, so then we can verify with:
{% if my_user != request.user %}
<!-- show something -->
{% else %}
<!-- show something else -->
{% endif %}

Sending POST data from inside a Django template 'for loop'

With this HTML:
...
{% for thing in things %}
<form method="post">
{% csrf_token %}
{{ thing.name }}
{{ form.value }}
<input type="submit" value="Submit" />
</form>
{% endfor %}
...
My website lists multiple 'things' from my database, so there can be many forms generated on the one page. How can I somehow determine in my views.py, which 'thing's' form is being submitted?
More elaboration:
Imagine you have a page of objects listed one after the other, and each object has a like button associated with it, that adds a like to the object it is next to. That's essentially what I'm trying to do here.
The problem is, I have a form that can process the like, but how do I take that like and add it to the object that it's displayed next to on the page? (by the aforementioned 'for loop')
I'm completely confused on how to go about this, am I looking at the problem the wrong way, or is there a standard idiom around this problem that I don't know about?
Thank you :)
The most common design pattern for model instance updates is to provide the primary key of an object in the url where you are submitting your post data.
# urls.py
from django.conf.urls import *
from library.views import UpdateThing
urlpatterns = patterns('',
url('^update_thing/(?P<pk>[\w-]+)$', UpdateThing.as_view(), name='update_thing'),
# views.py
def my_view(request, pk=None):
if pk:
object = thing.objects.get(pk=pk)
form = MyModelForm(data=request.POST or None, instance=object)
if form.is_valid():
...
Now, let's specify (using Django's url template tag) that we want to submit post data for each object to the correct url.
{% for thing in things %}
<form method="post" action="{% url 'update_thing' thing.pk %}">
{% csrf_token %}
{{ thing.name }}
{{ form.value }}
<input type="submit" value="Submit" />
</form>
{% endfor %}
The url tag does a reverse lookup through your urls for the name kwarg supplied for a given url, and accepting positional arguments (such as, in this case, thing.pk) and, when needed, keyword arguments.
The standard way to handle multiple forms of the same kind on one page with Django is to use Formsets.
It handles the annoying details like displaying errors on one form while preserving the input on others etc.
However, in your specific case that might be overkill. If you just want to create a like for an object, there isn't really any user input that needs to be validated, so you don't really need a form. Just perform a POST to a specified URL, maybe with Javascript. If the user messes with the URL, you display a 404.

A way to render content to a base template

I have a base template which contains header, footer and a block "content" which then I override in different CBVs.
There is little "user-space" divinside a header where I want to keep user's info in case he is logged in and a login form if he is a guest.
Right now the only way that comes in mind is to create a django app and use it as a tag. But I think there is a better solution. Because as far as I know tags can slow down django project in future.
Also I think that maybe I would like to manipulate this div from a view which renders a child template. For example I calculate and display some value on a page but also I want to display it in a "user-space" div as well. I think this can be achieved by pushing data to the user's session and then using this while rendering "user-space" div from another code.
Assuming you have django.contrib.auth.user in your INSTALLED_APPS, you can access the user's login status using user.is_authenticated():
{% if user.is_authenticated %}
<div>Welcome back, {{ user.username }} | <a href='/logout/'>Logout</a></div>
{% else %}
<div>
<form action='/login/' method='POST'>
{{ csrf_token }}
{{ login_form }}
<input type='submit'>
</form>
</div>
{% endif %}
Edit:
In response to your comment:
Let's suppose client does a POST request by which we calculate some number - total price. And I need to display it in that div.
As documented
Define your view:
from django.shortcuts import render
from .forms import MyForm
def simple_view(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = MyForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
result = form.cleaned_data['some_input'] * 50
return render(request, 'my_template.html', {'result': result})
# if a GET (or any other method) we'll create a blank form
else:
form = MyForm()
return render(request, 'my_other_template.html', {'form': form})
Display result in your template:
<!-- my_template.html -->
<div>{{ result }}</div>
Maybe you need to be more specific with your question, this is what I think you are looking for though. You should put logic like you are describing into a view, not a template.

Can't append_entry FieldList in Flask-wtf more than once

I have a form with flask-wtf for uploading images, also file field can be
multiple fields.
my form:
class ComposeForm(Form):
attachment = FieldList(FileField(_('file')), _('attachment'))
add_upload = SubmitField(_('Add upload'))
my view:
if form.validate_on_submit():
if form.add_upload.data:
form.attachment.append_entry()
return render_template('mailbox/compose.html', form=form)
else:
form.attachment.append_entry()
my template:
<form method="POST" enctype="multipart/form-data" action=".">
{% for field in form %}
{{field}}
{% endfor %}
</div>
When I use enctype="multipart/form-data" in the form, append_entry doesn't work. It only appends one more field.
Again I click on add_upload, but after refresh I have again only one field (not two).
How can I fix this? There is no error, I think, because enctype wtform forgets how many fields I have to add more.
You call to append_entry is missing it's data.
From the Documentation:
append_entry([data])
Create a new entry with optional default data.
Entries added in this way will not receive formdata however, and can only receive object data.
If you're trying to get the data that was submitted on the form, you might try to use pop_entry. Or at least doing some debugging and seeing what form.attachment.entries looks like. Does it contain values? What happens when you iterate through those values?

Categories