Remove products from cart in django - django-views

I wrote a code to remove products from cart but instead it adds products to the cart.
Here's my views.py
def delete_cart(request, slug):
products = get_object_or_404(Products, slug=slug)
cart = Cart.objects.get(user = request.user, products=products)
cart.delete()
return redirect('cart')
urls.py
path('delete/cart/item/<slug:slug>/', views.delete_cart, name = 'delete-cart')
What is wrong with this codes?
Any suggestion will be reallly helpful.

Based on your comment, this was a clash between two paths: two paths that have certain possible paths in common. In that case, Django will pick the first one of the matching paths, hence the delete_cart view is never called.
You can further simplify the view logic to:
def delete_cart(request, slug):
Cart.objects.filter(user=request.user, products__slug=products).delete()
return redirect('cart')
furthermore since this is a view that alters entities, this should be done through a POST or DELETE request, not a GET request, so you might want to restrict the view with the #require_http_methods(…) decorator [Django-doc]:
from django.views.decorators.http import require_http_methods
require_http_methods(['POST', 'DELETE'])
def delete_cart(request, slug):
Cart.objects.filter(user=request.user, products__slug=products).delete()
return redirect('cart')
In that case you thus create a mini form that will make the POST request with:
<form method="POST" action="{% url 'delete-cart' product.slug %}">
{% csrf_token %}
<button type="submit">remove from the cart</button>
</form>
with delete-cart the name of the path that refers to the delete_cart view.

Related

Accessing User objects through User.objects.filter for a profile page

How can i query User model to get user informations like: username, email, first name, and last name into his profile. I want to display all these information in his profile page.
as you can see here in the Profle view i'm willing to access user email, but how can i query the database to get all these information in the profile page ?
def profile(request, pk):
user_profile = User.objects.filter(email=request.user)
context = {'user_profile':user_profile}
return render(request, 'profile.html', context)
the profile template:
<p>{{user.email}}</p>
If you are looking for the logged_in user details, you already have that as part of the request object. So you can use the following in your template without doing a lookup in your view:
<p>Username: {{request.user.username}}</p>
<p>Email: {{request.user.email}}</p>
If you want to use the PK value being passed to your view, so that a visitor can visit the profile of another user (eg, the Primary Key is to identify another user account) you can do a lookup on that. As Sunderam Dubey notes, the best method for that is via a get_object_or_404() call in your view
views.py
from django.shortcuts import get_object_or_404
def profile(request, pk):
user = get_object_or_404(User,id=pk)
context = {'user_context':user}
return render(request, 'profile.html', context)
profile.html
<p>Email : {{user_context.email}}</p>
<p>Username : {{user_context.username}}</p>
Note the variable object names with _context at the end. You don't have to follow this naming convention, but what's important is that the things I have named 'user_context' have the same name.
If you want to access only current logged in user information so you should not use filter() as it gives you queryset and used via loops which we run in templates. You should use get_object_or_404[django-doc] for displaying data of single object. It gives 404 if found nothing.
Views.py
from django.shortcuts import get_object_or_404
def profile(request, pk):
single_user = get_object_or_404(User,pk=pk)
context = {'single_user':single_user}
return render(request, 'profile.html', context)
Template file
{% if single_user %}
<p>Email : {{single_user.email}}</p>
<p>Username: {{single_user.username}}</p>
<p>first name : {{single_user.first_name}}</p>
<p>last name: {{single_user.last_name}}</p>
{% else %}
<p>user is not coming</p>
{% endif %}
I solve my problem by doing this in the profile view:
def profile(request, pk):
user_context = get_object_or_404(User,id=pk)
context = {'user_context':user_context}
return render(request, 'profile.html', context)
I can now be able to access all user information in the template by:
<div class="container">
<div class="row justify-content-center">
<p>Email: {{user.email}}</p>
<br>
<p>Username {{ user.username }}</p>
</div>
</div>

How to reuse Django's admin foreign key widget in admin intermediate pages

