Django class sql data pull - python

I have class:
class Like(models.Model):
user = models.ForeignKey(User, related_name='likes', on_delete=models.CASCADE)
post = models.ForeignKey(Post, related_name='likes', on_delete=models.CASCADE)
ratingtype = models.IntegerField(default=0)
that lets me display total amount of lines in database table with this tag:
{{post.likes.count}}
What would be the best way to modify this class so i can differentiate the counting by two ratingtype (1 or 0)

Don't make queries in the template. Templates are used for render logic, not business logic.
You can annotate the Post object(s) you are fetching with:
from django.db.models import Count, Q
Post.objects.annotate(
likes0=Count('likes', filter=Q(ratingtype=0)),
likes1=Count('likes', filter=Q(ratingtype=1))
)
The Products that arise from this queryset will have two extra attributes: .likes0 and .likes1 that contains the number of likes with ratingtype=0 and ratingtype=1 respectively. You thus can then render this with:
{{ post.likes0 }}
{{ post.likes1 }}

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 "for" loop by date

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')

how to display information entered by a user into a django model in the User profile

I'm kind of new to django, I'm working on a project currently. It is a website where people can look for houses to rent. Users will be able to create accounts, search for houses to rent and create listings about the houses they want to rent out.
I created a model to save all the information about houses that users want to rent out. I need to filter this information and display each user's listing on their profile. I have searched online but no solution yet.
Really need help.
models.py
from django.db import models
from django.contrib.auth.models import User
class Myhouses(models.Model):
Available = 'A'
Not_Available = 'NA'
Availability = (
(Available, 'Available'),
(Not_Available, 'Not_Available'),
)
name_of_accomodation = models.CharField(max_length=200)
type_of_room = models.CharField(max_length=200)
house_rent = models.IntegerField()
availability = models.CharField(max_length=2, choices=Availability, default=Available,)
location = models.CharField(max_length=200)
nearest_institution = models.CharField(max_length=200)
description = models.TextField(blank=True)
image = models.ImageField(upload_to='profile_image')
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='author')
def __str__(self):
return self.name_of_accomodation
view.py
class ListingByUser(LoginRequiredMixin, generic.ListView):
model = Myhouses
template_name ='houses/ListingByUser.html'
paginate_by = 10
def get_queryset(self):
return Myhouses.objects.filter(author=self.request.user)
urls.py
from django.conf.urls import url, include
from . import views
from django.contrib.auth.models import User
urlpatterns = [
url(r'^addlisting/$', views.addlisting, name='addlisting'),
url(r'^mylisting/', views.ListingByUser.as_view(), name='ListingByUser')
]
Template
<ul>
{% for houses in myhouses_list %}
<li>{{ houses.name_of_accomodation }}</li>
{%endfor %}
</ul>
Taking a quick view of your code, there is something that stuck me on your ListingByUser view: you override the get method only to set some attributes that are normaly defined as class attributes. That also could be preventing your view to actually get your models out of the database (via calling the get_queryset method) and rendering a proper response.
Edit
I found there's also a problem linking your template to the response the ListingByUser view is rendering. As far as I know, Django views doesn't look into the variable template_name for getting the response's template. But it does call a method get_template_names which returns a list of template names given as strings.
Try to modify it in this way:
views.py
class ListingByUser(LoginRequiredMixin, generic.ListView):
model = Myhouses
template_name ='myhouses/listing_by_user.html'
paginate_by = 10
def get_queryset(self):
return Myhouses.objects.filter(author=self.request.user)
def get_template_names(self):
return [self.template_name]

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

Categories

Resources