Django dropdown filter with ajax? - python

I need to update my template when the user chooses a value of the dropdown list. Is there a possibility to do this with an ajax call or similiar? It would be perfect that if the user is selecting a value of the dropdown, this value would be send to my view and the content in my page would update without page refresh.
I tried different things but nothing worked... Any suggestion is welcome.
Here's my shortened code:
models.py
class Author(models.Model):
id = models.IntegerField(primary_key=True)
author = models.CharField(max_length=50)
status = models.CharField(max_length=50)
[...]
views.py
def authors(request):
authors = Author.objects.all()
if request.method="GET":
authors = Author.objects.filter(status = filter)
template
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Choose status
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="#">Staff</a>
<a class="dropdown-item" href="#">Admin</a>
[...]
</div>
</div>
{% for author in authors %}
{{author.author}}
{% endfor %}

template
<a class="dropdown-item" href="{% url 'admin' %}">Staff</a>
<a class="dropdown-item" href="{% url 'staff' %}">Admin</a>
urls.py
url(r'^admin/', authors_admin, name='admin'),
url(r'^staff/', authors_staff, name='staff'),
views.py
def authors_admin(request):
authors = Author.objects.all()
if request.method="GET":
authors = Author.objects.filter(status = 'admin')
return render(request, 'template.html', {
'authors ': authors
})
def authors_staff(request):
authors = Author.objects.all()
if request.method="GET":
authors = Author.objects.filter(status = 'staff')
return render(request, 'template.html', {
'authors ': authors
})

Related

How can I order/sort a filtered search query and render it on another template on Django?

I am trying to change the ordering of a filtered search query on Django. I am using class based ListView for my search views. I am able to render a filtered queryset from search, but how can I change the order of the same queryset with the same searches and render it on another page. Kind of like how twitter can order searches by top or new. I tried making a different view and changing the order, but I am not sure how I can translate the same search query onto the new view. Please help! Below is my code.
views.py
class search_view(ListView):
model = Post
template_name = 'main/search.html'
context_object_name = 'posts'
paginate_by = 2
# searches through everything using Q import
def get_queryset(self, *args, **kwargs):
q = self.request.GET.get('q')
self.posts = Post.objects.filter(
Q(ticker__icontains=q) |
Q(user__username__icontains=q) |
Q(content__icontains=q) |
Q(tags__name__icontains=q)
).annotate(
upvoted=Exists(Post.upvotes.through.objects.filter(
user_id=self.request.user.id,
post_id=OuterRef('pk')
))).order_by('-date_traded')
return self.posts
template.html
<a class="btn btn-secondary dropdown-toggle mb-3 ml-2" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Sort
</a>
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
<a class="dropdown-item" href='?q={{ request.GET.q }}'>New</a>
<a class="dropdown-item" href="#">Top</a> <!-- I would like to render the newly sorted results from here-->
</div>
<!-- the get request for the query-->
<form class="form-inline" method="GET" action="{% url 'main:search' %}">
<input class="form-control mt-2 mr-sm-2" type="search" placeholder="Search by ticker/tags" aria-label="Search" name="q">
<button class="btn btn-outline-info mt-2 mr-sm-2" type="submit" value="Search">Search</button>
</form>
Typically you would use a second get parameter order_by let's say it will look like: domain.com/view/?q=searchterm&order_by=-date_traded would keep current functionality if you just pass the order_by value to the queryset ordering.
After this change you can add any a tag with href in your template with needed order_by as param.
Updated view to support second param:
class search_view(ListView):
model = Post
template_name = 'main/search.html'
context_object_name = 'posts'
paginate_by = 2
# searches through everything using Q import
def get_queryset(self, *args, **kwargs):
q = self.request.GET.get('q')
order_by = self.request.GET.get('order_by', '-date_traded')
self.posts = Post.objects.filter(
Q(ticker__icontains=q) |
Q(user__username__icontains=q) |
Q(content__icontains=q) |
Q(tags__name__icontains=q)
).annotate(
upvoted=Exists(Post.upvotes.through.objects.filter(
user_id=self.request.user.id,
post_id=OuterRef('pk')
))).order_by(order_by)
return self.posts
And the template:
<a class="btn btn-secondary dropdown-toggle mb-3 ml-2" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Sort
</a>
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
<a class="dropdown-item" href='?q={{ request.GET.q
}}&order_by=-date_traded'>New</a>
<a class="dropdown-item" href='?q={{ request.GET.q
}}&order_by=-other_field'>Top</a> <!-- I would like to render the newly sorted results from here-->
</div>
<!-- the get request for the query-->
<form class="form-inline" method="GET" action="{% url 'main:search' %}">
<input class="form-control mt-2 mr-sm-2" type="search" placeholder="Search by ticker/tags" aria-label="Search" name="q">
<button class="btn btn-outline-info mt-2 mr-sm-2" type="submit" value="Search">Search</button>
</form>
Edit. So just to clarify I believe the same view and same template should work for this usecase, just the queryset order is different. If not, please update the requirements.
I had a similar situation. I tried using FBV. This below code sorted my case:
def songs(request, filter_by):
if not request.user.is_authenticated:
return render(request, 'music/login.html')
else:
try:
song_ids = []
for album in Album.objects.all():
for song in album.song_set.all():
song_ids.append(song.pk)
users_songs = Song.objects.filter(pk__in=song_ids)
if filter_by == 'favorites':
users_songs = users_songs.filter(is_favorite=True)
except Album.DoesNotExist:
users_songs = []
return render(request, 'music/songs.html', {
'song_list': users_songs,
'filter_by': filter_by,
})
Hope this will help you. Like #david said you dont need another template or view.

