Django "for" loop by date - python

I'm doing blog app. I did:
{% for entry in entry.all %}
<div class="timelinestamp">
...
</div>
<br />
<br />
<br />
<br />
{% endfor %}
and almost everything works fine. I changed one Entry in my admin panel (The very first Entry...). Since then the order of my post has changed... Can anyone explain me why ? Or tell how to using loop render all Entries sorted by date ?
class Entry(models.Model):
title = models.CharField(max_length=120)
pub_date = models.DateField(null=False)
body = models.TextField()
image = models.ImageField(upload_to='images/', max_length = 100)
def __str__(self):
return self.title
The pub_date field is NOT primary key in my DB! I'm using Django 2.1

From the docs:
If a query doesn’t have an ordering specified, results are returned from the database in an unspecified order. A particular ordering is guaranteed only when ordering by a set of fields that uniquely identify each object in the results. For example, if a name field isn’t unique, ordering by it won’t guarantee objects with the same name always appear in the same order.
It looks like you want to order by pub_date? Use ordering:
class Entry(models.Model):
...
class Meta:
ordering = ['-pub_date']

If you have created model definition without the ordering meta option items in the database are not enforced any ordering, everytime when you do Model.objects.all() it will give you items without any order. If you want to queries to be in specific order you can:
Add ordering option to Meta options to model definition - which would require
database migrations
Modify your query to enforce ordering like Model.objects.all().order_by('-pub_date') - also pass the query as context object to template like:
views.py -
entries_by_pub_date = Model.objects.all().order_by('-pub_date')
context['entries_by_pub_date'] = entries_by_pub_date
template
{% for entry in entries_by_pub_date %}
...
{% enfor %}

As far as I can see you haven't defined a sort order for your Entry model. This means that you will process those entries in a non-defined order.
To order your entries you could set a default sort order on Entry:
class Entry(models.Model):
title = models.CharField(max_length=120)
pub_date = models.DateField(null=False)
body = models.TextField()
image = models.ImageField(upload_to='images/', max_length = 100)
def __str__(self):
return self.title
class Meta:
ordering = ('-pub_date',)
Or, if that's not what you're looking for, you could order your queryset in your view:
Entry.objects.all().order_by('-pub_date')

Related

Multiple foreign key lookups