I haven't been able to find the answer anywhere on Django's documentation. Though, I'm not surprised given the question is a bit too complex to ask to a search engine.
I'm in a situation where I need to be able reassign a ForeignKey field for one or more entries of a model on Django's admin site.
So far, what I tried to do so using a custom action so that I can select the records I'm interested in and modify them all at once. But, then, I need to select the new related object I want their fk to be reassigned to. So, what I thought to do is an intermediate page where I'd display the fk widget I see all around the admin pages:
But it turns out this widget is really not designed to be publicly used. It's not documented and it's heavily complex to use. So far, I lost several hours digging into Django's code trying to figure how to use it.
I feel like I'm trying to do something really really exotic here so, if there's another solution, I'm all hears.
As shahbaz ahmad suggested, you can use ModelAdmin's autocomplete_fields which creates an select with autocompletion.
But if you're stuck with Django's foreign key widget, because, for instance, you have records which look the same and are indistinguishable in autocomplete, there is a solution.
It turns out ModelAdmin has a get_form method that you can use to retrieve the ModelForm used on the admin page. This method accepts a fields kwargs that you can use to select the fields you want to retrieve in the form. Use it like this:
class MyAdmin(ModelAdmin):
# define the admin subpath to your intermediate page
def get_urls(self):
return [
path(
"intermediate_page/",
self.admin_site.admin_view(self.intermediate_page),
name="intermediate_page",
),
*super().get_urls(),
]
def intermediate_page(self, request):
context = {
# The rest of the context from admin
**self.admin_site.each_context(request),
# Retrieve the admin form
"form": self.get_form(
request,
fields=[], # the fields you're interested in
)
}
return render(request, "admin/intermediate_page.html", context)
If your field is read only – which was my case – there's a workaround to an editable field: you can override the get_readonly_fields method which is called by get_form.
This method accepts an obj parameter which usually takes the model of the object being edited or None when creating a new entry. You can hijack this parameter to force get_readonly_fields exclude fields from read only fields:
def get_readonly_fields(self, request, obj=None):
readony_fields = super().get_readonly_fields(request, obj)
if not (
isinstance(obj, dict)
and isinstance(obj.get("exclude_from_readonly_fields"), Collection)
):
return readony_fields
return set(readony_fields) - set(obj["exclude_from_readonly_fields"])
get_form also has this obj parameter which it passes down to get_readonly_fields so you can call it like this:
# the fields you're interested in
include_fields = []
self.get_form(
request,
obj={"exclude_from_readonly_fields": include_fields},
fields=include_fields
)
Override changelist template of YourModelAdmin class to add one more button apart from add button.
#admin.register(YourModel)
class YourModelAdmin(admin.ModelAdmin):
change_list_template = "custom_your_model_change_list.html"
In custom_your_model_change_list.html,
{% extends "admin/change_list.html" %}
{% block object-tools-items %}
<li>
<a class="button" href="{% url 'your_reassign_url_name' %}">Reassign</a>
</li>
{{ block.super }}
{% endblock %}
Mapped a view to 'your_reassign_url_name' to processed your request.
In urls.py,
urlpatterns = [
path('reassign/', YourReassignView, name='your_reassign_url_name'),
]
forms.py,
class ReassignForm(forms.Form):
# your reassign field
reassign_field = forms.ModelChoiceField(queryset='your queryset')
# Select multiple objects
updatable_objects = forms.ModelMultipleChoiceField(queryset='All objects queryset',
widget=forms.CheckboxSelectMultiple)
In views.py, On GET request you render a form with your required fields and on submit your update your data (reassign values) and after that you redirect admin change_list page.
def YourReassignView(request):
if request.method == 'POST':
form = ReassignForm(request.POST)
if form.is_valid():
# you get value after form submission
reassign_field = form.cleaned_data.get('reassign_field')
updatable_objects = form.cleaned_data.get('updatable_objects')
# your update query
YourModel.objects.filter(
id__in=updatable_objects.values('id')
).update(field_name=reassign_field)
#after that you redirect admin change_list page with a success message
messages.success(request, 'Successfully reassign')
return redirect(reverse('admin:app_label_model_name_changelist'))
else:
context_data = {'form': form}
return render(request, 'reassign_template.html', context=context_data)
else:
form = ReassignForm()
context_data = {'form': form}
return render(request, 'reassign_template.html', context=context_data)
In reassign_template.html,
<form method="POST" action="{% url 'your_reassign_url_name' %}">
{% csrf_token %}
{{ form.as_p }}
<br>
<input type="submit" value="submit">
</form>

How to use search filter when slug is used in URL path

