Unable to search from hacker news api django - django

I would like to search different items (jobs, stories, ask) from the hacker news api but I can't seem to figure out how to do it correctly, please check code below and tell me what I'm doing wrong as I'm unable to run it successfully.
def search(request):
if 'search' in request.GET:
search = request.GET['search']
url = 'https://hacker-news.firebaseio.com/v0/item/{item-id}.json?print=pretty'
response = requests.get(url)
article_list = response.json()
context = {}
context['objects'] = []
for each_id in article_list[:10]:
# Make a separate API call for each article.
url = f"https://hacker-news.firebaseio.com/v0/item/{each_id}.json"
# get response for individual articles
response = requests.get(url)
article_dict = response.json()
context['objects'].append(article_dict)
return render(request, 'SyncNews/search.html', context)
{% for x in objects %}
<h3 class="news-subheading">{{ x.title }}</h3>
{% endfor %}
<form method="GET" action="{% url 'search' %}">
<input type="text" name="item-id" placeholder="Search" />
</form>

Your code in line 4 is problematic.
url = 'https://hacker-news.firebaseio.com/v0/item/{item-id}.json?print=pretty'
{item-id} has not been defined.

Related

request.GET.get('q', None) always returns None

# my .views
def search_view(request):
query = request.GET.get('q', None)
print(query)
context = {"query": query}
return render(request, 'view.html', context)
# .urls
path('search/', search_view)
# my view.html
{% if query %}
<p>Your query {{ query }}</p>
{% else %}
<form method="GET" action='/search/'>
<input type="search" name='q' placeholder="Search" aria-label="Search">
<button type="submit">Search</button>
</form>
{% endif %}
I'm new to Django and I'm following the docs, I looked over and over and seems right to me but I keep getting query=None. When I input a value in the search bar goes to http://127.0.0.1:8000/search/?q=value,
but the query is always None. Please help a noob getting started.
welcome to SO! Have you tried this? From reading this, it seems like you are having some issues returning your value back from the form to your function.
I don't know what are looking at at the end, but that is not a good job. did you try an < a > tag too?
< a href="search/?q=sth">

Get list of all URL names in Django to make Active links in Navigation

I am putting together a template tag for active/currently visited links in a navigation bar so I can easily add the proper active CSS class to the link.
I am have created code that works fine for either a mix of passed in urls with the same url-parameters included, but it does not allow me to pass in urls that have different params.
{% make_active 'index' %} and {% make_active 'users' 1 %} could not be grouped together accurately as {% make_active 'index~users' 1 %} because I am using reverse() to see if the url exists.
What I want is to just check the names from each of the url pattern files in my project and if the name exists, then I return the appropriate active class...but I cannot figure out how to simply grab the names. Is this possible, or can someone help with the code?
#register.simple_tag(takes_context=True)
def make_active(context, view_names, *args, **kwargs):
print(args, kwargs)
if not kwargs.pop('class', None):
class_to_return = 'sidebar-item-active'
else:
class_to_return = kwargs.pop('class')
request = context.get('request')
if not request:
raise Exception('A request must be passed in for this to work')
names = view_names.split('~')
print(names)
current_url_active = False
for view in names:
print(view)
try:
# always include the client url
args_to_use = [request.client_url]
# append the passed args into the args for reversing the url name
args_to_use.extend(args)
reversed_path = reverse(view, args=args_to_use)
print(reversed_path)
current_url_active = True
except NoReverseMatch:
current_url_active = False
continue
if current_url_active:
break
return class_to_return if current_url_active else None
I figured out a way to gather the url names using dynamic imports, but after figuring out what I wanted to do I learned that I did not even need to go through the complexity of gathering all url names. Anyways here is that code:
def get_url_names():
from django.apps import apps
list_of_url_names = list()
list_of_all_urls = list()
for name, app in apps.app_configs.items():
mod_to_import = f'apps.{name}.urls'
try:
urls = getattr(importlib.import_module(mod_to_import), "urlpatterns")
list_of_all_urls.extend(urls)
except ImportError as ex:
# is an app without urls
pass
for url in list_of_all_urls:
list_of_url_names.append(url.name)
return list_of_url_names
While making that work I figured out all I needed to check was if I was on the current url name, which is easy to gather with request.path_info. So now my code can be changed like so
#register.simple_tag(takes_context=True)
def make_active(context, view_names, *args, **kwargs):
if not kwargs.get('class', None):
class_to_return = 'sidebar-item-active'
else:
class_to_return = kwargs.get('class')
request = context.get('request')
if not request:
raise Exception('A request must be passed in for this to work')
names = view_names.split('|')
current_url_name = resolve(request.path_info).url_name
for view in names:
if view == current_url_name:
return class_to_return
And now I can return the correct active link CSS class with my url tag like so:
<div class="collapse {% make_active 'users_index|users_actions|groups_index|groups_edit|users_create' class='show' %} " id="userCollapse">
<div class="card card-body">
{% if perms.users.view_customuser and perms.users.view_staff %}
<a class="dropdown-item {% make_active 'users_create|users_index|users_actions' %}"
href="{% url 'users_index' CLIENT.url_base %}"><i
class="fas fa-users"></i>
<span class="nav-item-text"> Users</span>
</a>
{% endif %}
{% if perms.auth %}
<div class="dropdown-divider"></div>
<a class=" dropdown-item {% make_active 'groups_index|groups_edit' %}"
href=" {% url 'groups_index' CLIENT.url_base %}">
<i class="far fa-user-plus"></i>
<span class="nav-item-text"> Group Permissions</span>
</a>
{% endif %}
</div>
</div>