How can i add something to the database with a submit button in Django?

I'm making a grocery list web app in django and i have a page with your groceries list and i have a page with all the products you can add to your list.
every product has a button "add to list". The intention is that when you click on that button that that product automatically becomes added to the groceries list. Does someone know how to do that? thank you in advance.
The Groceries List page
The all products page
models.py
from django.db import models
# Create your models here.
class Brand(models.Model):
name = models.CharField(max_length=200, null=True)
def __str__(self):
return self.name
class AllProducts(models.Model):
name = models.CharField(max_length=200, null=True)
def __str__(self):
return self.name
class ShoppingList(models.Model):
product = models.ForeignKey(AllProducts, null=True, on_delete= models.SET_NULL, blank=True)
brand = models.ForeignKey(Brand, null=True, on_delete= models.SET_NULL, blank=True)
quantity = models.CharField(max_length=200, null=True, blank=True)
info = models.TextField(max_length=500, null=True, blank=True)
def __str__(self):
return self.product
The class brand is a class with all the brands of the products.
The class All_Products is a class with all the products that you can add to your groceries list.
And the class ShoppingList is a class with all the products in the groceries list.
Views.py
def home(request):
products = ShoppingList.objects.all()
context = {
'products':products,
}
return render(request, 'groceries_list/home.html', context )
def all_products(request):
all_products = AllProducts.objects.all()
context = {
'products':all_products,
}
return render(request, 'groceries_list/all_products.html', context)
The home function is the function that handels the groceries list page an the all_products function is the function that handels the page with all the product you can add to your list.
groceries list template
{% extends "groceries_list/base.html" %}
{% block content %}
<div class="card">
<div class="card-body">
<div class="card m-1 text-white">
Add Grocery
</div>
{% for product in products %}
<div class="item-row">
<div class="card m-1 text-white" style="background-color: #9BD6E0;">
<div class="card-body">
<a class="btn btn-sm btn-info" href="{% url 'update_gorcery' product.id %}">Update</a>
<a class="btn btn-sm btn-danger" href="{% url 'delete_gorcery' product.id %}">Delete</a>
<span class="text-dark"><strong>{{product.product}}</strong> {{product.quantity}} </span>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
all products template
% extends "groceries_list/base.html" %}
{% block content %}
<div class="card">
<div class="card-body">
<div class="card m-1 text-white">
Add Product
</div>
{% for product in products %}
<div class="item-row">
<div class="card m-1 text-white" style="background-color: #9BD6E0;">
<div class="card-body">
<button type="submit" class="btn btn-success btn-sm ">Add To List</button>
<span class="text-dark ml-3 text-center"><strong>{{product.name}}</strong>
<a class="btn btn-sm btn-danger float-right" href="{% url 'delete_product' product.id %}">Delete</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
The site may have many users using it at the same time, so you should arrange for a way to identify the particular user who adds the item to the shopping list. The easiest way to do this is to create an authentication system (You can use as-is or extend the User model) from django.auth.models
Looking at your views, as they are, both the home() and and all_products() view are rendering the same context, which definitely can't be what is in the cart (for the home view).
A way to handle this would be to make your ShoppingList model in a way that it includes a field for the customer. The customer would be the user making the request.
In the all_products.html page, you can create a form with a hidden field that you pre-populate with the product.id and the 'add to list' button as a submit button for this form.
When a user clicks 'add to list', the form gets posted to a url that invokes the responsible view. In that view, you create a 'ShoppingList object' (an instance of the ShoppingList model that you created in models) with values of the product.id that was posted by the form and the customer as request.user (the user making the request).
Just a few random tips:
In your ShoppingList model, you are defining quantity as a CharField but quantity is best defined as an IntegerField. Also, there is no need to use both blank=True and null=True arguments. I personally like to use blank=True only due to safety reasons that I won't talk about here.
I would also recommend that you revise the naming systems for your models and views.
In summary:
Add field customer to the ShoppingList model.
Make field product a CharField.
Make a form with a hidden field that posts back the product.id when a
user clicks 'add to list'.
Handle the post request by making a ShoppingList object.
Consider making quantity an IntegerField.

