ListView queryset Can not pass context data to Template - django

I am having trouble with my Search API. Results of the queryset could not get through my template even though the query set fetched data from the model.
If the search is empty the queryset should return all the Models associated to the current project, otherwise, it should return models that qualify the criteria in the query.
I have tested the result of the query and it returns records from the model but could not display the instances into the template.
My SEARCH ListView:
class ModelSearchListView(ListView):
model = Model
template_name = 'predictions/model_listview.html'
context_object_name = 'models'
paginate_by = 2
def get_queryset(self):
query = self.request.GET.get('q')
proj_pk = self.kwargs.get('pk')
proj = get_object_or_404(Project, id=proj_pk)
if query:
result = Model.objects.filter(Q(project=proj.id) & (Q(name__contains=query) |
Q(algorithm_type__contains=query) |
Q(predictors__contains=query) |
Q(target_column__contains=query))).order_by('-date_created')
# print('result: ', result)
else:
result = Model.objects.filter(project=proj.id).order_by('-date_created')
print('result: ', result)
return result
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
project = Project.objects.filter(id=self.kwargs.get('pk')).first()
context['current_project'] = project.id
MY SEARCH FORM:
<form class="form my-2 my-lg-0" method="GET"
action="{% if current_project %}
{% url 'model-search-listview' current_project %}
{% else %}
{% url 'model-search-listview' object.project.id %}
{% endif %}">
<div class="input-group">
<input class="form-control " type="text" name="q" value="{{ request.GET.q }}" aria-label="Search"
placeholder="Search">
<span class="input-group-btn">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit" value="Search">
Search
</button>
</span>
</div>
</form>
The TEMPLATE:
{% if not models %} #Always TRUE because models is empty
<h5>No prediction models created for this project!</h5>
{% else %}
#Loop never executed
{% for model in models %} # models HERE ALWAYS returns empty
[SOME CODE HERE]
{% endfor %}
{% endif %}

You need to return the new context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
project = Project.objects.filter(id=self.kwargs.get('pk')).first()
context['current_project'] = project.id
return context

Related

How to search for exact values in Django?

I have created a search function. However, it searches for all elements that contain the entered value. For example, there are the following elements: 44564, 76436, 445. When I enter "445", it shows "44564" and "445", but I need only 445. Or if I enter "64", then nothing should be shown, but "44564" and "76436" are shown. How to fix it?
case_list.html
<div>
<h3>Search</h3>
<form method="GET" action="{% url 'case_search' %}">
<input type="search" type="text" name="q" prequired placeholder="Put value">
<button type="submit">Find</button>
</form>
</div>
<div>
{% for case in object_list %}
<div>
<p>{{ case.name }}</p>
</div>
{% endfor %}
</div>
Views.py
class CaseView(ListView):
model = Case
template_name = 'case_list.html'
class CaseSearch(ListView):
template_name = 'case_list.html'
def get_queryset(self):
return Case.objects.filter(name__icontains=self.request.GET.get("q"))
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context["q"] = self.request.GET.get("q")
return context
Urls.py
path('case_list/', CaseView.as_view(), name='case_list'),
path('case_list/search/', CaseSearch.as_view(), name="case_search"),
Use iexact for exact matches. Check out the docs https://docs.djangoproject.com/en/4.1/ref/models/querysets/#iexact
Case.objects.filter(name__iexact=self.request.GET.get("q"))

Show django form in a designed page