Implementing confirmation view and template in django

I have two views one that accepts inputs and the other for confirmation and execution of an action. My problem is how to confirm the inputs from another view and template.
This is similar when you delete a record. That you should confirm from the user his actions.
Here is the input view. PreprocessinputationView:
def PreprocessInputationView(request, **kwargs):
proj_pk = kwargs.get('pk')
project = Project.objects.get(id=proj_pk)
df = pd.read_csv(project.base_file)
n_cols = df.keys
context = {}
context['df'] = df
context['n_cols'] = n_cols
context['project'] = project
if request.method == 'POST':
# try:
checked_value = request.POST.getlist(u'predictors')
method = ''.join(request.POST.getlist(u'method'))
if checked_value and method:
context['checked_value'] = checked_value
context['method'] = method
return render(request, 'projects/preprocess/confirm_inputation.html', context)
return render(request, 'projects/preprocess/preprocess_inputation.html', context)
The confirmation view goes here. ConfirmInputationView:
def ConfirmInputationView(request, context):
print('method:', context['method'])
project = context['project']
df = pd.read_csv(project.base_file)
n_cols = df.keys
filename = project.base_file.name
tmp = filename.split('/')
filename = str(tmp[1:])
if request.method == 'POST':
# try:
checked_value = context['checked_value']
method = context['method']
if checked_value and (method=='mean'):
df[checked_value].fillna(df[checked_value].mean())
# df.drop(columns=checked_values, inplace=True)
new_df = df.to_csv(index=False)
updated_file = ContentFile(new_df)
updated_file.name = filename
project.base_file = updated_file
project.save()
str_checked_value = ', '.join(checked_value)
context['str_checked_value'] = str_checked_value
if str_checked_value:
messages.success(request, f'Inputation to column(s) {str_checked_value} successful!')
return render(request, 'projects/preprocess/preprocess_inputation.html', context)
The confirmation template. Confirm_inputation.html:
{% extends "base.html" %}
{% block page_heading %}
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Delete Project</h1>
</div>
{% endblock page_heading %}
{% block content %}
<div class="jumbotron col-xl-8 col-md-6 mb-1"">
<form method=" POST">
{% csrf_token %}
<fieldset class='form-group'>
<p>
You have chosen <strong>{{ method }}</strong> as an inputation method?
Are you sure you want to proceed?
</p>
</fieldset>
<div class="form-group">
<button class="btn btn-danger float-sm-right mr-1" type="submit">Yes, Delete</button>
<a class="btn btn-secondary float-sm-right mr-1" href="{% url 'project-detail' project.id %}">Cancel</a>
</div>
</form>
</div>
{% endblock content %}
The data from the PreprocessImputationView should be passed to ConfirmImputationView for confirmation and processing.
I'm not sure I understand your question or rather problem. So I'll summarize what I understood. Please clarify more, if this doesn't fit your problem.
You have view A (PreprocessInputationView), which shows the user some values/forms and allows some POST action to A.
If view A receives a POST request you check the form input and render template B of view B.
Your rendered template B offers two options to the user: Accept, which triggers POST to view B or decline, which links to some details view.
I think what you're missing is, that context in render is "lost" after rendering. The moment the user sees the finished html page that variable is no longer relevant and inaccessible.
A way to provide the necessary 'method' information to your B view would be to add a form field to your B template, which holds some sort of key for B view to determine on POST what to do. Like a hidden input field with a number. Each number would predetermined stand for a method.

Passing value from previous page to django view

