Populate list in Django using view - python

I have the following function to read news headlines into a Python list:
import requests
def us_news_articles():
url = 'https://newsapi.org/v2/top-headlines?country=us&apiKey=###
source = requests.get(url)
data = source.json()
us_news_articles_list = []
for article in data['articles']:
us_news_articles_list.append(article)
return us_news_articles_list
This function works, and I've verified it.
Now I want to be able to use this to populate HTML li items
I have the following views built:
def viz(request):
return render(request, 'resume/viz.html')
class USNewsArticles(TemplateView):
template_name = 'viz'
def get_context_data(self, *args, **kwargs):
context = {
'articles': us_news_articles(),
}
return context
My URL looks like this
path('viz/', views.viz, name='viz')
And in my HTML file, I have the following:
<ul>
{% for article in articles %}
<li>{{ article.title }}</li>
<ul>
<li>{{ article.description }}</li>
</ul>
{% endfor %}
</ul>
However, when I deploy the website, I get no list. I believe it's an issue with the view, but I am not well-versed enough in understand views and functions to understand why it will not populate the li items

Your URLs are routed to your viz view, which just renders an empty template (viz.html). I think what you meant to do is this:
class USNewsArticles(TemplateView):
template_name = 'resume/viz.html'
def get_context_data(self, *args, **kwargs):
context = {
'articles': us_news_articles(),
}
return context
And your URL:
path('viz/', USNewsArticles.as_view())
TemplateView is a helper where you specify the template file directly, and it supplies the appropriate GET handler. - no need for an intermediate def myview function. In fact, the beauty of Class-Based Views is it writes a lot of the boilerplate for you. Unless you are customizing the behavior, you generally don't have to write methods that return Responses by hand.

Related

Is there any method for getting random context every time page reloads?

The goal is to make index.html get random context when we reload page. I don't know how to search for this. I've tried converting QuerySet to json for using it in script tag of index.html.
models.objects.ordered_by('?').first()
views.py
class IndexView(TemplateView):
template_name = 'index.html'
model = models.Meal
recommended_meal = models.Meal.objects.ordered_by('?').first()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["recommended_meal"] = self.context
return context
index.html
This codes exist between {%block%}~{%endblock%}
<div class="container">
<div class="picture_food">
<img src="{{recommended_meal.meal_photo.url}}" class = "img-rounded">
<h1>{{recommended_meal.meal_name}}</h1>
<ul>
<p>{{recommended_meal.restaurant_name}}</p>
<p>{{recommended_meal.price_range}}</p>
<p>{{recommended_meal.satisfaction}}</p>
</ul>
</div>
</div>
When I reload my index.html page, I got the same result, but when the server restarts it works.
Surely should be context["recommended_meal"] = self.recommended_meal?
I think the problem is that you are resolving recommended_meal down to a single fixed object with .first() at the time the class is defined. I'd suggest you try
context["recommended_meal"] = models.Meal.objects.ordered_by('?').first()
in the get_context_data method.

In Django, can a url tag have an arbitrary number of arguments?