Associate multiple unique forms to a unique object in Django

I'm trying to build a web application using Django where a user can create a 'Project' and within that 'Project', there are four different forms.
My Question is, how do I associate those forms to a specific 'project' in the sense that the form is linked to that specific 'project'.
I'm close to doing it but I'm having issues with the 'create-project.html', 'create-request-1.html' and 'create-request-2.html' pages from rendering due to the 'PK' in the 'projects.html' page URL.
The exact errors I get for navigating to those pages is -
NoReverseMatch at /projects/create-project/
Reverse for 'initiate_project' with no arguments not found. 1 pattern(s) tried: ['projects\\/project\\/(?P<pk>[0-9]+)\\/$']
Or
NoReverseMatch at /project/create-post-1/
Reverse for 'initiate_project' with no arguments not found. 1 pattern(s) tried: ['projects\\/project\\/(?P<pk>[0-9]+)\\/$']
The path is as follows -
'projects.html' -
'create-project.html' -
'initiate-project.html' -
'create-request-1.html'
'create-request-2.html'
I can navigate and go into each unique 'project' (e.g. project 1, project 2) in 'projects.html' but I can't get into my forms ('create-request-1.html', 'create-request-2.html') inside the 'initiate-project.html' page that's within each 'project' in the 'projects.html' page.
Here's my code so far -
model.py -
from django.db import models
class create_new_project(models.Model):
list_display = ('project_name', 'project_manager',
'technical_lead', 'test_lead')
class Meta:
verbose_name = 'Create New Project'
verbose_name_plural = 'Create New Projects'
project_name = models.CharField(max_length=100)
project_manager = models.CharField(max_length=100)
technical_lead = models.CharField(max_length=100)
test_lead = models.CharField(max_length=100)
environment_choices = (
('1', '1'),
('2', '2'),
('3', '3')
)
environment = models.CharField(
max_length=50, choices=environment_choices, default='ICCS')
def __str__(self):
return self.project_name
views.py
from django.shortcuts import render
from django.shortcuts import render, get_object_or_404
from main.models import *
# Create your views here.
def index(request):
return render(request, 'main/index.html')
def projects(request):
CreateNewProject = create_new_project.objects.all()
args = {'CreateNewProject': CreateNewProject}
return render(request, 'main/projects.html', args)
def create_project(request):
return render(request, 'main/create-project.html')
def initiate_project(request, pk):
InitiateProjectURL = get_object_or_404(create_new_project, pk=pk)
return render(request, 'main/initiate-project.html', {'InitiateProjectURL': InitiateProjectURL})
def create_post_request_1(request):
return render(request, 'main/create-post-request-1.html')
def create_post_section_2(request):
return render(request, 'main/create-post-request-2.html')
urls.py
from django.urls import include, path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('projects/', views.projects, name='projects'),
path('projects/create-project/', views.create_project, name='create_project'),
path('projects/project/<int:pk>/',
views.initiate_project, name='initiate_project'),
path('project/create-post-request-1/',
views.create_post_request_1, name='create_post_paid_request_section_1'),
path('project/create-post-request-2/',
views.create_post_request_2, name='create_post_request_2'),
]
projects.html
<div class="row">
<a href="{% url 'create_project' %}" class="card card-1 bg-red shadow m-2">
<div class="card-body d-flex h-100">
<div class="text-center mx-auto justify-content-center align-self-center">
{% load staticfiles %}
<img src="{% static 'main/images/plus.svg' %}" alt="Plus Icon" height="60vh">
<div class="mb-3 pt-4 text-white">Create New Project</div>
</div>
</div>
</a>
{% for create_new_project in CreateNewProject %}
<div class="card card-1 bg-white shadow m-2">
<div class="card-body">
<h4 class="card-title text-red">{{ create_new_project.project_name }}</h4>
<div class="card-text pt-2 pb-2">
<div class="mb-3"><b>Project Manager: </b>{{ create_new_project.project_name }}</div>
<div class="mb-3"><b>Project Lead: </b>{{ create_new_project.project_manager }}</div>
<div class="mb-3"><b>Test Lead: </b>{{ create_new_project.test_lead }}</div>
<div class="mb-3"><b>Environment: </b>{{ create_new_project.environment }}</div>
</div>
<div class="float-right">
<a href="{% url 'initiate_project' pk=create_new_project.pk %}" class="text-red">
{% load staticfiles %}
<img src="{% static 'main/images/next.svg' %}" class="text-red" alt="Next Icon" height="25vh"></a>
</div>
</div>
</div>
{% endfor %}
</div>
create-post-request-1.html / create-post-request-2.html
<div class="col-md-2">
Cancel
</div>
To better illustrate what I'm trying to do, See the following image - Visual description
the href for the a tag in create-post-request-1.html / create-post-request-2.html should be sth like "{% url 'initiate_project' pk=any_pk_you_choose %}" instead of {% url 'initiate_project' %}