I have a job site where a user enters a zip code into a form and a list of jobs matching that zip code is displayed
search.html :
<h6>Results: {{ number_of_results }}</h6>
{% for job in jobs_matching_query %}
<h2><a class="job_link" href="#">{{ job.job_title}}</a></h2>
<p class="job_address lead">{{ job.establishment_name }} - {{ job.address }}</p>
{% endfor %}
<form action = "{% url 'email_subscription' %}">
<p>Subscribe to recieve job alerts near {{ query }}:</p> <!-- query stores zip code-->
<input type="text" name= "email" placeholder="Email">
<button class="btn" type="submit">Subscribe</button>
</form>
The search form is handled by the following view (not sure if I need to include this or not):
def job_query(request):
if request.method == "GET":
query = request.GET.get('query')
jobs_matching_query = Job.objects.filter(zip_code__iexact = query) | Job.objects.filter(city__iexact=query) | Job.objects.filter(state__iexact=query)
number_of_results = 0
for job in jobs_matching_query:
number_of_results = number_of_results + 1
return render(request, 'core/search.html', {'query': query ,'jobs_matching_query': jobs_matching_query, 'number_of_results': number_of_results})
On that search.html I have an email subscription box (subscribe to recieve alerts for that particular zip code), is there a way to pass the value of query from that page to an email_subscription view? I believe I've seen this done in the url so here is the url for said view
url(r'^email_subscription$', views.email_subscription, name="email_subscription"),
You can use django sesions for that you can save the query in django session
#put it somewhere
request.session['query'] = my_query
#then you can access it from the other view in views.email_subscription
it is going to work but im not sure if this is a practitacal one

Django pagination in filtered search post results

I have a view that filters out results for a posted search form:
def profile_advanced_search(request):
args = {}
if request.method == "POST":
form = AdvancedSearchForm(request.POST)
qs=[]
if form.is_valid():
cd = form.cleaned_data
s_country=cd['country']
s_province=cd['province']
s_city = cd['city']
if s_country: qs.append(Q(country__icontains = s_country))
if s_province: qs.append( Q(province__icontains=s_province))
if s_city: qs.append( Q(city__icontains=s_city))
f = None
for q in qs:
if f is None:
f=q
else: f &=q
list = UserProfile.objects.filter(f).order_by('-created_at')
else:
form = AdvancedSearchForm()
list = UserProfile.objects.all().order_by('-created_at')
paginator = Paginator(list,10)
page= request.GET.get('page')
try:
results = paginator.page(page)
except PageNotAnInteger:
results = paginator.page(1)
except EmptyPage:
results = paginator.page(paginator.num_pages)
args.update(csrf(request))
args['form'] = form
args['results'] = results
return render_to_response('userprofile/advanced_search.html', args,
context_instance=RequestContext(request))
the urls.py part is:
url(r'^search/$', 'userprofile.views.profile_advanced_search'),
The results in the first page are fine but the problem is that when I go to the second page, the filtered results is just forgotten.
It is obvious to me why this happnes: filtering in the views accounts only for POST while pagination uses GET hence the queryset filter does not apply after the first page.
I have looked at several suggestions and snippets for similar problem but none was close enough to my views, so I could not figure out how to fix it and appreciate your help.
Update: here is the relevant template:
<form action="/search/" method="post">{% csrf_token %}
<ul class="list-unstyled">
<li><h3>Country</h3></li>
<li>{{form.country}}</li><br>
<h4>Province</h4>
<li>{{form.province}}</li>
<h4>City</h4>
<li>{{form.city}}</li>
</ul>
<input type="submit" name="submit" value="search" />
</form>
Search Results:
{% for p in results %}
<div">
<div>
<br>
<strong><a href="/profile/{{p.username}}" >{{p.username}}</a></strong>
{{p.country}} <br>
{{p.province}} <br>
{{p.city}} <br>
</div>
</div>
{% endfor %}
<div>
<div class="pagination">
{% if results.has_previous %}
<< Prev &nbsp
{% endif %}
{% if results.has_next %}
Next >>
{% endif %}
</div>
</div>
</div>
You should move to Post/Redirect/Get pattern. You need 2 views, first one for form and second for results.
Create a new url able to parse all three parameters plus page. /(?P<country>\w+)/(?P<province>\w+)/(?P<site>\w+)/(?P<page>\d+). Route this url to a new view show_results. This view shows page results filtering for all three parameters.
In view form, after receive search parameters, compose url and make a redirect to it. return HttpResponseRedirect(reverse('your.views.show_results', args=(country, province, site, 0, )))
Obviously, instead to named url parameters you can work with GET key value parameters.
Take a look how google works in this way:
https://www.google.es/search?q=post+redirect+get