I have the following models in my app
Account
class Account(CommonModel): # Accounts received from Client
client = models.ForeignKey('Client', on_delete=models.RESTRICT)
reference = models.CharField(db_index=True, max_length=50)
def __str__(self):
return f"{self.client} {self.reference}"
Person
class Person(CommonModel):
title = models.CharField(max_length=100,choices=choi.person_title())
name = models.CharField(db_index=True, max_length=100)
birth_date = models.DateField()
def __str__(self):
return f"{self.title} {self.name}"
AccountPerson
class AccountPerson(CommonModel): # Account -> Person link
account = models.ForeignKey("core.Account", on_delete=models.RESTRICT, related_name="accountperson_account")
person = models.ForeignKey("core.Person", on_delete=models.RESTRICT, related_name="accountperson_person")
contact_type = models.CharField(max_length=50, choices=choi.contact_type())
def __str__(self):
return f"{self.account} - {self.person} ({self.contact_type})"
The AccountPerson model holds relationships between accounts and people (one person can have multiple accounts). I'm trying to return a query set containing a list of Accounts, and the Person they're linked to (if any). My background is SQL, so I'm thinking of a query that would hit Account -> AccountPerson --> Person, but I'm stuck.
I've tried prefetch_related() but I'm only returning details in the Account table - I'm unsure of how to access Person from there and put those fields into my HTML file.
View
def account_list(request):
data = Account.objects.all().prefetch_related('accountperson_account')
return render(request, 'core/account_list.html', {'data': data})
account_list.html
Code condensed for readability
...
{% for i in data %}
<tr>
<td>{{i.client}}</td>
<td>{{i.reference}}</td>
{% endfor %}
...
I'm currently in a position where my page loads, and I see the entries in my Account model, but that's it.
Update
I changed my view to this
def account_list(request):
data = AccountPerson.objects.all().select_related('account').select_related('person')
return render(request, 'core/account_list.html', {'data': data})
And I can now access fields in Account and Person in my HTML like so
{% for i in data %}
<tr>
<td>{{i.account.client}}</td>
<td>{{i.account.reference}}</td>
<td>{{i.contact_type}}</td>
<td>{{i.person.name}}</td>
{% endfor %}
I just want to check that this is the right way (or one of them)?
I'd change the datamodel slightly to be more Django-y. Django has the concept of ManyToMany fields which is what you're trying to accomplish. (https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ManyToManyField)
You would define the Person model as you did and change the Account model to have a ManyToMany field (you could also switch it around, that won't matter).
You can also defined the intermediate model like you intended. Use the through argument on the ManyToMany for this.
You can use Related Manager to handle all lookups both ways: account.person and person.account (the 'account' part is set by the related_name).
class Account(CommonModel): # Accounts received from Client
client = models.ForeignKey('Client', on_delete=models.RESTRICT)
reference = models.CharField(db_index=True, max_length=50)
person = models.ManyToManyField(Person, through=AccountPerson, related_name='account')

Django view only returning incomplete data, only one field to template

I have forked the django-oscar catalogue app to alter the models being used. Not in a major way, and not in a way that would affect pulling data from the database as far as I can see. This seems to be supported by the fact the the django-oscar dashboard still works fine and lets me add and view products. My models.py from my forked app:
from django.db import models
class Collection(models.Model):
name = models.CharField(max_length=50)
prod_category = models.CharField(max_length=50)
description = models.TextField()
manufacturer = models.TextField()
num_products = models.PositiveIntegerField()
image_url = models.URLField()
from oscar.apps.catalogue.abstract_models import AbstractProduct
class Product(AbstractProduct):
collection = models.ForeignKey(Collection, on_delete=models.CASCADE, null=True)
multiplier = models.DecimalField(max_digits=2, decimal_places=1, default='2.2')
from oscar.apps.catalogue.models import *
Here is my relevant view from my views.py
def product(request):
template = loader.get_template('/home/my_app/my_site/main_page/templates/main_page/product.html')
prods = Product.objects.values_list('categories')
context={'prods': prods}
return HttpResponse(template.render(context))
I tried loading from the built in model and my forked model (commenting and uncommenting one or both), neither makes a difference:
#from forkedoscarapps.catalogue.models import Product
from oscar.core.loading import get_class, get_model
Product = get_model('catalogue', 'product')
And the code I am using in the template to display data from the view:
{% for instance in prods %}
<li><{{ instance.name }}</li>
{% endfor %}
The resulting HTML is:
<li></li>
Which shows it is reaching the for loop, but for some reason no data is returned.
There is at least one category called beds, which displays fine in the django-oscar dashboard. What have I missed in my view?
edit: When I change instance.name to just instance I get the following returned in the HTML:
(1,)
So it is somewhat working, and showing what I assume is the primary key being returned, but why is the name of the field not being returned?
Product.objects.values_list('categories') yields a list of id tuples that represent the categories associated with the products in that queryset. That's not what you want to send to the template, you want to send instances, more specifically product instances if I'm not mistaken.
Do Product.objects.all() instead, and just use {{ instance.title }} in the template according to the definition of the oscar model: https://github.com/django-oscar/django-oscar/blob/master/src/oscar/apps/catalogue/abstract_models.py and to what ever you customised over it.

Django just sort ListView by date

I'm dealing with some Django code I inherited from a dev and need to make a very simple change. In the code, there is a list of jobs displayed through a Django ListView. My problem is really simple. When I go to the page, I see the jobs sorted by date with earliest one first. I want to sort the jobs in the opposite order. I don't need any filtering, passing parameters in the URL, etc for now. Here are the relevant parts of the files:
#models.py
from django.db import models
class Job(models.Model):
created = models.DateTimeField(auto_now_add=True)
position = models.ManyToManyField(Position)
title = models.CharField(max_length=100)
#views.py
from .models import Job
class JobListView(ListView):
template_name="jobs/list.html"
model = Job
paginate_by = 10
#list.html
{% for job in object_list %}
<li class="display-list-item">
<h4><strong>{{job.title}}</strong></h4>
<ul class="list-inline job-info-list">
<span>{{job.created | timesince}} ago</span>
</ul>
</li>
{% endfor %}
#urls.py
urlpatterns = [
url('^$', views.JobListView.as_view(), name='job_list')
]
As mentioned, this causes the jobs to be displayed sorted by 'created' field. The ones created earlier are displayed first. What is the quickest way to make the ones created later display first?
first way
models.py
from django.db import models
class Job(models.Model):
created = models.DateTimeField(auto_now_add=True)
position = models.ManyToManyField(Position)
title = models.CharField(max_length=100)
class Meta:
ordering = ['-created']
second way
views.py
from .models import Job
class JobListView(ListView):
template_name="jobs/list.html"
queryset = Job.objects.order_by('-created')
paginate_by = 10

How do I iterate through a list of related objects in a django template?

I'm new to Django and I'm trying to get related models printing in a view. I'm pretty sure the DB itself is set up properly, including keys and such, and there is data in the table to play with. What I'm not sure about is
Am I wiring the models to the correct table and column names?
Am I loading the phones for each voter?
How do I iterate over the relationship itself?
I can get the voter information itself, but nothing I try with the related Phone model works.
Tables:
voters:
id int primary key
first_name varchar
phones:
id int primary_key
phone_number varchar
voter_phones:
voter_id FK
phone_id FK
Models:
from django.db import models
class Phone(models.Model):
phone_number = models.CharField(max_length=255)
class Meta:
db_table = 'phones'
class Voter(models.Model):
first_name = models.CharField(max_length=255)
phones = models.ManyToManyField('Phone', through='VoterPhone', related_name='voters', through_fields=('voter_id', 'phone_id'))
class Meta:
db_table = 'voters'
class VoterPhone(models.Model):
voter = models.ForeignKey('Voter', related_name='voter_phones')
phone = models.ForeignKey('Phone', related_name='voter_phones')
class Meta:
db_table = 'voter_phones'
The view:
def results(request):
voters = Voter.objects.all()
return render(request, 'site/results.html', {'voters': voters})
The template:
{% for voter in voters %}
{{ voter.first_name }}
{% for phone in voter.phones_set.all %}
{{ phone.phone_number }}
{% endfor %}
{% endfor %}
The end result is a list of voter names, and no phone numbers. What am I missing?
EDIT: I created a local database, generated and ran django migrations, and inserted some dummy data. I still get no phone numbers.
I finally got something working. I had to make the following changes:
got the tables renamed to their default django names (namely, voters_phones instead of voter_phones)
found that the foreign keys in voters_phones were int(11) while the primary keys in both voters and phones were bigint(20)
removed the join models
Thanks for your help, guys.
If you do not use Django to create database, you need to specify table name for the VoterPhone class. Django would not choose voterphones table as the intermediate table. I think by setting
class Meta:
db_table = 'voterphones'
for VoterPhone class you should be able to see related phone numbers.

Django-haystack - how to represent relations in search indexes?

So I'm trying to index some items with Django-Haystack (elasticsearch backend), one of the indexing criteria being tags on the item, which are a m2m relation(I implemented my own custom solution as it was easier for me than using taggit), here is what my models look like.
class GalleryTag(models.Model):
tag = models.CharField(max_length=100, unique=True)
slug = AutoSlugField(populate_from='tag', unique=True)
class Meta:
abstract = True
def __unicode__(self):
return self.tag
class Tag(GalleryTag):
pass
class Artist(GalleryTag):
pass
class Character(GalleryTag):
pass
class Gallery(models.Model):
characters = models.ManyToManyField(Character, blank=True, related_name='characters')
artists = models.ManyToManyField(Artist, blank=True, related_name='artists')
tags = models.ManyToManyField(Tag, blank=True, related_name='tags')
def __unicode__(self):
return self.name
The object I'm trying to index to be searchable is Gallery, and I would like to be able to have the tags, artists, and characters(all the m2ms) be one of the searchable criteria on them. I could not really find anything about how to make relations searchable, the basic examples only use completely flat models. Thanks.
One way to do this would be to pull in the data in the template file of GalleryIndex.
Something like:
{% for s in object.hasTags.all %}
{{t.tag}}
{% endfor %}
If, for whatever reason, resolving your relation is too complex for a template then add a field called tags to GalleryIndex and add a routine prepare_tags(self, obj) that queries the relevant data, concatenates and returns it as a string.

Categories

Resources