In Django, I have created a dataclass to help me move data around in a standardized way:
# models.py
class Person(models.Model):
name = CharField(max_length=50)
#utils.py
#dataclass
class Performance:
person: Person
last_year: int
five_years: int
The idea here is that I can access the properties by name when transferring them to the template:
#views.py
class IndexView(View):
def get(self, request):
persons = Person.objects.filter(name="pam")
perfs_array = []
for person in persons:
p = Performance()
p.last_year = #some math
p.five_years = #more math
perfs_array.append(p)
context = {"perfs": perfs_array}
return render(...)
The goal is to be able to do this:
<!--index.html-->
{% for perf in perfs %}
<h1>{{ perf.person.name }}</h1>
<p>last year's performance: {{ p.last_year }}</p>
{% endfor %}
However, I can only access the data using numbered indices:
<!--index.html-->
{% for perf in perfs %}
<h1>{{ perf.0.name }}</h1>
<p>last year's performance: {{ p.1 }}</p>
{% endfor %}
This made it very hard to write correct code and to debug the templates when things go inevitably south. Is there a way to access the data with named indices?
Thanks!
Related
I have 2 lists of equal size elements that I want to join and then output to my Django template.
But I'm having trouble identifying the row so just that row is printed.
I am trying to zip the two lists and then create a new list that will loop through onto the template page.
When I use the variable on the template like the code below It works fine but I want to be able to loop through the list.
I know this is incorrect as not all the elements are printed to the template but it creates the desired result.
<p> {{ news.esp_article.0 }}</p>
<p> {{ news.eng_article.0 }}</p>
<hr>
<p> {{ news.esp_article.1 }}</p>
<p> {{ news.eng_article.1 }}</p>
<hr>
<p> {{ news.esp_article.2 }}</p>
<p> {{ news.eng_article.2 }}</p>
<hr>
<p> {{ news.esp_article.3 }}</p>
<p> {{ news.eng_article.3 }}</p>
<hr>
<p> {{ news.esp_article.4 }}</p>
<p> {{ news.eng_article.4 }}</p>
To try to solve it here is my views.py And I'm almost certain my problem is my queryset result.
class ArticleViewPage(DetailView):
model = Newspapers
template_name = 'rtves/article.html'
context_object_name = 'news'
eng_articles = Newspapers.objects.values_list('eng_article')
esp_article = Newspapers.objects.values_list('esp_article')
zip_scripts = list(zip(eng_article, esp_article))
context.update(
zip_scripts = zip_scripts
)
Then I get the full raw data of every row in the database
here is my template file:
{% for text1, text2 in zip_scripts %}
<p>{{ text1 }}</p>
<p>{{ text2 }}</p>
{% endfor %}
Here is my URL pattern file which is working correctly
path('newspapers/articles/<slug:pk>/', ArticleViewPage.as_view(), name='Articles'),
I know the problem is my queryset and it's pulling in too much detail but I have tried filtering and about 20 other things
here is my models both objects I'm trying to print to the template are saved ass ArrayFields
class Newspapers(models.Model):
esp_article = ArrayField(models.CharField(max_length=8000, blank=True))**
eng_article = ArrayField(models.CharField(max_length=8000, blank=True))**
Thanks
You are running zip on the top level list, the Newspapers queryset in a list of lists, instead of the two lists of articles (eng_articles and esp_articles).
class ArticleViewPage(DetailView):
model = Newspapers
template_name = 'rtves/article.html'
context_object_name = 'news'
def get_context_data(self, **kwargs):
zip_scripts = zip(self.object.eng_articles, self.object.esp_articles)
context = super().get_context_data(**kwargs)
context.update(zip_scripts=zip_scripts)
return context
This is assuming ArticleViewPage should only show English and Spanish articles for a single newspaper at a time.
As an alternate solution you could instead of zipping the articles in the view's get_context_data method, make the zipped articles available on the model like so:
class Newspapers(models.Model):
esp_articles = ArrayField(models.CharField(max_length=8000, blank=True))
eng_articles = ArrayField(models.CharField(max_length=8000, blank=True))
#property
def articles_zipped(self):
return zip(self.esp_articles, self.eng_articles)
and update your template to use the new model property:
{% for article_spanish, article_english in news.articles_zipped %}
<p>{{ article_spanish }}</p>
<p>{{ article_english }}</p>
{% endfor %}
You need to add a simple code to fix your queryset. values_list returns a tuple. In order to make it return a list, you need to add flat=True.
Try this:
eng_articles = Newspapers.objects.values_list('eng_article', flat=True)
esp_article = Newspapers.objects.values_list('esp_article', flat=True)
In your querysets you should use values() to get a list of dicts with each dict corresponding to a row in the database. Then, in your template, you can use . to access a single column value (for each row).
views.py:
class ArticleViewPage(DetailView):
model = Newspapers
template_name = 'rtves/article.html'
context_object_name = 'news'
def get_context_data(self, **kwargs):
eng_articles = Newspapers.objects.values('eng_articles')
esp_articles = Newspapers.objects.values('esp_articles')
zip_scripts = zip(eng_articles, esp_articles)
context = super().get_context_data(**kwargs)
context.update(zip_scripts=zip_scripts)
return context
rtves/article.html:
{% for article_eng, article_esp in zip_scripts %}
<p>{{ article_eng.text }}</p>
<p>{{ article_esp.text }}</p>
{% endfor %}
I'm not sure what your models.py looks like. Are you fetching a list of related articles in English and Spanish for a single newspaper or all newspapers? Note: It is recommended to use singular nouns as model names, that is Newspaper without the s at the end.
I have this models (simplified):
#models.py
class Expression(models.Model):
text = models.CharField(max_length=254)
class Country(models.Model):
name = models.CharField(max_length=100)
class Definition(models.Model):
expression = models.ForeignKey(Expression)
country = models.ForeignKey(Country)
text = models.CharField(max_length=254)
class Vote(models.Model):
definition = models.ForeignKey(Definition)
And this view
#views.py
def index(request):
expressions = Expression.objects.all()
return render(request, 'expression_index.html', { 'expressions':expressions)
So it will show the last 10 created expressions.
Then in the template I have this:
#index.html
{% for expression in expressions %}
{{ expression }}
{% for definition in expression.definition_set.all %}
<ul>
<li>{{ definition }}</li>
</ul>
{% endfor %}
{% endfor %}
Every definition has several votes.
Every vote is a single row so we can do:
definition.votes_set.count()
How can I achieve to display them like this:
The top definition of every country alphabetically. Each country appears only with one definition.
Lets say Germany has two definitions for expression "A" and Denmark has three definitions for the same expression it will show only two definitions: the one with the most votes.
I hope I'm making sense.
Thanks
I think something like this should work (untested)
from django.db.models import Count
{{ expression.definition_set.annotate(Count('votes_set')) }}
{% for definition in expression.definition_set.order_by('country','votes_set__count') %}
This queryset will sort alphabetically by country, then each country from it's top voted definition to least.
expression = Expression.objects.get(text__exact="A")
definitions = Definition.objects.filter(expression=expression).annotate(num_votes=Count('vote')).order_by("country__name", "-num_votes")
If i try to keep only the top definition of each country as you wanted, and set .distinct("country") at the end of the queryset, it will throw this error:
NotImplementedError at annotate() + distinct(fields) is not implemented.
So another solution would be:
import operator
expression = Expression.objects.get(text__exact="A")
# Get top definitions sorted by num of votes and convert the values to list.
top_definitions = list(Definition.objects.filter(expression=expression).annotate(num_votes=Count('vote')).order_by("num_votes").values("id", "country", "country__name", "expression", "text", "num_votes"))
# Remove dublicate countries and leave the only the top voted definition.
definitions = list({v['country']: v for v in top_definitions}.values())
# Sort alphabetically by country.
definitions.sort(key=operator.itemgetter('country__name'))
return render(request, 'expression_index.html', {'definitions': definitions, 'expression': expression})
template:
<h1>{{ expression.text }}</h1>
<ul>
{% for definition in definitions %}
<li>{{ definition.country__name }} - {{ definition.text }} - {{ definition.num_votes }}</li>
{% endfor %}
</ul>
I'm trying to understand the best way to display ForeignKey filtered data in a Django model.
I have three models reduced to this:
// models.py
class Publisher(models.Model)
def publisher_name = models.TextField()
def publisher_slug = models.SlugField()
def founded_year = models.IntegerField()
class Album(models.Model)
def album_name = models.TextField()
def publisher = models.ForeignKey('Publisher', related_name='albums')
class Song(models.Model)
def song_name = models.TextField()
def album = models.ForeignKey('Album', related_name='songs')
def published_year = models.IntegerField()
I have a URL that is composed of: /<publisher>/<published_year>/
The view I'm having trouble composing is supposed to be details like this:
Title of: Publisher.publisher_name
List of All Albums by the publisher: List of All songs from that album published the same year as the publisher__published_year: List of All songs from that album published as the url
The way, I've tried to do this that works right now is similar to this:
// views.py
class SongYearView(TemplateView):
def get_context_data(self, **kwargs):
context = super(SongYearView, self).get_context_data(**kwargs)
context['publisher'] = Publisher.objects.get(slug=kwargs['publisher_slug']
album_list=[]
for album in context['publisher'].albums.all():
single_album = dict()
single_album['album'] = album
single_album['publisher_year_song'] = album.songs.filter(published_year=context['publisher'].published_year)
single_album['filtered_year_song'] = album.songs.filter(published_year=kwargs['published_year']
album_list.append(single_album)
context['albums'] = album_list
return context
Then in the template I'm doing (with stripped out formatting)
// template.html
{{ publisher.name }}
{% for album in albums %}
{{ album.album.album_name }}
{% for song in album.publisher_year_song %}
{{ song.song_name }}
{% endfor %}
{% for song in album.filtered_year_song %}
{{ song.song_name }}
{% endfor %}
{% endfor %}
While this does work, it's not pretty and I'm pretty sure there are better ways of doing this.
This is an odd example, but just a basic example of my more detailed models. The way to think about it is Publisher -> Album -> Song or A -> B -> C. And I'm trying to get a view of all B items, that are only linked with a specific A item and then get two sets of C items for each B item, where one set is filtered on an A property and the other set is filtered on a passed argument from a URL.
I tried to get a custom model.Manager to help get this constructed, but didn't have much luck.
You could do add a custom template filter of_year:
#register.filter
def of_year(songs, year):
return songs.filter(published_year=year)
And change your template to
// template.html
{{ publisher.name }}
{% for album in publisher.albums %}
{{ album.album.album_name }}
{% for song in album.songs|of_year:publisher.founded_year %}
{{ song.song_name }}
{% endfor %}
{% for song in album.songs|of_year:filtered_year %}
{{ song.song_name }}
{% endfor %}
{% endfor %}
And clean your view:
// views.py
class SongYearView(TemplateView):
def get_context_data(self, **kwargs):
context = super(SongYearView, self).get_context_data(**kwargs)
context['publisher'] = Publisher.objects.get(slug=kwargs['publisher_slug'])
context['filtered_year'] = kwargs['published_year']
return context
Edit: rename the template filter
please help to get the file name.
I uploaded the images to the database. model:
class AvtoPark(models.Model):
title = models.CharField(max_length=100)
date = models.DateField()
image = models.FileField(upload_to='avtos/static/uploads/')
#classmethod
def get_all_entries(self):
return self.objects.all()
field "IMAGE" contains addresses like:
avtos/static/uploads/img1_b.png
template I bring them as follows:
{% for entrie in all_entries_avtopark %}
{{ entrie.image.url }}
{% endfor %}
the result is displayed:
avtos/static/uploads/img1_b.png
I need to display:
img1_b.png
In your model add this:
import os
...
class AVTPark(models.Model):
...
#property
def image_name(self):
return os.path.basename(self.image.name)
In your template:
{% for entrie in all_entries_avtopark %}
{{ entrie.image_name }}
{% endfor %}
Edited :
You can create your own method in your models.py class.
class AvtoPark(models.Model):
title = models.CharField(max_length=100)
date = models.DateField()
image = models.FileField(upload_to='avtos/static/uploads/')
#classmethod
def get_all_entries(self):
return self.objects.all()
def get_image_name(self):
# here you can apply your logic what you want to display
# try to do same like below i am doing
return str(self.image.url).split('/')[-1]
while in template you can call directly like :
{% for entrie in all_entries_avtopark %}
{{ entrie.get_image_name( }}
{% endfor %}
I'm trying to run a search on a model that has a many to many field, and I want to filter the search using this field.
here is my current code:
search_indexes.py
class ListingInex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
business_name = indexes.CharField(model_attr='business_name')
category = indexes.MultiValueField(indexed=True, stored=True)
city = indexes.CharField(model_attr='city')
neighborhood= indexes.CharField(model_attr='neighborhood')
state = indexes.CharField(model_attr='state')
address = indexes.CharField(model_attr='address')
zip_code = indexes.CharField(model_attr='zip_code')
phone_number = indexes.CharField(model_attr='phone_number')
def get_model(self):
return listing
def index_queryset(self, using=None):
return self.get_model().objects.all()
def prepare_category(self, obj):
return [category.name for category in obj.category_set.all()]
listing_text.txt
{{ object.business_name }}
{{ object.state }}
{{object.city}}
{{object.zip_code}}
{{object.phone_number}}
{{object.neighborhood}}
{% for category in obj.category.all %}
{{ category.name }}
{% endfor %}
I'm trying to do a simple search like:
search_results = SearchQuerySet().filter(category=query_text).filter
it returns an empty list. Thanks in advance.
Reading your code I think the problem is here:
{% for category in obj.category.all %}
{{ category.name }}
{% endfor %}
Category is not an object, is a MultiValueField. Maybe if you try something like:
{% for category in object.category.all %}
{{ category.name }}
{% endfor %}
object.category.all instead of obj.category.all. I'm not sure the solution is this, but I'm pretty sure the error could be in this 3 lines.