How to search for exact values in Django? - 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"))

Related

Can't manage to display form from my view

I'm trying to create a page where admins can upload some files using some FileField. The problem is that I can't manage to display any field from my form, I must be missing something important but I can't find out what, hope anyone can help me.
Here is the code related to this form:
urls.py
urlpatterns = patterns(
'',
url(r'^admin_fichiers_phyto/$', phyto_views.AdminFichiersPhyto.as_view(), name='phyto-admin-fichiers-phyto'),
)
phyto_admin_fichiers.html
{% block forms %}
{% if user.is_staff%}
<fieldset>
<div>
<span>{{ form.other }}</span>
</div>
</fieldset>
<p>
<input id="submit" class="btn btn-primary" type="submit" value="Synchronisation Autre" name="autre"/>
<input id="submit" class="btn btn-primary" type="submit" value="Synchronisation Traitements généraux" name="trtm_gen"/>
</p>
{% endif %}
{% endblock %}
views.py
class AdminFichiersPhyto(TemplateView):
template_name = 'phyto/phyto_admin_fichiers.html'
form_class = forms.PhytoFileForm
current_url = 'phyto-files'
context_object_name = 'phyto_files'
def post(self, request, *args, **kwargs):
if request.POST.get('autre'):
return HttpResponse('<h1>autre</h1>')
if request.POST.get('trtm_gen'):
return HttpResponse('<h1>Traitement Generaux</h1>')
forms.py
class PhytoFileForm(forms.Form):
class Meta:
model = models.PhytoFile
fields = ['general_treatment', 'other']
def __init__(self, *args, **kwargs):
super(PhytoFileForm, self).__init__(*args, **kwargs)
models.py
class PhytoFile(models.Model):
general_treatment = models.FileField(upload_to='fichiers_phyto/', blank=True, null=True)
other = models.FileField(upload_to='fichiers_phyto/', blank=True, null=True)
Here is what my webpage is showing :
https://imgur.com/a/yH0be0K
I can't understand why the Field isn't displayed, I really hope somebody have the knowledge to help me with my problem ! ^_^
Have a nice day ! :D
You have several major issues here.
TemplateView doesn't know anything about form_class (or context_object_name, for that matter). And by defining post like that you've avoided actually doing anything with the uploaded data. You need to use a view that deals with forms; in your case, a CreateView would be ideal. Inside that view, that response code needs to go in form_valid.
Secondly, PhytoFileForm needs to inherit from forms.ModelForm; a standard Form class doesn't know anything about models and doesn't use a Meta class. However, since you're not customizing the form beyond the field selection, you can just rely on the automatic form created by CreateView.
Next, your template is broken; it doesn't have an HTML form element. Also, since your form class has two fields but you only display one on the template, the form will never be valid.
So, putting it together:
class AdminFichiersPhyto(CreateView):
template_name = 'phyto/phyto_admin_fichiers.html'
model = models.PhytoFile
def form_valid(self, form):
form.save()
if request.POST.get('autre'):
return HttpResponse('<h1>autre</h1>')
if request.POST.get('trtm_gen'):
return HttpResponse('<h1>Traitement Generaux</h1>')
{% block forms %}
{% if user.is_staff%}
<form method="post" action="" enctype="multipart/form-data">
<fieldset>
<div>
<span>{{ form.other }}</span>
</div>
<div>
<span>{{ form. general_treatment }}</span>
</div>
</fieldset>
<p>
<input id="submit" class="btn btn-primary" type="submit" value="Synchronisation Autre" name="autre"/>
<input id="submit" class="btn btn-primary" type="submit" value="Synchronisation Traitements généraux" name="trtm_gen"/>
</p>
</form>
{% endif %}
{% endblock %}

ListView queryset Can not pass context data to Template

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

Using modelformset_factory in a Django Class Based View