ImproperlyConfigured at /app/category/Python/

I wanna make a page which shows POST's models' contents is shown each category.For example, when I put Python link in in detail.html,only POST's models' contents with Python's category is shown in category.html.When I put Python link in category.html,I got an error,ImproperlyConfigured at /app/category/Python/ CategoryView is missing a QuerySet. Define CategoryView.model, CategoryView.queryset, or override CategoryView.get_queryset(). I wrote codes in views.py
def top(request):
content = POST.objects.order_by('-created_at')[:5]
category_content = Category.objects.order_by('-created_at')[:5]
page = _get_page(blog_content, request.GET.get('page'))
return render(request, 'top.html',{'content':content,'category_content':category_content,"page":page})
class CategoryView(BaseListView):
template_name = 'category.html'
def get_queryset(self):
category_name = self.kwargs['category']
self.category = Category.objects.get(name=category_name)
queryset = super().get_queryset().filter(category=self.category)
return queryset
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['category'] = self.category
return context
in urls.py
urlpatterns = [
path('top/', views.top, name='top'),
path('category/<str:category>/',views.CategoryView.as_view(), name='category'),
]
in models.py
class Category(models.Model):
name = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class POST(models.Model):
title = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.title
in top.html
<div class="list-group">
<a href="#">
Category
</a>
<div>
{% for category in category_content %}
<a href="{% url 'category' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</div>
</div>
in category.html
{% load static %}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Category</title>
</head>
<body>
<div>
{% for content in queryset %}
<h2>{{ content.title }}</h2>
<img src="content.image.url" />
<a class="btn btn-outline-primary btn-lg btn-block" href="{% url 'detail' content.pk %}">SHOW DETAIL</a>
{% endfor %}
</div>
<div>
<a href="#" class="list-group-item active">
Category
</a>
<div>
{% for category in category_content %}
<a class="list-group-item justify-content-between" href="{% url 'category' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</div>
</div>
</body>
</html>
I really cannot understand why ImproperlyConfigured which means settings.py is wrong.When I changed BaseListView into ListView,same error happens.I wrote get_queryset in CategoryView's class so I cannot understand codes needs QuerySet. How should I fix this?What is wrong in my codes?
One issue is that you're calling super().get_queryset(), which expects your class to define a queryset or a model. Adding model = Category will fix that error.
It looks like you're returning a queryset with just one entry, though. If you only want to get the one category, it would be simpler to use a DetailView:
from django.views.generic import DetailView
from .models import Category # or wherever it is
class CategoryView(DetailView):
model = Category
template_name = 'category.html'
def get_object(self):
category_name = self.kwargs['category']
return Category.objects.get(name=category_name)
Note that your template would need to be updated to reference category or object instead of looping through the queryset.