BACKGROUND: I'm learning Django by working on a simple to-do-list app using Django 1.11. The app has "groups" and "items", where items are the individual to-do items and every item belongs to exactly one group. The URLs may end up working something like this:
# snippet from urls.py
url(r'^groups$', views.all_groups, name="all_groups"),
url(r'^groups/(\d+)/$', views.view_group, name="view_group"),
url(r'^items$', views.all_items, name="all_items"),
url(r'^items/(\d+)/$', views.view_item, name="view_item"),
Each of the above pages would display a one or two column table (e.g. all items, or all items in a specific group, etc.), where each table element would be a link to a page to show either a specific group or a specific item.
I have a separate view for each of the above URLs, but I was able to have a single HTML template to render each table successfully. Depending on the URL, there are either 0 or 1 arguments to the URL tag: {% url 'my_url' %} or {% url 'my_url' arg1 %}
Here is a snippet of the HTML template that can render the table with an arbitrary number of rows and columns, but at most two arguments for the url tag:
# lists.html - works for several different views
# every table entry is a dictionary with an 'page_ref' key and a 'display_text' key
<table>
{% for row in url_table %}
<tr>
{% for col in row %}
{% if col.arg2 %}
<td>{{col.display_text}}</td>
{% elif col.arg1 %}
<td>{{col.display_text}}</td>
{% else %}
<td>{{col.display_text}}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</table>
MY QUESTIONS:
1) My code in the HTML template to handle {% url 'my_url' %} vs. {% url 'my_url' arg1 %} vs. {% url 'my_url' arg1 arg2 %} works but it's ugly and limited to at most 2 levels deep on the URL. That is probably fine for what I want, but I don't like the code. Is there a cleaner way to handle an arbitrary number of arguments? When I tried simply not passing arg1/arg2 if not needed, I got an exception about not being able to reverse '' (or some similar error message).
2) In general, is it a bad idea to make a generic template like this? I.e., it is a better practice to have one specific HTML template per view, without making it more general? My guess is no, but I figured that I'd ask.
I can provide the view code as well, but I don't think that it is needed for my question.
Thanks
As you have different views, but you are using same template
So what you can do is remove conditions from template and
send same context variable name from each view containing different absolute url with respect to your view.
# view1 no arguments
class AllGroupView(TemplateView):
template_name = 'path/to/your.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['my_url'] = reverse('all_groups')
return context
#view2 one argument
class GroupView(TemplateView):
template_name = 'path/to/your.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['my_url'] = reverse('view_group', args=[arg1])
return context
...
#view3 two argument
class ItemView(TemplateView):
template_name = 'path/to/your.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['my_url'] = reverse('view_item', args=[arg1, arg2])
return context
In html just use this variable
# use my_url absolute: http://localhost:8000/...
<td>{{display_text}}</td>

How to pass a non DB object from a template to a view outside the URL with Django ?

I'm fairly new to Django. I'm writing an app that displays articles using RSS. I want users to be able to react to any given article.
On the index, I display the latest articles dynamically (they are not stored in the DB).
View :
class IndexView(generic.ListView):
template_name = 'publisher/index.html'
def get_context_data(self, **kwargs):
context = super(IndexView,self).get_context_data(**kwargs)
feeds = feedparser.parse('http://www.foo.com/rss/news.xml')
context['feeds'] = feeds
return context
Template :
{% for entry in feeds.entries %}
<li>
<a target="_blank" href="{{entry.link}}">{{entry.title}}</a>
I want to react
</li>
{% endfor %}
This works so far. I have a link next to each of these articles that should call the RSSReactView and display the article title, abstract, and a form to react. I don't want the article to be saved anywhere if the reaction is not posted, and thus I don't want any parameter to be included in the URL of the reaction form.
URL :
app_name = 'publisher'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^new_rss/react/$', views.RSSReactView.as_view(), name='rss_react'),
]
View :
class RSSReactView(generic.CreateView):
model = ReactionArticle
form_class = ReactionForm
template_name = 'publisher/react.html'
def get_context_data(self, **kwargs):
context = super(ReactView,self).get_context_data(**kwargs)
entry = self.kwargs['entry']
context['entry'] = entry
return context
def form_valid(self, form):
pass
Template :
<h1>You are reacting to article {{ entry.title }}</h1>
<p>
{{ entry.description }}
</p>
Of course, I get a NoReverseMatch error since the URL pattern isn't built to catch anything.
Passing parameters from DB stored objects through the URL is straightforward, but how do I pass a context variable ('entry', in this case) as an object to another view without using the URL ?
NB : I'm aware of the solutions using sessions or posts, but I can't believe that's the only way of doing it.

Django admin actions in detail view?

