Getting the id of a model from a button in Django - python

I currently have a template set up so that it loops through all of my menu items with a button that correspond to a post request in my views.py
<td>
{% for menu_items in menu %}
{{ menu_items.Menu_name }}
{% endfor %}
</td>
<td>
{% for menu_desc in menu %}
{{ menu_desc.Menu_Desc }}
{% endfor %}
</td>
<form method="post">
{% csrf_token %}
<th><input class="btn btn-success" type="submit" value="Add To Cart" name="add">.
</th>
</form>
In my views file I have a if statement that tries to get the id of the model that was clicked.
However, i'm only able to get the Query Set and not the specific ID of the model.
def ordering(request):
latest_order = Order.objects.all()
menu = Menu.objects.all()
if 'add' in request.POST:
user_order = Order.objects.get(name='')
print(menu.id)
return render(request, 'users/ordering.html', {'title':'Ordering', 'latest_order': latest_order, 'menu':menu})

When working with queryset, whatever is returned has a primary key and not id as you would occasionally think, therefore use
item.pk instead of item.id

You cannot get the id of QuerySet, in the situation, you can treat the QuerySet like an iterable, try to print this way:
menu = Menu.objects.all()
print([item.id for item in menu])

Related

Problems with edit button in Django

i'm novice and just trying Django functionality. I create button to edit value in db in Django, but it doesn't work write. Problem in that: when i press the button (from notes.html), it's redirect to page edit_note.html, but values in fields is empty. When i manually press adress in browser, it work's normally. I don't understand where i maked mistake.
in views.py:
class EditNoteView(UpdateView):
model = Notes
form_class = NotesForm
template_name = 'notes/edit_notes.html'
context_object_name = 'note'
def get_success_url(self):
return reverse_lazy('edit_note', kwargs={'pk': self.object.pk})
in urls.py:
urlpatterns = [
path('', home, name='home'),
path('notes/', NoteView.as_view(), name='notes'),
path('<int:pk>/edit', EditNoteView.as_view(), name='edit_note'),
in notes.html:
{% for i in all_notes %}
<tr>
<td>{{ i.notes_category }}</td>
<td>{{ i.title }}</td>
<td>{{ i.text }}</td>
<td>{{ i.reminder_data_time }}</td>
<td>
<form action="{% url 'edit_note' i.id %}" method="post">
{% csrf_token %}
<div class="btn-small-group">
<button type="submit">Edit</button>
</div>
</form>
</td>
<td>
<div class="btn-small-group">
<button type="submit">Delete</button>
</div>
</td>
{% endfor %}
in edit_notes.html:
<form action="{% url 'edit_note' note.id %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<div class="btn-small-group">
<button type="submit">Edit</button>
</div>
</form>
after press in notes.html on button "edit"
enter press adress in browser
The problem is the method you are using to access the edit form.
You are using the POST method instead of a GET method.
This make Django want to save the edited object instead of displaying the edit form. You should get some validation errors.
Solution
Change the request method to a GET method
Or use an anchor tag as below
Edit

Django getting right return value for post.request

I am trying to get the product id using request.post in Django. I am currently testing using the console, but the only product_id value I am returned is 1.
This is the particular function in the view:
def test_view(request):
cart_obj, new_obj = Cart.objects.new_or_get(request)
my_carts_current_entries = Entry.objects.filter(cart=cart_obj)
products = Product.objects.all()
if request.POST:
product_id = request.POST.get('product_id')
entry_quantity = request.POST.get('entry_quantity')
product_obj = Product.objects.get(id=product_id)
print(product_id)
# print(entry_quantity)
# Entry.objects.create(cart=cart_obj, product=product_obj, quantity=product_quantity)
return render(request, 'carts/test.html', {'cart_obj': cart_obj, 'my_carts_current_entries': my_carts_current_entries,
'products': products})
This is the html on the template.
<form method="POST">
<br>
{% csrf_token %}
{% for product in products %}
{{ product.name }} <br>
<button>Add to Basket</button>
{{ product.id }}
<input type="hidden" name='product_id' value='{{ product.id }}'>
<br>
{% endfor %}
</form>
Your problem is that you have as many <input> tags within 1 <form> as many products you have displayed. They all have the same name, so you always get the value of the first one.
I'd recommend getting rid of <input> and attaching the value of product.id to the button itself (or <input type="submit"> to be exact). Here's the more descriptive explanation:
How can I build multiple submit buttons django form?
An alternative would be to change your code to have multiple forms, like that:
{% for product in products %}
<form method="POST">
{% csrf_token %}
{{ product.name }}
<br/>
<button>Add to Basket</button>
{{ product.id }}
<input type="hidden" name='product_id' value='{{ product.id }}'>
</form>
<br/>
{% endfor %}

non_form_errors doesn't show

I am working with inlineformset_factory and created a forms.BaseInlineFormSet to override the clean function to make some validations. the validation works and the form isn't accepted but the ValidationError message doesn't appear when the page is reloaded.
here is my form class:
class BaseDetailFormSet(forms.BaseInlineFormSet):
def clean(self):
super(BaseDetailFormSet, self).clean()
if any(self.errors):
return self.errors
for form in self.forms:
product = form.cleaned_data['product']
if form.cleaned_data['quantity_sold'] > product.quantity_in_stock:
raise forms.ValidationError(_('not enough products'))
DetailFormset = inlineformset_factory(Invoices,
InvoiceDetail,
fields=('product', 'quantity_sold'),
widgets= {'product': forms.Select(
attrs={
'class': 'search',
'data-live-search': 'true'
})},
formset=BaseDetailFormSet,
extra=1)
and my html code:
<form class="form-inline" method="post" action="">
{% csrf_token%}
{% include '_layouts/form_snippet.html' %}
<table class="table">
{{inlines.management_form}}
{%for form in inlines.forms%}
{% if forloop.first%}
<thead>
<tr>
{%for field in form.visible_fields%}
<th>{{field.label|capfirst}}</th>
{%endfor%}
{%endif%}
</tr>
</thead>
<tr class="formset_row">
{{ form.non_form_errors }}
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<button type="submit" value="Add" class="btn btn-inverse">{% trans 'Submit Invoice' %}</button>
</form>
any ideas how to show the validation error message or why it isn't shown.
non_form_errors belongs to the formset not the form.
So you can display it with:
{{ formset.non_form_errors }}
Or in your case, since you use the variable inlines:
{{ inlines.non_form_errors }}
Since it belongs to the formset, you should do this outside of the forloop through the forms.
If you have errors on the form that don't belong to any field, then you access these with
{{ form.non_field_errors }}

Django-Filter: Dynamically Create Queryset on Search or Hide Queryset until Search

I am using django-filter to search a model. Here is the code:
filters.py:
class PersonFilter(django_filters.FilterSet):
lastName = django_filters.CharFilter(lookup_expr='icontains')
firstName = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Person
fields = ['lastName', 'firstName']
views.py:
def search(request):
people = Person.objects.all()
people = PersonFilter(request.GET, queryset=people)
context = {'filter': people}
return render(request, 'myapp/template.html', context)
template.html:
<form method="get">
{{ filter.form.as_p }}
<button type="submit">Search</button>
</form>
<table>
{% for field in filter.qs %}
<tr>
<td>
{{ field.idno }}
</td>
<td>
{{ field.lastName }}
</td>
<td>
{{ field.firstName }}
</td>
<td>
{{ field.status }}
</td>
</tr>
{% endfor %}
</table>
{% endblock %}
</body>
</html>
Right now, this results in a table that mirrors my model with search boxes for first name and last name. The search works perfectly.
How do I prevent the table of data from showing up initially? Logically, this could be done superficially (hide) or, better yet, substantially (dynamically create queryset). Is this possible?
You can leverage the FilterSet's is_bound property, although you would need to change the view code to only provide the request query params when the form has been submitted.
def search(request):
submitted = 'submitted' in request.GET
data = request.GET if submitted else None
people = PersonFilter(data, queryset=Person.objects.all())
return render(request, 'myapp/template.html', {'filter': people})
<form method="get">
{{ filter.form.as_p }}
<button type="submit" name="submitted">Search</button>
<!-- ^^^^ added 'name' parameter -->
</form>
{% if filter.is_bound %}
<table>
{% for person in filter.qs %}
...

'dict' object has no attribute 'all_aircraft'

I have a list of posts that the user is able to favorite and save to their to their account. However, I keep getting the error message above. Does anybody have any idea as to where the issue lies?
views.py:
def AircraftFavourite(request, id=None):
instance = get_object_or_404(Aircraft, id=id)
queryset = Aircraft.objects.all()
context = {'all_aircraft' : queryset}
try:
selected_aircraft = context.all_aircraft.get(pk=request.POST['context'])
except(KeyError, Aircraft.DoesNotExist):
return render(request,'aircraft.html', {
"aircraft" : instance,
"error_message" : "You did not select a valid aircraft",
})
else:
selected_aircraft.is_favorite = True
selected_aircraft.save()
return render(request,'aircraft.html', context)
urls.py
urlpatterns = [
url(r'^detail/(?P<id>\d+)/$', AircraftDetail, name='AircraftDetail'),
url(r'^(?P<id>\d+)/favourite/$', AircraftFavourite, name='AircraftFavourite'),]
aircraft.html
{% block content %}
{% for a in all_aircraft %}
<table>
<tr>
<th> {{ a.title }}
</th>
</tr>
<tr>
<td>
<form action="{% url 'AircraftFavourite' id=a.id %}" method="post">
{% csrf_token %}
<input type="submit" id="aircraft{{ forloop.counter }}" name ="aircraft" value="{{ a.id }}">
<label for="aircraft{{ forloop.counter }}"
{% if aircraft.is_favourite %}
<img src="http://i.imgur.com/b9b13Rd.png" />
{% endif %}
<input type="submit" value="Favourite"><br>
</form>
</td>
</tr>
</table>
{% endfor %}
{% endblock %}
In django templates attribute access and item access are equivalent:
template.html
#rendered with context = dict(a=dict(something=5), b=SomeObject))
<body>
{{ a.something }} vs {{ b.something }}
</body>
Both will work and django figures out whether to access the attribute or get the item.
However in Python, the attribute access with the . is not equivalent to item access with the brackets [ ].
Your script should be changed to reflect this:
try:
selected_aircraft = context['all_aircraft'].get(pk=request.POST['context'])
Or ideally just access the queryset directly, so the code better reflects what you're doing:
try:
selected_aircraft = queryset.get(pk=request.POST['context'])
Change to this:
selected_aircraft = get_object_or_404(Aircraft, id=request.POST['context'])

Categories