I am building a view that will let me update multiple fields on multiple objects at the same time. I'm doing this using ModelFormSet & modelformset_factory.
The template will be a table of forms with the object name to the left of the fields (see image below).
I found this example, but I am stuck on how to implement the class based view & template.
My Formset
class BaseFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(BaseFormSet, self).__init__(*args, **kwargs)
self.queryset = Reference.objects.filter(
start__isnull=True)
ReferenceFormSet = modelformset_factory(
Reference,
fields=('start', 'end'),
formset=BaseFormSet,
extra=0)
My View
class ReferenceFormSetView(LoginRequiredMixin, SuperuserRequiredMixin, FormView):
model = Reference
form_class = ReferenceFormSet
template_name = "references/references_form.html"
def form_valid(self, form):
for sub_form in form:
if sub_form.has_changed():
sub_form.save()
return super(ReferenceFormSetView, self).form_valid(form)
My Template
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<h1>{{ headline }}</h1>
<div class="row">
<form action="" method="post">
{% crispy form %}
<div class="">
<input type="submit" value="Submit" />
</div>
</form>
</div>
</div>
{% endblock content %}
Questions
The view seems odd with the Formset in the form_class. Is there a better way to handle this?
How can I access the instance name to display in the form?
I found a solution using a package called django-extra-views.
There is a class called ModelFormSetView which does exactly what I wanted. Here is my implementation (simplified) for others to use -
My View
class ReferenceFormSetView(ModelFormSetView):
model = Reference
template_name = "references/references_form.html"
fields = ['start', 'end']
extra = 0
def get_queryset(self):
return self.model.objects.all()
def get_success_url(self):
return reverse('references:formset')
def formset_valid(self, formset):
"""
If the formset is valid redirect to the supplied URL
"""
messages.success(self.request, "Updated")
return HttpResponseRedirect(self.get_success_url())
def formset_invalid(self, formset):
"""
If the formset is invalid, re-render the context data with the
data-filled formset and errors.
"""
messages.error(self.request, "Error dummy")
return self.render_to_response(self.get_context_data(formset=formset))
My Template
<form class="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<div class="">
{% for field in form %}
{{ field }}
{% endfor %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Save</button>
</form>

get_query_set(self) is not being called in django

I am implementing search function using TemplateView in Django the class is
class part_search_view(TemplateView):
model = part_list
context_object_name = 'part_list'
template_name = 'part_list.html'
def get_context_data(self, **kwargs):
context = super(part_search_view, self).get_context_data(**kwargs)
context['my_list'] = populate_nav_bar()
return context
def get_queryset(self):
key = self.request.GET['search_text']
partlist = part_list.objects.filter(Q(part_id__icontains=key) | Q(part_name__icontains=key))
return partlist
part_list.html
{% for part in part_list %}
{{ part.part_id }} - {{ part.part_name }}
<a href="{% url 'parts:part_update_view' part.id %}" > Edit </a>
{% endfor %}
the url mapping is
url(r'^search/',views.part_search_view.as_view(),name='part_search_view'),
the form for serch button
<form action="{% url 'parts:part_search_view'%}" role="form" class="navbar-form navbar-left" method="get" >
{% csrf_token %}
<div class="form-group ">
<input class="form-control mr-sm-2" type="text" placeholder="Search" name="search_text">
<button class="form-control search_buton btn btn-success " type="submit" >Search</button>
</div>
</form>
after the search button is pressed the address is
http://127.0.0.1:8000/parts/search/?csrfmiddlewaretoken=PWjEw1hRsyH9B6YcseVuhS0urX8L7f170q9ucLF9hTPQPThulpgMSP4y5xhScCVr&search_text=mp6
but the get_query_set(self) is not called here the get_context_data(...) is called though, why?
TemplateViews don't know anything about querysets, so they never call a get_queryset method. You should subclass a more appropriate view, perhaps ListView.
If you look at docs, TemplateView does not have a method get_queryset(). Then, you would have to manually call it in the view.

Refering to related object in ListView

I am trying to create some kind of 'panel control'. I need to manage my objects directly from ListView. I'll show you transparent (I believe) schema what I'm trying to solve.
Models
class Category(models.Model):
cat_name = models.CharField(max_length=120)
class Product(models.Model):
category = models.ForeignKey(Category)
prod_name = models.CharField(max_length=120)
Views
class CategoryListView(ListView):
model = Category
def get_context_data(self, *args, **kwargs):
context = super(CategoryListView, self).get_context_data(*args, **kwargs)
# two class based forms created for models
context['form_category'] = CategoryForm()
context['form_product'] = ProductForm()
return context
def post(self, request, *args, **kwargs):
form_category = CategoryForm(request.POST or None)
if form_category.is_valid():
new_category = form_category.save(commit=False)
new_category.save()
return redirect('category_list')
return Http404
templates
I use two modal window to open my forms which I added to my context views
<button>Modal btton opens product form</button>
{% for category in object_list %}
<div class="panel">
<div class="panel-heading">
<h3>{{ category.cat_name}}</h3>
<button>Modal btn opens product form</button>
</div>
<div class="panel-body">
<ul>
{% for product in category.product_set.all %}
<li>
<p>{{ product.prod_name }}<p>
<span>Delete</span>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
<form action="." method="POST">{%csrf_token%}
{{ form_category.as_p }}
<input type="submit" value="add category">
</form>
<form action="." method="POST">{%csrf_token%}
{{ form_product.as_p }}
<input type="submit" value="add product">
</form>
For first form (CategoryForm) it's working fine because whole page refers to that (model = Category). Now is the question - how to create second object on that page. I can do this in DetailView using urls and parameter like slug, id or pk and after passing it as value but I want to do this from that page without moving to detail page.
Secondly I was thinking how to delete products from list but there is the same problem how to let django know that witch object I would like to remove.
Thanks.