I am trying to add search and ordering filters to a page which is dynamically created with a <slug:slug>. The issue is that as soon as I add my ordering filter I get:
Reverse for 'quiz-results-filtered' with no arguments not found. 1 pattern(s) tried: ['tests\/results\/(?P[-a-zA-Z0-9_]+)\/$']
If I am reading this correctly it is telling me that it can't determine the page when the filter is empty. Problem is that when I do not have the HTML ordering filter on the page it works just fine.
Order_by button HTML:
<!--Order By Button-->
<form action="{% url 'quiz-results-filtered' %}"
class="small-form"
method="get">
<input name="ordering"
type="hidden"
value="-end">
<input class="btn btn-link"
type="submit"
value="Date: most recent">
</form>
URL:
path('tests/', include([
path('results/<slug:slug>/', views.QuizMarkingFilteredList.as_view(), name='quiz-results-filtered'),
...
Views.py (although irrelevant some people made needlessly ask for this):
class QuizMarkingFilteredList(PermissionRequiredMixin, LoginRequiredMixin, QuizMarkerMixin, ListView):
model = Sitting
template_name = 'quiz/sitting_filtered_list.html'
permission_required = ('quiz.view_sittings',)
permission_denied_message = 'User does not have permissions to view quiz sittings.'
def get_ordering(self, *args, **kwargs):
ordering = self.request.GET.get('ordering', 'end')
return ordering
def get_queryset(self):
queryset = super(QuizMarkingFilteredList, self).get_queryset()
queryset = queryset.filter(complete=True)
queryset = queryset.filter(user__supervisor__exact=self.request.user)
queryset = queryset.filter(quiz__url=self.kwargs['slug'])
ordering = self.get_ordering()
if ordering and isinstance(ordering, str):
ordering = (ordering,)
queryset = queryset.order_by(*ordering)
return queryset
This error does not occur when the HTML Order_by filter is removed from the page.
I am sure it is pretty simple but I am not seeing it. Thoughts on why?
Thanks in advance! =D
The issue you are having is that you are trying to use the url "quiz-results-filtered" but it requires a slug keyword argument that you are not passing. You need to pass the argument to the url tag
{% url 'quiz-results-filtered' slug=slug %}
Removing the action attribute from the form tag will cause the form to submit to the current URL, you don't need it here as you are already on the correct path
<form class="small-form">
You also don't need to specify method="get" as it is the default

The "post" method in Django

I created three files:
2- view.py :
class AddTeamView(View):
def get (self, request):
form = TeamForm()
context = {'form': form}
return render(request, 'add_team.html', context)
1-forms.py:
class TeamForm(forms.Form):
name = forms.CharField( max_length='100')
details = forms.CharField(max_length='250')
3-add_team.html:
-here there is another file called "base.html"
{% extends 'base.html' %}
{% block title %}
add team
{% endblock %}
{% block content %}
<form action="/add_team/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
{% endblock %}
and i went to cmd and entered the server "python manage.py runserver"
it appeared on the browser:
"This page isn’t working
If the problem continues, contact the site owner.
HTTP ERROR 405"
A view can support methods like GET, POST, PUT, etc. given the corresponding method exists, so the view should have a .get(..), .post(..), .put(..), etc. function.
Here you only implemented a def get(self, request), and so POST requests are not allowed.
Based on the data you however show, this looks like the typical usecase of a CreateView [Django-doc]. The idea of these views is to encapsulate common scenario's such that by overriding a few attributes, one creates a view that is tailored towards a specific case, like:
class AddTeamView(CreateView):
form_class = TeamForm
template_name = 'add_team.html'
success_url = '/some/success_url'
The TeamForm should probably be a ModelForm, or at least a Form where you override the .save(..) function to properly save your data to the database, since right now, the form is not doing anything (well it receives the data, but after validation, it throws it away).
You might want to override the form_valid(..) function in case you do not want to redirect to the success_url. Furthermore it is very common that the success_url is resolved lazily from a given view name, like:
class AddTeamView(CreateView):
form_class = TeamForm
template_name = 'add_team.html'
success_url = reverse_lazy('view_name')
So, we don’t need to do a conditional to check if the request is a POST or if it’s a GET:
Your views.py:
from django.views.generic import View
class AddTeamView(View):
def post(self, request):
form = TeamForm(request.POST)
if form.is_valid():
new_tm = TeamModel(name=form.cleaned_data['name'], details=form.cleaned_data['details'])
new_tm.save()
return redirect('team_list')
return render(request, 'add_team.html', {'form': form})
def get(self, request):
form = TeamForm()
return render(request, 'add_team.html', {'form': form})
Hope this help you...

Dynamically get template name in bound Django form

I am trying to render a Django contact form on any arbitrary page. I am doing it with a request context processor and a template include. This allows me to display the form fine anywhere I want. Then I have a special URL that accepts POST requests (on GET, I just redirect them). If the form is valid, I send an email, and redirect to a success page. On form invalid, I know to pass the form bound with errors, but...I don't know which template to specify because the form is an include and the parent template could be anywhere.
The only way to get something in a Django view is from the request. I can get the path, and with more work, probably the original view from where the POST came from, but that doesn't get me the template.
# urls.py
url(r'^services/$', 'website.views.services', name='services'),
url(r'^services/contact/$', 'website.views.services_contact', name='services_contact'),
url(r'^services/contact/done/$', 'website.views.services_contact_done', name='services_contact_done')
# views.py
class ServicesView(TemplateView):
template_name = 'services/services.html'
services = ServicesView.as_view()
class ServicesContactView(View):
def get(self, request, *args, **kwargs):
return redirect('services')
def post(self, request, *args, **kwargs):
form = ContactForm(request.POST)
if form.is_valid():
form.send_email()
return redirect('services_contact_done')
else:
return render(request, ????, {'contact_form': form})
services_contact = ServicesContactView.as_view()
# contact.html
<h2>Contact me</h2>
<p>Enter your email to receive your questionnaire</p>
<form action="{% url 'services_contact' %}" method="post">
{% csrf_token %}
{% if contact_form.non_field_errors %}
{{ contact_form.non_field_errors }}
{% endif %}
{{ contact_form.as_p }}
<button type="submit" name="submit">Send questionnaire</button>
</form>
# home.html
{% extends "base.html" %}
{% block content %}
<h1>{{ site.name }}</h1>
{% include "services/contact.html" %}
{% endblock %}
The typical Django form view is somewhat silent on form invalid in that its scenario is mostly similar to an unbound form, so it's all just render in the end. My scenario is different due to the template include.
You could set up a session variable every time you render a template and use it afterwards when you need it :
request.session['template']="nameOfTemplate"
.
return render(request, request.session.get('template', 'default.html'), {'contact_form': form})
I know it requires to write a line of code every time you render a template, but that's the best solution I could think of.
If anybody needs this answer, I figured it out on my own. It's possible, but a different approach is required. First, a request context processor is not appropriate for this situation. They're fairly dumb because they just get something once and stick it in the context. Their only advantage is their global nature.
My context processor:
def contact_form(request):
"""
Gets the contact form and adds it to the request context.
You almost certainly don't want to do this.
"""
form = ContactForm()
return {'contact_form': form}
The nature of forms is that they act differently after being processed by Django's validation machinery, specifically ContactForm() is an unbound form and will always be. You don't want to do this (unless you want a form that simply displays but doesn't work). The TEMPLATE_CONTEXT_PROCESSORS should be edited to remove this processor.
Now the burden on displaying the form is back on the view, which also means just about any view must be able to handle POST requests as well. This means that editing each view that wants a contact form is required, but we can use the power of class-based views and mixins to handle most of the repetition.
ServicesView remains almost the same as a TemplateView, except with a mixin that will handle the form. This way, the template name always remains the same (my original problem), but with additional form power.
class ServicesView(ContactMixin, TemplateView):
template_name = 'services/services.html'
services = ServicesView.as_view()
ContactMixin uses FormMixin, to create and display a form, and ProcessFormView to handle the GET and POST requests for the form. And because the form's nature changes with different kinds of requests (unsubmitted, submitted and invalid, submitted and valid), get_context_data needs to be updated with the correct form class instance. Lastly, we probably want to prefix (namespace) our form because it can can be used anywhere, and we want to avoid conflicts when another possible form can POST to the same view. Thus, the mixin is:
class ContactMixin(FormMixin, ProcessFormView):
form_class = ContactForm
success_url = reverse_lazy('contact_done')
def get_form_kwargs(self):
kwargs = super(ContactMixin, self).get_form_kwargs()
kwargs['prefix'] = 'contact'
return kwargs
def get_context_data(self, **kwargs):
context = super(ContactMixin, self).get_context_data(**kwargs)
form_class = self.get_form_class()
context['contact_form'] = self.get_form(form_class)
return context
def form_valid(self, form):
form.send_email()
return super(ContactMixin, self).form_valid(form)
The subtleties of self.get_form_class() were almost lost on me if it were not for an example in the docs (of what not to do, heh) and another StackOverflow answer, where I would've usually just said self.form_class, which ignores the processing of the form.
Now I simply add ContactMixin to any view and {% include "includes/contact.html" %} to any template.