problems with an edit form/view

I work with Django 1.9. I created a view to add a new object and it works, but when to create a view to edit the objects, I have two different problems:
when I want to edit an album I click the link to the page where I want to see the entire list of albums for editing, but I get the error Exception Type: NameError and Exception Value: name 'album' is not defined that relates -> views.py in mod_artist form = AlbumForm(instance=album)
instead, when I want to edit an artist, I do the same thing as before to see the whole list of artists to be able to edit, but I get the error Exception Type:
UnboundLocalError and Exception Value: local variable 'artist' referenced before assignment that relates -> views.py in mod_artist form = ArtistForm(instance=artist)
my views.py file is
from django.shortcuts import render, redirect, get_object_or_404
from .forms import *
from .models import *
def mod_album(request):
if request.method == "POST":
form = AlbumForm(request.POST, instance=album)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('http://127.0.0.1:8000/')
else:
form = AlbumForm(instance=album)
return render(request,'polls/mod_album.html',{'form':form})
def mod_artist(request):
if request.method == 'POST':
form = ArtistForm(request.POST, instance=artist)
if form.is_valid():
artist = form.save(commit=False)
artist.author = request.user
artist.save()
return redirect('http://127.0.0.1:8000/')
else:
form = ArtistForm(instance=artist)
return render(request,'polls/mod_artist.html',{'form':form})
my index.html file is
...
<body>
<div class="container-fluid">
<div class="header">
<div>
<h1>personal library</h1>
</div>
<div id="wrap-nav">
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav">
<li class="active">+ add album</li>
<li class="active">- edit album</li>
<li class="active">+ add artist</li>
<li class="active">- edit artist</li>
<li class="active">view the list of albums</li>
</ul>
</div>
</div>
</nav>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-6">
{% for album in albums %}
<ul>
<li>Album name: {{album.name}}</li>
<li>Artist: {{album.artist}}</li>
<li>Genre: {{album.genre}}</li>
<li>Songs: , </li>
<li>Vote: {{album.vote}}</li>
</ul>
<hr />
{% endfor %}
</div>
<div class="col-xs-6 col-md-6">
{% for artist in artists %}
<ul>
<li>Artist name: {{artist.name}}</li>
</ul>
<hr />
{% endfor %}
</div>
</div>
</div>
</body>
...
my urls.py file is
from django.conf.urls import url
from . import views
urlpatterns=[
...
url(r'^new_album/$', views.new_album, name='new_album'),
url(r'^mod_album/$', views.mod_album, name='mod_album'),
url(r'^new_artist/$', views.new_artist, name='new_artist'),
url(r'^mod_artist/$', views.mod_artist, name='mod_artist'),
]
and my forms.py file is
from django import forms
from .models import *
class AlbumForm(forms.ModelForm):
class Meta:
model = Album
fields = ['name','artist','year','genre','vote']
class ArtistForm(forms.ModelForm):
class Meta:
model = Artist
fields = ['name']
thanks in advance of our time.

Categories