I have a few admin actions defined in my (fairly standard) Django app. Some of those actions would also make a lot of sense if they were available on the detail page of an object.
Right now, users would need to navigate back to the list, then search for the specific record, then trigger the action.
Is there a way to expose this functionality on the detail page as well?
Here is the answer you're looking for.
Essentially, you create a yourmodel_changeform.html file with following contents:
{% extends 'admin/change_form.html' %}
{% block submit_buttons_bottom %}
{{ block.super }}
<div class="submit-row">
<input type="submit" value="Button Label" name="_your-action-name">
</div>
{% endblock %}
And then override the response_change method on your ModelAdmin class and set the change_form_template attribute.
from django.http import HttpResponseRedirect
class YourModelAdmin(admin.ModelAdmin):
change_form_template = "templatelocation/yourmodel_changeform.html"
def response_change(self, request, obj):
if "_your-action-name" in request.POST:
# do whatever you want the button to do
obj.name = "new name"
obj.save()
return HttpResponseRedirect(".") # stay on the same detail page
return super().response_change(request, obj)
Tested on Django 3.0.3
If I understand you right, what you need to do is to write some separate html and then use it on different pages with {% include '/path/foo.html' %}. Here is more on this topic.
In foo.html you can add any functionality you want to use on different pages(forms, links, etc.).
If you want more detailed answer, it would be nice to see your code and what exactly you want to do.
To elaborate pythad.
1 Add another simple html that extends your admin html's and holds the buttons describing the admin action. you may call it change_form.py
make sure to strt is with:
{% extends "admin/change_form.html" %}
2 you would need to add small functions in your admin.py to conect the buttons to the action functions.
somthong like :
try:
sr = Scenario.objects.filter(pk = pk)
queitem = QueueScript.objects.filter(scriptresult__pk=sr.scriptreport.pk)
#do somtho
return HttpResponseRedirect(request.META["HTTP_REFERER"])
3 you need to elaborate/override the admins get_urls function to know the functions from above:
def get_urls(self, ):
urls = super(ScenarioAdmin, self).get_urls()
my_urls = patterns("",
url(r"(?P<pk>\d+)/stop_my_script/$",
self.stop_my_script),
url(r"(?P<pk>\d+)/run_scenario_standalone/$",
self.run_scenario_standalone),
)
return my_urls + urls
4 and finally elaborate your change_view func also in admin.py
def change_view(self, request, object_id, form_url='', extra_context=None):
extra_context = {}
extra_context['show_scer_buttons'] = True
extra_context['is_running'] = self.choose_template_buttons(object_id, extra_context)
return super(ScenarioAdmin, self).change_view(request, object_id,
form_url, extra_context=extra_context)

get_full_path in django model method

I'm just starting with django (and python too, to be honest)
I am trying to get a model method that would cut the self.slug from current URL and return it to template.
This is the method I tried:
class Category(models.Model):
...
def remove_filter(self):
url = HttpRequest.get_full_path()
slug = '/' + self.slug
return url.replace(slug, '')
But as you can imagine, it doesn't work.
Template's snippet:
{% for object in active_filters %}
<li><i class="icon-remove"></i>{{ object }}</li>
{% endfor %}
My core goal here is to have a front-end icon with a url altered by removing current object's slug.
I have no idea how to do it through views, but I'm open to any suggestions.
def category_page(request, url):
slugs = url.split('/')
active = Category.objects.filter(slug__in=slugs)
sorted_slugs = []
for i in active:
sorted_slugs.append(i.slug)
if slugs != sorted_slugs:
url = '/'.join(sorted_slugs)
return redirect('http://127.0.0.1:8000/catalog/' + url)
inactive = Category.objects.exclude(slug__in=slugs)
return render(request, 'category.html', {'active_filters': active,
'inactive_filters': inactive})
Thanks.
You can send a list of all active slugs to the template and then build a custom template filter to construct the modified url.
views.py
# Send your list of active slugs to the template
return render(request, 'category.html', {
'active_filters': active,
'inactive_filters': inactive,
'slugs': slugs,
})
tags_and_filters.py
import copy
from django import template
register = template.Library()
#register.filter(name='remove_filter')
def remove_filter(category, slugs):
copied_slugs = copy.copy(slugs)
slug = category.slug
if slug in copied_slugs:
copied_slugs.remove(slug)
return '/'.join(copied_slugs)
your template
{% for object in active_filters %}
<li>
<i class="icon-remove"></i>{{ object }}
</li>
{% endfor %}
Your remove_filter method has no access to the current request. HttpRequest is the class, not the current request instance.
I suggest that you rewrite remove_filter as a custom tag or filter. That way your function can access the category and request instance. You will have to activate the request template context processor in your settings as well.

Categories

Resources