How are you?
I m totally new in Django.I designed a page and I wanted to show a django form(edit or create) in a well designed HTML page. but i do not know how.
This is my owner method:
class OwnerUpdateView(LoginRequiredMixin, UpdateView):
"""
queryset to the requesting user.
"""
def get_queryset(self):
print('update get_queryset called')
""" Limit a User to only modifying their own data. """
qs = super(OwnerUpdateView, self).get_queryset()
return qs.filter(user=self.request.user)
class OwnerCreateView(LoginRequiredMixin, CreateView):
"""
Sub-class of the CreateView to automatically pass the Request to the Form
and add the owner to the saved object.
"""
# Saves the form instance, sets the current object for the view, and redirects to get_success_url().
def form_valid(self, form):
print('form_valid called')
object = form.save(commit=False)
object.user = self.request.user
object.save()
return super(OwnerCreateView, self).form_valid(form)
This is my views.py
class TaskUpdateView(OwnerUpdateView):
model = Task
fields = ["title", "text", "endDate"]
class TaskCreateView(OwnerCreateView):
model = Task
fields = ["title","text","status","endDate"]
This is my urls.py:
app_name='task'
urlpatterns = [
path('', views.TaskListView.as_view(), name='all'),
path('task/<int:pk>/', views.TaskDetailView.as_view(), name='detail'),
path('task/create', views.TaskCreateView.as_view(success_url=reverse_lazy('task:all')), name='task_create'),
path('task/update/<int:pk>', views.TaskUpdateView.as_view(success_url=reverse_lazy('task:all')),
name='task_update'),
path('task/delete/<int:pk>', views.TaskDeleteView.as_view(success_url=reverse_lazy('task:all')),
name='task_delete'),
path("accounts/login/", views.login, name='login'),
path("accounts/logout/", views.logout, name='logout'),
]
And this is the models.py:
class Task(models.Model):
title=models.CharField(max_length=250)
text=models.TextField()
user=models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=False)
status=models.ForeignKey('Status',on_delete=models.SET_NULL,null=True)
startDate=models.DateTimeField(auto_now_add=True)
endDate=models.DateField(null=True)
def __str__(self):
return self.title
class Status(models.Model):
name=models.CharField(max_length=250)
def __str__(self):
return self.name
And this is where these both function work:
{%extends 'base.html'%}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<table>{{ form.as_table }}</table>
<input type="submit" value="Submit">
{# <input type="submit" onclick="window.location='{% url 'project:all' %}' ; return false;" value="Cancel">#}
</form>
{% endblock %}
How can i separate each element of this form and put it in a better designed page?
Thanks
There are two ways:
Option 1:
Loop over the form fields and render them individually:
{% for field in form %}
<div class="form-group">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<span class="form-text">{{ field.help_text|safe }}</span>
{% endif %}
</div>
{% endfor %}
See docs for more.
Option 2:
You can manually create form inputs and give them the correct field name attribute. This gives you more control but also requires more work:
<div class="form-group"
<input
type="text"
name="title"
value="{{ form.title.value }}"
class="form-control {% if form.title.errors %}is-invalid{% endif %}"
>
{% if form.title.help_text%}
<span class="form-text">{{ form.title.help_text|safe }}</span>
{% endif %}
<div class="invalid-feedback">{{ form.title.errors }}</div>
</div>
<!-- now do the same for other fields -->

How to filter a model Django and compare against an id

I'm trying to filter a model called "CartItem" , that has a field called item , I want to check whether a product id is in this field. each cartitem instance is connected to a cart.id. The product id is taken from a form in templates.
I currently have " {% if cartitem.objects.filter(item_id=product_id).exists %} " in the template, I also tried making an items list in the views.py , to try to use the "if in" statement in the template, based off the "items" contents, the only problem was that when the browser is refreshed the for loop to create this list is not called , it is only called when the user clicks on the "add to cart" button. Hope that makes sense.
at the moment I get an error:
TemplateSyntaxError
Exception Value:
Could not parse the remainder: '(item.id=product.id).exists' from 'cartitem.objects.filter(item.id=product.id).exists'
Thanks
models.py
class CartItem(models.Model):
item = models.ForeignKey(Product, blank=True, on_delete=models.CASCADE, null=True)
items_cart = models.ForeignKey('Cart', blank=True, on_delete=models.CASCADE, null=True)
quantity = models.IntegerField(null=True, blank=True)
updated = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return 'item:{} cart:{} quantity:{}'.format(self.item, self.items_cart, self.quantity)
views.py
def cart_update(request):
product_id = request.POST.get('product_id')
cart_obj, new_obj = Cart.objects.new_or_get(request)
cart_items = CartItem.objects.filter(items_cart_id=cart_obj)
items = []
for item in cart_items:
items.append(item.item.id)
print(items)
if product_id is not None:
try:
product_obj = Product.objects.get(id=product_id)
except Product.DoesNotExist:
print("Show message to user, product is gone?")
return redirect("cart:home")
cart_obj, new_obj = Cart.objects.new_or_get(request)
cart_items = CartItem.objects.filter(items_cart_id=cart_obj)
if product_obj in cart_items:
cart_items.delete(product_obj)
added = False
else:
newitem = CartItem(item=product_obj, items_cart=cart_obj, quantity=1)
newitem.save()
added = True
request.session['cart_items'] = cart_items.count()
# return redirect(product_obj.get_absolute_url())
if request.is_ajax(): # Asynchronous JavaScript And XML / JSON
print("Ajax request")
json_data = {
"added": added,
"removed": not added,
"cartItemCount": cart_items.count()
}
return JsonResponse(json_data, status=200) # HttpResponse
# return JsonResponse({"message": "Error 400"}, status=400) # Django Rest Framework
return redirect("cart:home")
cart.html
<form class='form-product-ajax' method='POST' action='{% url "cart:update" %}' data-endpoint='{% url "cart:update" %}' class="form" {% if request.user.is_authenticated %}data-user='abc'{% endif %}> {% csrf_token %}
<input type='hidden' name='product_id' value='{{ product.id }}' {% if product.is_digital %}data-is-digital='true'{% endif %}/>
<span class='submit-span'>
{% if cartitem.objects.filter(item_id=product_id).exists %}
<div class='btn-group'> <a class='btn btn-link' href='/cart/'>In cart</a> <button type='submit' class='btn btn-link'>Remove?</button></div>
{% else %}
<button type='submit' class='btn btn-success'>Add to cart</button>
{% endif %}
</span>
</form>
you can not use this syntax in template django:
cartitem.objects.filter(item.id=product.id).exists
you can use templatetags and call it in your template:
register = template.Library()
#register.filter
def existing_product(product):
return CartItem.objects.filter(item.id=product.id).exists()
then load your templatetag in your template
{% load file_name_containing_your_method %}
<form class='form-product-ajax' method='POST' action='{% url "cart:update" %}' data-endpoint='{% url "cart:update" %}' class="form" {% if request.user.is_authenticated %}data-user='abc'{% endif %}> {% csrf_token %}
<input type='hidden' name='product_id' value='{{ product.id }}' {% if product.is_digital %}data-is-digital='true'{% endif %}/>
<span class='submit-span'>
{% if product|existing_product %}
<div class='btn-group'> <a class='btn btn-link' href='/cart/'>In cart</a> <button type='submit' class='btn btn-link'>Remove?</button></div>
{% else %}
<button type='submit' class='btn btn-success'>Add to cart</button>
{% endif %}
</span>
</form>
hope this can help your second question
NB: you should reboot your server after adding a templatetag

How to save class results in django cache and use it in other class

Html search file
<div class="content-section">
<h1 class="mb-3">{{ user.username }}</h1>
<form method="GET" action="{% url 'doctor:search' %}">
<input name ="q" value="{{request.GET.q}}" placeholder="search..">
<button class="btn btn-success" type="submit">
Search
</button>
</form>
</div>
VIEWS.py i like to save 'query' value in cache and use it later in different views.py class
class SearchResultsView(ListView):
model = User
template_name = 'all_users/doctor/search.html'
def get_queryset(self): # new
*query = self.request.GET.get('q')*
object_list = User.objects.filter(Q(username__icontains=query))
return object_list
You can use render_to_response for returning multiple objects like that:
def get_queryset(self): # new
query = self.request.GET.get('q')
object_list = User.objects.filter(Q(username__icontains=query))
another_object_list = Doctor.objects.filter(Q(username__icontains=query))
context = {
'object_list': object_list,
'another_object_list': another_object_list
}
return render_to_response('search.html', context)
And you can call this context in your template:
{{ object_list }}
{{ another_object_list }}

Retrieving models from form with ModelMultipleChoiceField

I am having difficulties with forms, specifically ModelMultipleChoiceField.
I've pieced together this code from various examples, but it sadly doesn't work.
I would like to be able to:
Search for some Works on work_search.html
Display the results of the search, with checkboxes next to each result
Select the Works I want, via the checkboxes
After pressing Add, display which works were selected.
I believe everything is okay except the last part. The page simply displays "works" :(
Here is the code - sorry about the length.
Models.py
class Work(models.Model):
title = models.CharField(max_length=200)
artist = models.CharField(max_length=200)
writers = models.CharField(max_length=200)
def __unicode__(self):
return self.title + ' - ' + self.artist
forms.py
class WorkSelectForm(forms.Form):
def __init__(self, queryset, *args, **kwargs):
super(WorkSelectForm, self).__init__(*args, **kwargs)
self.fields['works'] = forms.ModelMultipleChoiceField(queryset=queryset, widget=forms.CheckboxSelectMultiple())
views.py
def work_search(request):
query = request.GET.get('q', '')
if query:
qset = (
Q(title__icontains=query) |
Q(artist__icontains=query) |
Q(writers__icontains=query)
)
results = Work.objects.filter(qset).distinct()
form = WorkSelectForm(results)
return render_to_response("work_search.html", {"form": form, "query": query })
else:
results = []
return render_to_response("work_search.html", {"query": query })
def add_works(request):
#if request.method == POST:
form = WorkSelectForm(request.POST)
#if form.isvalid():
items = form.fields['works'].queryset
return render_to_response("add_works.html", {"items":items})
work_search.html
{% extends "base.html" %}
{% block content %}
<h1>Search</h1>
<form action="." method="GET">
<label for="q">Search: </label>
<input type="text" name="q" value="{{ query|escape }}">
<input type="submit" value="Search">
</form>
{% if query %}
<h2>Results for "{{ query|escape }}":</h2>
<form action="add_works" method="post">
<ul>
{% if form %}
{{ form.as_ul }}
{% endif %}
</ul>
<input type="submit" value="Add">
</form>
{% endif %}
{% endblock %}
add_works.html
{% extends "base.html" %}
{% block content %}
{% if items %}
{% for item in items %}
{{ item }}
{% endfor %}
{% else %}
<p>Nothing selected</p>
{% endif %}
{% endblock %}
In add_works, you're not constructing your WorkSelectForm the right way. It's expecting as a first parameter the queryset of possible/authorized choices, then the POST data.
Also, you're not accessing the selected works correctly from the form. You have to use is_valid method on the form, then use cleaned_data as described in the doc.
From what I see in your work_search view, there's no restriction on which Work objects you can search then add to the result, so you could do simply:
def add_works(request):
#if request.method == POST:
form = WorkSelectForm(Work.objects.all(), request.POST)
if form.is_valid():
# the items are in form.cleaned_data['works']
items = form.cleaned_data['works']
return render_to_response("add_works.html", {"items":items})
else:
# handle error case here
...