haystack on existing template - django

How to implement working SearchView in existing views.py?
I already have CBV, and added in urls.py as /moderate and want to apply search form in it. but always got "Results No results found."
This is my /moderate page with 3 forms, using SearchView and piece of code from tutorial in template.
And this from /search page, with urls(r'^search/$', include('haystack.urls'))
urls.py
urlpatterns= [
url(r'^search/', include('haystack.urls')),
url(r'^moderate/', Moderate.as_view(), name='moderate'),
]
views.py
class Moderate(SearchView):
#method_decorator(staff_member_required)
def dispatch(self, *args, **kwargs):
return super(Moderate, self).dispatch(*args, **kwargs)
#model = Ad
template_name = 'adapp/ad_moderate.html'
#template_name = 'search/search.html'
paginator_class = DiggPaginator
paginate_by = 10
ad_type = None
ad_sub_type = None
def get_queryset(self):
qs = super(Moderate, self).get_queryset().filter(ad_type__isnull=False,
ad_sub_type__isnull=False)
return qs
def get_context_data(self, **kwargs):
context = super(Moderate, self).get_context_data(**kwargs)
context['filter'] = ModerateFilter(self.request.GET)
return context
# define method to recieve fields from form, and change data accordings
def post(self, request, *args, **kwargs):
selected = request.POST['selected']
record = Ad.objects.get(pk=int(selected))
form = ModerateForm(request.POST, instance=record)
if form.is_valid():
form.save(commit=True)
return HttpResponseRedirect('')
template/ad_moderate.html
{% extends 'base.html' %}
{% load i18n url_tags %}
{% block content %}
<div id="casing">
<div id="content">
{# filter form, to show only models with moderated=True #}
<form action="" method="get">
{{ filter.form.as_p }}
<input type="submit">
</form>
<h2>Search</h2>
{# search form right from tutorial #}
<form method="get" action="">
<table>
{{ form.as_table }}
<tr>
<td> </td>
<td>
<input type="submit" value="Search">
</td>
</tr>
</table>
{% if query %}
<h3>Results</h3>
{% for result in page.object_list %}
<p>
{{ result.object.title }}
</p>
{% empty %}
<p>No results found.</p>
{% endfor %}
{% else %}
{# Show some example queries to run, maybe query syntax, something else? #}
{% endif %}
</form>
{% for object in filter %}
{# a lot of template tags and third form to change value of model #}
<form action="" method="POST">
{% csrf_token %}
<input type="radio" name="moderated" value="True">Accept
<br>
<input type="radio" name="moderated" value="False">Decline
<input type="hidden" value="{{ object.id }}"
name="selected">
<input class="btn" type="submit" value="moderate">
</form>
search_indexes.py
from .models import Ad
class AdIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
# my model, with one search should be
return Ad
templates/search/indexes/app/ad_text.txt
{{ object.title }}
{{ object.short_desc }}
{{ object.description }}
{{ object.experience }}
{{ object.skills }}
{{ object.name }}
{{ object.city }}

get_context_data():
context['search'] = SearchForm(self.request.GET).search()
Would solve a problem.
That means, I should create form and send return from .save() method, rather that django-like form instance.

Related

How to use model form instances of a formset in Django template

I'm trying to access the instance of the forms in a formset, but it is not working. I CAN access them using the variable notation, as in {{ form }}, but not in code, as in {% url 'section' form.instance.pk %}. I need to iterate through the forms in the formset along with the corresponding model instance.
My view:
# views.py
def sec(request, companyurl):
company = get_if_exists(Company, author=request.user)
SectionFormSet = modelformset_factory(Section, form=SectionForm, can_delete=True)
sections = Section.objects.filter(company=company).order_by('order')
formset = SectionFormSet(request.POST or None,
initial=[{'company': company}],
queryset=sections
context = {
'sections': sections,
'formset': formset,
}
return render(request, 'questions/sections.html', context)
My model:
# models.py
class Section(models.Model):
section = models.CharField(max_length=100)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
order = models.PositiveIntegerField(default=1000000)
show = models.BooleanField(default=True)
def __str__(self):
return self.section
My Form (I'm using django-crispy forms):
# forms.py
class SectionForm(forms.ModelForm):
class Meta:
model = Section
fields = ['company', 'section', 'show', 'order']
labels = {
'section': '',
'show': 'Show'
}
def __init__(self, *args, **kwargs):
super(SectionForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Div(
Div(HTML("##"), css_class = 'my-handle col-auto'),
Div('section', css_class='col-3'),
Div('show', css_class = 'col-auto'),
Div('DELETE', css_class = 'col-auto'),
Field('company', type='hidden'),
Field('order', type='hidden'),
css_class='row',
),
)
My template (this is where the problem is seen):
<form action="#" method="post">
{% csrf_token %}
{{ formset.management_form }}
<div id="simpleList" class="list-group">
{% for fo in formset %}
<div class="list-group-item hold">
{% crispy fo %}
<!-- TESTING TO SEE IF THIS WORKS, AND IT DOES! -->
{{ fo.instance }} + {{ fo.instance.pk }} + {{ fo.instance.section }}
<!-- THE PROBLEM OCCURS WHEN THIS IS ADDED -->
<a href="{% url 'section' fo.instance.pk fo.instance.section %}">
{{ fo.instance }}
</a>
<!-------------------------------------------->
<input type="hidden" name="order" value="{{ section.pk }}">
{% for hid in fo.hidden_fields %}
{{ hid }}
{% endfor %}
</div>
{% endfor %}
<button type="submit" class="btn btn-outline-primary">Save changes</button>
</form>
When I add the <a href="{% url 'section' fo.instance.pk fo.instance.section %}>link</a> line I get
Reverse for 'section' with arguments '(None, '')' not found. 1 pattern(s) tried: ['section/(?P<pk>[0-9]+)/(?P<section>[^/]+)\\Z']
The error is clear. fo.instance.pk is None and fo.instance.section is an empty string. Yet when I remove the anchor tag, the line above appears and shows the correct values for both of these. I think I know the difference in how the {{ }} and the {% %}, and I thought I knew how model form instances were tied to the model, but I am missing something.
Thanks for any help.
Formsets create blank forms
The answer was staring me in the face, when I printed the results. The last form, a blank, of course was giving me None and an empty string, since it had no data to fill it with. Thus the simple solution is to check for this before trying to form the url with the information. Therefore, this has nothing to do with the differences between {{ }} and {% %} nor form instances.
{% for fo in formset %}
<div class="list-group-item hold">
{% crispy fo %}
<!-- TESTING TO SEE IF THIS WORKS, AND IT DOES! -->
{{ fo.instance }} + {{ fo.instance.pk }} + {{ fo.instance.section }}
<!-- THE PROBLEM OCCURED WHEN THIS WAS ADDED -->
<!-- THE SIMPLE SOLUTION: --------------------->
{% if fo.instance.pk %}
<a href="{% url 'section' fo.instance.pk fo.instance.section %}">
{{ fo.instance }}
</a>
{% endif %}
<!-------------------------------------------->
<input type="hidden" name="order" value="{{ section.pk }}">
{% for hid in fo.hidden_fields %}
{{ hid }}
{% endfor %}
</div>
{% endfor %}
<button type="submit" class="btn btn-outline-primary">Save changes</button>
</form>

django search returns a blank page

I have designed my search box in my django app but the search only returns the extended html page, not the search results.
#View
class SearchListProject(ListView):
paginate_by = 4
template_name = 'website/search_project.html'
def get_queryset(self):
search_project = self.request.GET.get('q')
return Project.objects.filter(description__icontains=search_project)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['search_project'] = self.request.GET.get('q')
return context
#url
path('search_project/', SearchListProject.as_view(), name='search_project'),
path('search_project/page/<int:page>', SearchListProject.as_view(), name='search_project')
#html
{% extends 'website/project_list.html' %}
<h3 class="alert alert-primary text-center">Search: {{ search_project }}</h3>
{% block previous_page_url %}
{% url 'website:search_project' page_obj.previous_page_number %}?q={{ search_project }}
{% endblock %}
{% block next_page_url %}
{% url 'website:search_project' page_obj.next_page_number%}?q={{ search_project }}
{% endblock %}
#my form
<form action="{% url 'website:search_project' %}">
<input type="text" name="q">
<button type="submit"><i class="icofont-search"></i></button>
</form>

Using two submit buttons to save django inlineformset as draft

I have an inlineformset that works. I have attempted to add a second submit button, that when clicked will check that a particular field in each inline form has been filled out i.e. this field becomes a required filed only when the second submit button is clicked.
The problem is that when I click this second submit button the validation errors don't appear and the form just seems to submit anyway. I think the issue is within my view, within form_valid. I'm not sure what I need to return when if form.is_valid() fails.
I'm teaching myself to code, so any help or direction is much appreciated.
Forms.py
class response_form_draft(forms.ModelForm):
class Meta:
name = response
exclude = ['id_question','order']
def __init__(self, *args, **kwargs):
super(response_form_draft, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = False
class response_form_final(forms.ModelForm):
class Meta:
name = response
exclude = ['id_question','order']
def __init__(self, *args, **kwargs):
super(response_form_final, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = False
def clean(self):
data = self.cleaned_data
resp = data['response']
if resp == "":
raise forms.ValidationError(('must not be blank'))
checklist_formset_draft = inlineformset_factory(checklist, response,
form = response_form_draft,
extra=0, can_delete=False,
widgets={'comments': forms.Textarea(attrs={'cols': 7, 'rows': 3,
'style':'resize:none'})
})
checklist_formset_final = inlineformset_factory(checklist, response,
form = response_form_final,
extra=0, can_delete=False,
widgets={'comments': forms.Textarea(attrs={'cols': 7, 'rows': 3,
'style':'resize:none'}),
'response': forms.TextInput(attrs={'required':True})
})
Views.py
class ChecklistUpdateView(LoginRequiredMixin, UpdateView):
login_url = '/user/login'
model = checklist
form_class = checklist_form
success_url = reverse_lazy('checklist:checklist_list')
def get_context_data(self, **kwargs):
data = super(ChecklistUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
if 'complete' in self.request.POST:
print("complete")
data['question'] = checklist_formset_final(self.request.POST, instance=self.object)
else:
print("draft")
data['question'] = checklist_formset_draft(self.request.POST, instance=self.object)
else:
data['question'] = checklist_formset_draft(instance=self.object)
return data
def form_valid(self, form):
context = self.get_context_data()
question = context['question']
with transaction.atomic():
self.object = form.save(commit=False)
self.object.last_edit_by = str(self.request.user)
self.object.last_edit_date = timezone.now()
if question.is_valid():
question.instance = self.object
question.save()
return super(ChecklistUpdateView, self).form_valid(form)
HTML template
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method="post">
{% csrf_token %}
<h1>{{ object.id_template.title }}</h1>
{{ form.errors }}
{{ form.entity|as_crispy_field }}
{{ form.date_created.as_hidden }}
{{ form.created_by.as_hidden }}
{{ form.id_template.as_hidden }}
{{ form.status.as_hidden }}
<div class="card">
<table id="table_id" class="table">
<tbody>
{{ question.management_form }}
{% for form in question.forms %}
{{ formset.errors }}
{{ form.non_field_errors }}
{% if forloop.first %}
<thead class="thead-dark">
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
<th></th>
</tr>
</thead>
{% endif %}
<tr class="{% cycle row1 row2 %} formset_row">
{% 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.errors.as_ul }}
{% if field.label == "Question" %}
<p>{{ form.question.value }}</p>
{{ field.as_hidden }}
{% elif field.label == "Response" %}
{{ field.as_hidden }}
<button id="Yes.{{ forloop.parentloop.counter0 }}" row="{{ forloop.parentloop.counter0 }}" class="ans {{ forloop.parentloop.counter0 }} btn btn-dark right">Yes</button>
<button id="No.{{ forloop.parentloop.counter0 }}" row="{{ forloop.parentloop.counter0 }}" class="ans {{ forloop.parentloop.counter0 }} btn btn-dark right">No</button>
<button id="N/a.{{ forloop.parentloop.counter0 }}" row="{{ forloop.parentloop.counter0 }}" class="ans {{ forloop.parentloop.counter0 }} btn btn-dark right">N/a</button>
{% else %}
{{ field|as_crispy_field }}
{% endif %}
</td>
{% endfor %}
<td> </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{{ form.comments|as_crispy_field }}
{{ form.audit|as_crispy_field }}
<div class="form-submit-row" id="submit-row">
<input class="btn btn-dark right" name="draft "type="submit" value="Save draft">
<input class="btn btn-dark right" name="complete" type="submit" value="Complete">
Delete
</div>
</form>
<br><br>
</div>
form_valid is activated when your form is valid. In your classview django automatically check validation for your checklist_form without validating formset. you can add custom validation in your form_valid and form_invalid, but i would go another way. i would put formset inside your base form (checklist_form)
something like that:
class checklist_form(forms.ModelForm):
def __init__(*args, **kwargs):
self.question= kwargs.pop("question")
super()__.init__(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
if not self.question.is_valid():
self.add_error(None, "Have some formset errors")
return cleaned_data
now form will be valid only if formset is also valid. And then in your ChecklistUpdateView i would put creation of formset into get_form_kwargs
def get_form_kwargs(self):
data = super().get_form_kwargs()
if self.request.POST:
if 'complete' in self.request.POST:
data['question'] = checklist_formset_final(self.request.POST, instance=self.object)
else:
data['question'] = checklist_formset_draft(self.request.POST, instance=self.object)
else:
data['question'] = checklist_formset_draft(instance=self.object)
return data
After that in your html template you will need to use form.question instead of question

How to update a post using form wizard in Django

I have created a multipage form using the form wizard from the form tools package and it works. I am trying to create and update view so that I can be able to update the created post.
I have tried using generic update view, this only display the page without the form. Then, I tried using the same code for the create view by using a get method but this seems not to work also.
I will appreciate it if someone can point me in the right direction. Thanks.
views.url
class FormWizardView(SessionWizardView):
template_name = "new_rental.html"
form_list = [NewRentalPropertyForm, NewContractForm]
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'images'))
def done(self, form_list, **kwargs):
rentalproperty_instance = RentalProperty()
contract_instance = Contract()
for form in form_list:
rentalproperty_instance = construct_instance(form, rentalproperty_instance)
contract_instance = construct_instance(form, contract_instance)
rentalproperty_instance.save()
contract_instance.rentalproperty = rentalproperty_instance
contract_instance.save()
return redirect('mini_rental:property_list')
class UpdateWizardView(SessionWizardView):
template_name = "update.html"
form_list = [NewRentalPropertyForm, NewContractForm]
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'images'))
def done(self,pk, form_list, **kwargs):
rentalproperty_instance = RentalProperty.objects.get(pk=pk)
contract_instance = Contract()
for form in form_list:
rentalproperty_instance = construct_instance(form, rentalproperty_instance)
contract_instance = construct_instance(form, contract_instance)
rentalproperty_instance.save()
contract_instance.rentalproperty = rentalproperty_instance
contract_instance.save()
return redirect('mini_rental:property_list')
form. html
Update rental listing
{% load i18n %}
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form method="post">
{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{% trans "first step" %}</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% trans "prev step" %}</button>
{% endif %}
<input type="submit" value="{% trans 'submit' %}"/>
</form>
{% endblock body %}
urls.py
path('property_update/<int:pk>', UpdateWizardView.as_view(forms), name='property_update'),
path('new_rental/', FormWizardView.as_view(forms), name='new_rental'),

Django: how to get username in template using django-tweaks

I have rendered a html form with widget_tweaks and i want the default value of the field to be the current username.
#template
{% if user.is_authenticated %}
<form method="post">
{% render_field form.title value="{{ request.user }}" readonly="True" %}
</form>
{% else %}
<h 1>Login First</h 1>
{% endif %}
but this render the exact text "{{ request.user }}" rather than printing the username. If I use the tag and use the {{ request.user }}, prints the current username.
This is how my views.py looks:
views.py
class CreatePostView(CreateView):
template_name = 'posts/new_post.html'
redirect_field_name = 'posts/post_detail.html'
form_class = PostForm
model = Post
def get_absolute_url(self):
return reverse('posts:post_detail', args = [self.id])
In django templatetags, you do not need double accolades {{ }}. Try the following:
<!-- Your template -->
[...]
{% render_field form.title value=request.user readonly="True" %}
[...]
I just removed the double accolades.